@gerege-systems/eid-mongolia-sdk 0.15.1 → 0.15.3

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
@@ -59,6 +59,7 @@ __export(index_exports, {
59
59
  interactionsToBase64: () => interactionsToBase64,
60
60
  isMobileUserAgent: () => isMobileUserAgent,
61
61
  memoryLoginStore: () => memoryLoginStore,
62
+ ocspRevocationChecker: () => ocspRevocationChecker,
62
63
  openOrShowQR: () => openOrShowQR,
63
64
  parseExpiresAt: () => parseExpiresAt,
64
65
  parseRetryAfter: () => parseRetryAfter,
@@ -224,6 +225,13 @@ var Http = class {
224
225
  post(path, body, timeoutMs) {
225
226
  return this.json("POST", path, JSON.stringify(body), "application/json", timeoutMs);
226
227
  }
228
+ /** {@link post} + HTTP статус (201 / 200-г ялгах: `POST /pdf/signatures`). */
229
+ async postWithStatus(path, body, timeoutMs) {
230
+ const op = `POST ${maskPath(path)}`;
231
+ const res = await this.send("POST", path, JSON.stringify(body), "application/json", timeoutMs);
232
+ const text = new TextDecoder().decode(await readLimited(res, MAX_JSON_BYTES, op));
233
+ return { status: res.status, body: text ? JSON.parse(text) : {} };
234
+ }
227
235
  get(path, timeoutMs) {
228
236
  return this.json("GET", path, void 0, void 0, timeoutMs);
229
237
  }
@@ -537,12 +545,32 @@ function registrationFromCompleted(done, extra) {
537
545
  if (extra.medium) out.medium = extra.medium;
538
546
  if (extra.sessionID) out.sessionID = extra.sessionID;
539
547
  if (done.hasPage && done.pageSha256) out.pageSha256 = done.pageSha256;
540
- if (done.priorCmsSha256.length) out.priorCmsSha256 = done.priorCmsSha256;
541
- if (extra.priorRevisionSha256?.length) out.priorRevisionSha256 = extra.priorRevisionSha256;
548
+ if (extra.priorRevisionSha256?.length) {
549
+ if (done.priorCmsSha256.length) out.priorCmsSha256 = done.priorCmsSha256;
550
+ out.priorRevisionSha256 = extra.priorRevisionSha256;
551
+ } else if (done.priorCmsSha256.length) {
552
+ out.priorCmsSha256 = done.priorCmsSha256;
553
+ const rev = done.priorRevisionSha256 ?? [];
554
+ if (rev.length === done.priorCmsSha256.length) {
555
+ out.priorSignatures = done.priorCmsSha256.map((c, i) => ({ cmsSha256: c, revisionSha256: rev[i] }));
556
+ }
557
+ }
542
558
  if (extra.onBehalfOf) out.onBehalfOf = extra.onBehalfOf;
543
559
  if (extra.signerEtsi) out.signerEtsi = extra.signerEtsi;
544
560
  return out;
545
561
  }
562
+ function priorSplits(n) {
563
+ const out = [];
564
+ const seen = /* @__PURE__ */ new Set();
565
+ for (let k = 1; k <= n; k++) {
566
+ seen.add((1 << k) - 1);
567
+ out.push(Array.from({ length: n }, (_, i) => i < k));
568
+ }
569
+ if (n <= 4) {
570
+ for (let m = 1; m < 1 << n; m++) if (!seen.has(m)) out.push(Array.from({ length: n }, (_, i) => (m & 1 << i) !== 0));
571
+ }
572
+ return out;
573
+ }
546
574
  var HEX64 = /^[0-9a-f]{64}$/;
547
575
  var MAX_CMS_BYTES = 28672;
548
576
  function buildRegisterBody(input) {
@@ -696,7 +724,7 @@ var SignApi = class {
696
724
  * (base64 → байт) `complete(state, signature)`-д.
697
725
  */
698
726
  async pdfDigest(input) {
699
- const digest = dtbsBase64(input.dtbs);
727
+ const digest2 = dtbsBase64(input.dtbs);
700
728
  const fileName = input.fileName?.trim() ?? "";
701
729
  if (!fileName || Array.from(fileName).length > PDF_MAX_FILE_NAME) {
702
730
  throw new ValidationError(`pdfDigest: fileName \u0437\u0430\u0430\u0432\u0430\u043B, \u2264 ${PDF_MAX_FILE_NAME} \u0442\u044D\u043C\u0434\u044D\u0433\u0442`);
@@ -714,7 +742,7 @@ var SignApi = class {
714
742
  certificateLevel: input.certificateLevel ?? this.defaults.certificateLevel,
715
743
  signatureProtocol: "RAW_DIGEST_SIGNATURE",
716
744
  signatureProtocolParameters: {
717
- digest,
745
+ digest: digest2,
718
746
  signatureAlgorithm: "sha256WithRSAEncryption",
719
747
  signatureAlgorithmParameters: { hashAlgorithm: "SHA-256" }
720
748
  },
@@ -734,7 +762,7 @@ var SignApi = class {
734
762
  }
735
763
  dl = { sessionToken: String(r.sessionToken), sessionSecret: String(r.sessionSecret), deviceLinkBase: String(r.deviceLinkBase) };
736
764
  }
737
- return { sessionId: String(r.sessionID), vc: vc != null ? String(vc) : null, digest, interactions, deviceLink: dl, receivedAt };
765
+ return { sessionId: String(r.sessionID), vc: vc != null ? String(vc) : null, digest: digest2, interactions, deviceLink: dl, receivedAt };
738
766
  }
739
767
  /**
740
768
  * `POST /pdf/signatures` — `pades.complete()`-ийн дараа CMS-ийг CA-д бүртгэнэ (`/verify/{docID}`, иргэний түүх). PDF
@@ -744,16 +772,38 @@ var SignApi = class {
744
772
  * `/verify` «бүртгэлгүй» гэнэ.
745
773
  */
746
774
  async registerSignature(input) {
747
- return this.registerBody(buildRegisterBody(input));
775
+ const body = buildRegisterBody(input);
776
+ const prior = input.priorSignatures ?? [];
777
+ if (input.priorRevisionSha256?.length || prior.length === 0 || prior.length !== input.revIndex - 1) return this.registerBody(body);
778
+ try {
779
+ return await this.registerBody(body);
780
+ } catch (first) {
781
+ if (!(first instanceof ApiError && first.code === "PRIOR_MISMATCH")) throw first;
782
+ for (const asRevision of priorSplits(prior.length)) {
783
+ const next = {
784
+ ...input,
785
+ priorSignatures: void 0,
786
+ priorCmsSha256: prior.filter((_, i) => !asRevision[i]).map((p) => p.cmsSha256),
787
+ priorRevisionSha256: prior.filter((_, i) => asRevision[i]).map((p) => p.revisionSha256)
788
+ };
789
+ try {
790
+ return await this.registerBody(buildRegisterBody(next));
791
+ } catch (e) {
792
+ if (!(e instanceof ApiError && e.code === "PRIOR_MISMATCH")) throw e;
793
+ }
794
+ }
795
+ throw first;
796
+ }
748
797
  }
749
798
  async registerBody(body) {
750
- const raw = await this.http.post(routePath(RP_ROUTES.pdfSignatures), body);
799
+ const { status, body: raw } = await this.http.postWithStatus(routePath(RP_ROUTES.pdfSignatures), body);
751
800
  const r = rec2(raw);
752
801
  return {
753
802
  docID: String(r.docID ?? body.docID),
754
803
  revIndex: Number(r.revIndex ?? 0),
755
804
  registeredAt: typeof r.registeredAt === "string" ? r.registeredAt : "",
756
805
  verifyUrl: String(r.verifyUrl ?? ""),
806
+ created: status === 201,
757
807
  raw
758
808
  };
759
809
  }
@@ -1113,11 +1163,11 @@ function parseSubject(cert) {
1113
1163
  if (atv.tag !== 48) throw new ValidationError("subject: AttributeTypeAndValue SEQUENCE \u0431\u0438\u0448");
1114
1164
  const oidT = tlv(der, atv.start, atv.end);
1115
1165
  const valT = tlv(der, oidT.end, atv.end);
1116
- const oid = oidOf(der, oidT);
1166
+ const oid2 = oidOf(der, oidT);
1117
1167
  const value = stringOf(der, valT);
1118
- if (value !== void 0 && !(oid in out.attributes)) {
1119
- out.attributes[oid] = value;
1120
- const key = OIDS[oid];
1168
+ if (value !== void 0 && !(oid2 in out.attributes)) {
1169
+ out.attributes[oid2] = value;
1170
+ const key = OIDS[oid2];
1121
1171
  if (key) out[key] = value;
1122
1172
  }
1123
1173
  ap = atv.end;
@@ -1128,8 +1178,8 @@ function parseSubject(cert) {
1128
1178
  out.name = full || out.commonName || null;
1129
1179
  return out;
1130
1180
  }
1131
- function certExtension(der, oid) {
1132
- return certExtensions(der).find((e) => e.oid === oid)?.value;
1181
+ function certExtension(der, oid2) {
1182
+ return certExtensions(der).find((e) => e.oid === oid2)?.value;
1133
1183
  }
1134
1184
  function certExtensions(der) {
1135
1185
  const out = [];
@@ -1141,9 +1191,9 @@ function certExtensions(der) {
1141
1191
  for (let pos = f.end; pos < tbs.end; ) {
1142
1192
  const t = tlv(der, pos, tbs.end);
1143
1193
  if (t.tag === 163) {
1144
- const seq = tlv(der, t.start, t.end);
1145
- for (let ep = seq.start; ep < seq.end; ) {
1146
- const ext = tlv(der, ep, seq.end);
1194
+ const seq2 = tlv(der, t.start, t.end);
1195
+ for (let ep = seq2.start; ep < seq2.end; ) {
1196
+ const ext = tlv(der, ep, seq2.end);
1147
1197
  const id = tlv(der, ext.start, ext.end);
1148
1198
  let v = tlv(der, id.end, ext.end);
1149
1199
  let critical = false;
@@ -1432,10 +1482,10 @@ var ResponseValidator = class {
1432
1482
  `\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D certificate-choice-\u043E\u043E\u0440 \u0441\u043E\u043D\u0433\u043E\u0441\u043E\u043D\u0442\u043E\u0439 \u0442\u0430\u0430\u0440\u0430\u0445\u0433\u04AF\u0439 (serial ${leaf.serialNumber} \u2260 ${o.expectedCertificate.serialNumber})`
1433
1483
  );
1434
1484
  }
1435
- const digest = Buffer.from(digestB64, "base64");
1485
+ const digest2 = Buffer.from(digestB64, "base64");
1436
1486
  const { alg, hash } = this.algorithmOf(result, leaf, o.expectedAlgorithm ?? null, hashAlgorithm);
1437
- if (digest.length !== hashLen(hash)) throw new ValidationError("digest \u0443\u0440\u0442 hashAlgorithm-\u0442\u0430\u0439 \u0442\u0430\u0430\u0440\u0430\u0445\u0433\u04AF\u0439");
1438
- if (!verifyPrehashedSignature(digest, result.signatureValueB64, leaf.publicKey, alg, hash)) {
1487
+ if (digest2.length !== hashLen(hash)) throw new ValidationError("digest \u0443\u0440\u0442 hashAlgorithm-\u0442\u0430\u0439 \u0442\u0430\u0430\u0440\u0430\u0445\u0433\u04AF\u0439");
1488
+ if (!verifyPrehashedSignature(digest2, result.signatureValueB64, leaf.publicKey, alg, hash)) {
1439
1489
  throw new ValidationError("\u0413\u0430\u0440\u044B\u043D \u04AF\u0441\u044D\u0433 \u04E9\u0433\u0441\u04E9\u043D digest-\u0442\u044D\u0439 \u0442\u0430\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439");
1440
1490
  }
1441
1491
  return {
@@ -1641,13 +1691,20 @@ var ResponseValidator = class {
1641
1691
  * "skipped" (hook алга). "revoked", мөн hard-fail горимд "unknown" → ValidationError; hook-ийн алдаа дамжина.
1642
1692
  */
1643
1693
  async checkRevocationStatus(cert) {
1644
- if (!this.revocation) return "skipped";
1645
- const status = await this.revocation(cert, this.findIssuer(cert));
1694
+ return this.checkRevocationWith(cert, this.revocation, this.revocationMode);
1695
+ }
1696
+ /**
1697
+ * {@link checkRevocationStatus}-тэй ижил, гэхдээ TrustConfig-ийн биш өгсөн checker ба горимоор (issuer-ийг trust
1698
+ * pool-оос олно). checker алга бол "skipped"; горим анхдагч "hard-fail".
1699
+ */
1700
+ async checkRevocationWith(cert, checker, mode = "hard-fail") {
1701
+ if (!checker) return "skipped";
1702
+ const status = await checker(cert, this.findIssuer(cert));
1646
1703
  if (status === "good") return "good";
1647
1704
  if (status === "revoked") {
1648
1705
  throw new ValidationError("\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0446\u0443\u0446\u043B\u0430\u0433\u0434\u0441\u0430\u043D (revoked)");
1649
1706
  }
1650
- if (this.revocationMode === "hard-fail") {
1707
+ if (mode === "hard-fail") {
1651
1708
  throw new ValidationError("\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0438\u0439 revocation \u0442\u04E9\u043B\u04E9\u0432 \u0442\u043E\u0433\u0442\u043E\u043E\u0433\u0434\u0441\u043E\u043D\u0433\u04AF\u0439 (hard-fail)");
1652
1709
  }
1653
1710
  return "unchecked";
@@ -1676,9 +1733,9 @@ function verifyPayloadSignature(payload, sigB64, key, alg, hash) {
1676
1733
  if (alg === "ecdsa-with-SHA256") return (0, import_node_crypto4.verify)(h, payload, { key, dsaEncoding: "der" }, sig);
1677
1734
  return (0, import_node_crypto4.verify)(h, payload, { key, padding: import_node_crypto4.constants.RSA_PKCS1_PADDING }, sig);
1678
1735
  }
1679
- function verifyPrehashedSignature(digest, sigB64, key, alg, hash) {
1736
+ function verifyPrehashedSignature(digest2, sigB64, key, alg, hash) {
1680
1737
  const sig = Buffer.from(sigB64, "base64");
1681
- if (alg === "ecdsa-with-SHA256") return ecdsaP256VerifyPrehashed(digest, sig, key);
1738
+ if (alg === "ecdsa-with-SHA256") return ecdsaP256VerifyPrehashed(digest2, sig, key);
1682
1739
  const modBits = key.asymmetricKeyDetails?.modulusLength ?? 0;
1683
1740
  if (!modBits || sig.length !== Math.ceil(modBits / 8)) return false;
1684
1741
  let em;
@@ -1687,10 +1744,10 @@ function verifyPrehashedSignature(digest, sigB64, key, alg, hash) {
1687
1744
  } catch {
1688
1745
  return false;
1689
1746
  }
1690
- if (alg === "rsassa-pss") return emsaPssVerify(digest, em, modBits - 1, hash);
1747
+ if (alg === "rsassa-pss") return emsaPssVerify(digest2, em, modBits - 1, hash);
1691
1748
  const prefix = DIGEST_INFO[hash];
1692
1749
  if (!prefix) return false;
1693
- const t = Buffer.concat([prefix, digest]);
1750
+ const t = Buffer.concat([prefix, digest2]);
1694
1751
  const expected = Buffer.concat([Buffer.from([0, 1]), Buffer.alloc(em.length - t.length - 3, 255), Buffer.from([0]), t]);
1695
1752
  return em.length === expected.length && em.equals(expected);
1696
1753
  }
@@ -1741,7 +1798,7 @@ function derInts(sig) {
1741
1798
  }
1742
1799
  return i === sig.length ? [out[0], out[1]] : null;
1743
1800
  }
1744
- function ecdsaP256VerifyPrehashed(digest, sig, key) {
1801
+ function ecdsaP256VerifyPrehashed(digest2, sig, key) {
1745
1802
  if (key.asymmetricKeyDetails?.namedCurve !== "prime256v1") return false;
1746
1803
  const jwk = key.export({ format: "jwk" });
1747
1804
  if (!jwk.x || !jwk.y) return false;
@@ -1751,7 +1808,7 @@ function ecdsaP256VerifyPrehashed(digest, sig, key) {
1751
1808
  const [r, sv] = rs;
1752
1809
  const { n } = P256;
1753
1810
  if (r <= 0n || r >= n || sv <= 0n || sv >= n) return false;
1754
- const z = BigInt("0x" + digest.subarray(0, 32).toString("hex"));
1811
+ const z = BigInt("0x" + digest2.subarray(0, 32).toString("hex"));
1755
1812
  const w = inv(sv, n);
1756
1813
  const R = ptAdd(ptMul(mod(z * w, n), [P256.gx, P256.gy]), ptMul(mod(r * w, n), Q));
1757
1814
  return R !== null && mod(R[0], n) === r;
@@ -1804,10 +1861,10 @@ function keyUsageBits(cert) {
1804
1861
  function extKeyUsage(cert) {
1805
1862
  const v = certExtension(cert.raw, "2.5.29.37");
1806
1863
  if (!v) return [];
1807
- const seq = tlv(v, 0, v.length);
1864
+ const seq2 = tlv(v, 0, v.length);
1808
1865
  const out = [];
1809
- for (let p = seq.start; p < seq.end; ) {
1810
- const t = tlv(v, p, seq.end);
1866
+ for (let p = seq2.start; p < seq2.end; ) {
1867
+ const t = tlv(v, p, seq2.end);
1811
1868
  out.push(oidOf(v, t));
1812
1869
  p = t.end;
1813
1870
  }
@@ -1822,9 +1879,9 @@ function subjectKeyId(cert) {
1822
1879
  function authorityKeyId(cert) {
1823
1880
  const v = certExtension(cert.raw, "2.5.29.35");
1824
1881
  if (!v) return void 0;
1825
- const seq = tlv(v, 0, v.length);
1826
- if (seq.start >= seq.end) return void 0;
1827
- const k = tlv(v, seq.start, seq.end);
1882
+ const seq2 = tlv(v, 0, v.length);
1883
+ if (seq2.start >= seq2.end) return void 0;
1884
+ const k = tlv(v, seq2.start, seq2.end);
1828
1885
  return k.tag === 128 && k.end > k.start ? Buffer.from(v.subarray(k.start, k.end)).toString("hex") : void 0;
1829
1886
  }
1830
1887
  function isCa(cert) {
@@ -1890,9 +1947,9 @@ function caReason(ca, below, now, usage) {
1890
1947
  function basicConstraintsPathLen(cert) {
1891
1948
  const v = certExtension(cert.raw, "2.5.29.19");
1892
1949
  if (!v) return void 0;
1893
- const seq = tlv(v, 0, v.length);
1894
- for (let p = seq.start; p < seq.end; ) {
1895
- const t = tlv(v, p, seq.end);
1950
+ const seq2 = tlv(v, 0, v.length);
1951
+ for (let p = seq2.start; p < seq2.end; ) {
1952
+ const t = tlv(v, p, seq2.end);
1896
1953
  if (t.tag === 2) {
1897
1954
  if (t.end - t.start > 4 || (v[t.start] & 128) !== 0) throw new ValidationError("basicConstraints: pathLen \u0431\u0443\u0440\u0443\u0443");
1898
1955
  let n = 0;
@@ -1963,6 +2020,614 @@ var EidClient = class {
1963
2020
  }
1964
2021
  };
1965
2022
 
2023
+ // src/ocsp.ts
2024
+ var import_node_crypto5 = require("crypto");
2025
+
2026
+ // src/pades/bytes.ts
2027
+ var enc = new TextEncoder();
2028
+ function concat(parts) {
2029
+ let n = 0;
2030
+ for (const p of parts) n += p.length;
2031
+ const out = new Uint8Array(n);
2032
+ let o = 0;
2033
+ for (const p of parts) {
2034
+ out.set(p, o);
2035
+ o += p.length;
2036
+ }
2037
+ return out;
2038
+ }
2039
+ function equal(a, b) {
2040
+ if (a.length !== b.length) return false;
2041
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2042
+ return true;
2043
+ }
2044
+
2045
+ // src/pades/asn1.ts
2046
+ var Asn1Error = class extends Error {
2047
+ };
2048
+ var TAG = {
2049
+ BOOLEAN: 1,
2050
+ INTEGER: 2,
2051
+ BIT_STRING: 3,
2052
+ OCTET_STRING: 4,
2053
+ NULL: 5,
2054
+ OID: 6,
2055
+ ENUMERATED: 10,
2056
+ UTF8String: 12,
2057
+ SEQUENCE: 48,
2058
+ SET: 49,
2059
+ PrintableString: 19,
2060
+ IA5String: 22,
2061
+ UTCTime: 23,
2062
+ GeneralizedTime: 24
2063
+ };
2064
+ function readNode(b, off = 0) {
2065
+ if (off + 2 > b.length) throw new Asn1Error("asn1: \u0434\u0443\u0442\u0443\u0443");
2066
+ const tag = b[off];
2067
+ if ((tag & 31) === 31) throw new Asn1Error("asn1: \u043E\u043B\u043E\u043D \u0431\u0430\u0439\u0442 tag \u0434\u044D\u043C\u0436\u0438\u0433\u0434\u044D\u0445\u0433\u04AF\u0439");
2068
+ let len = b[off + 1];
2069
+ let p = off + 2;
2070
+ if (len & 128) {
2071
+ const n = len & 127;
2072
+ if (n === 0 || n > 4) throw new Asn1Error("asn1: \u0443\u0440\u0442 \u0431\u0443\u0440\u0443\u0443");
2073
+ if (p + n > b.length) throw new Asn1Error("asn1: \u0434\u0443\u0442\u0443\u0443");
2074
+ if (b[p] === 0) throw new Asn1Error("asn1: \u0443\u0440\u0442 minimal \u0431\u0438\u0448");
2075
+ len = 0;
2076
+ for (let i = 0; i < n; i++) len = len * 256 + b[p + i];
2077
+ if (len < 128) throw new Asn1Error("asn1: \u0443\u0440\u0442 minimal \u0431\u0438\u0448");
2078
+ p += n;
2079
+ }
2080
+ if (p + len > b.length) throw new Asn1Error("asn1: \u0434\u0443\u0442\u0443\u0443");
2081
+ return { tag, raw: b.subarray(off, p + len), value: b.subarray(p, p + len) };
2082
+ }
2083
+ function parseOne(b) {
2084
+ const n = readNode(b, 0);
2085
+ if (n.raw.length !== b.length) throw new Asn1Error("asn1: \u0438\u043B\u04AF\u04AF \u04E9\u0433\u04E9\u0433\u0434\u04E9\u043B");
2086
+ return n;
2087
+ }
2088
+ function children(n) {
2089
+ const v = n instanceof Uint8Array ? n : n.value;
2090
+ const out = [];
2091
+ for (let p = 0; p < v.length; ) {
2092
+ const c = readNode(v, p);
2093
+ out.push(c);
2094
+ p += c.raw.length;
2095
+ }
2096
+ return out;
2097
+ }
2098
+ function expect(n, tag, what) {
2099
+ if (!n || n.tag !== tag) throw new Asn1Error(`asn1: ${what} \u0445\u04AF\u043B\u044D\u044D\u0441\u044D\u043D`);
2100
+ return n;
2101
+ }
2102
+ function lenBytes(n) {
2103
+ if (n < 128) return Uint8Array.of(n);
2104
+ const out = [];
2105
+ for (let x = n; x > 0; x = Math.floor(x / 256)) out.unshift(x & 255);
2106
+ return Uint8Array.from([128 | out.length, ...out]);
2107
+ }
2108
+ function tlv2(tag, ...content) {
2109
+ const v = concat(content);
2110
+ return concat([Uint8Array.of(tag), lenBytes(v.length), v]);
2111
+ }
2112
+ var seq = (...c) => tlv2(TAG.SEQUENCE, ...c);
2113
+ var octet = (b) => tlv2(TAG.OCTET_STRING, b);
2114
+ var nullDer = () => Uint8Array.of(5, 0);
2115
+ function oid(s) {
2116
+ const parts = s.split(".").map((x) => BigInt(x));
2117
+ const body = [];
2118
+ const enc2 = (v) => {
2119
+ const tmp = [Number(v & 0x7fn)];
2120
+ for (v >>= 7n; v > 0n; v >>= 7n) tmp.unshift(Number(v & 0x7fn) | 128);
2121
+ body.push(...tmp);
2122
+ };
2123
+ enc2(parts[0] * 40n + parts[1]);
2124
+ for (const p of parts.slice(2)) enc2(p);
2125
+ return tlv2(TAG.OID, Uint8Array.from(body));
2126
+ }
2127
+ function oidString(v) {
2128
+ const out = [];
2129
+ let cur = 0n;
2130
+ for (const b of v) {
2131
+ cur = cur << 7n | BigInt(b & 127);
2132
+ if (!(b & 128)) {
2133
+ out.push(cur);
2134
+ cur = 0n;
2135
+ }
2136
+ }
2137
+ if (out.length === 0) return "";
2138
+ const first = out[0];
2139
+ const a = first < 40n ? 0n : first < 80n ? 1n : 2n;
2140
+ return [a, first - a * 40n, ...out.slice(1)].join(".");
2141
+ }
2142
+ function bigFromBytes(b) {
2143
+ let v = 0n;
2144
+ for (const x of b) v = v << 8n | BigInt(x);
2145
+ return v;
2146
+ }
2147
+ function intValue(v) {
2148
+ if (v.length === 0) throw new Asn1Error("asn1: \u0445\u043E\u043E\u0441\u043E\u043D INTEGER");
2149
+ let x = bigFromBytes(v);
2150
+ if (v[0] & 128) x -= 1n << BigInt(v.length * 8);
2151
+ return x;
2152
+ }
2153
+ function parseTime(n) {
2154
+ const s = String.fromCharCode(...n.value);
2155
+ let m;
2156
+ if (n.tag === TAG.UTCTime) {
2157
+ m = /^(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})?(Z|[+-]\d{4})$/.exec(s);
2158
+ if (!m) throw new Asn1Error("asn1: UTCTime \u0431\u0443\u0440\u0443\u0443");
2159
+ let y = Number(m[1]);
2160
+ y += y < 50 ? 2e3 : 1900;
2161
+ return mkDate(y, m[2], m[3], m[4], m[5], m[6] ?? "00", "", m[7]);
2162
+ }
2163
+ if (n.tag === TAG.GeneralizedTime) {
2164
+ m = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\.\d+)?(Z|[+-]\d{4})$/.exec(s);
2165
+ if (!m) throw new Asn1Error("asn1: GeneralizedTime \u0431\u0443\u0440\u0443\u0443");
2166
+ return mkDate(Number(m[1]), m[2], m[3], m[4], m[5], m[6], m[7] ?? "", m[8]);
2167
+ }
2168
+ throw new Asn1Error("asn1: \u0446\u0430\u0433 \u0445\u04AF\u043B\u044D\u044D\u0441\u044D\u043D");
2169
+ }
2170
+ function parsePreciseTime(n) {
2171
+ const date = parseTime(n);
2172
+ const m = /\.(\d+)/.exec(String.fromCharCode(...n.value));
2173
+ const nanos = m ? Number((m[1] + "000000000").slice(0, 9)) : 0;
2174
+ return { date, nanos };
2175
+ }
2176
+ function mkDate(y, mo, d, h, mi, s, frac, z) {
2177
+ let t = Date.UTC(y, Number(mo) - 1, Number(d), Number(h), Number(mi), Number(s));
2178
+ if (frac) t += Math.floor(Number("0" + frac) * 1e3);
2179
+ if (z !== "Z") {
2180
+ const off = (Number(z.slice(1, 3)) * 60 + Number(z.slice(3, 5))) * 6e4;
2181
+ t += z[0] === "+" ? -off : off;
2182
+ }
2183
+ return new Date(t);
2184
+ }
2185
+
2186
+ // src/pades/crypto.ts
2187
+ var subtlePromise;
2188
+ var nodeImport = (m) => import(
2189
+ /* @vite-ignore */
2190
+ /* webpackIgnore: true */
2191
+ m
2192
+ );
2193
+ async function webcrypto() {
2194
+ const g = globalThis.crypto;
2195
+ if (g?.subtle) return g;
2196
+ const m = await nodeImport("node:crypto");
2197
+ if (!m.webcrypto) throw new Error("Web Crypto \u0430\u043B\u0433\u0430");
2198
+ return m.webcrypto;
2199
+ }
2200
+ function subtle() {
2201
+ subtlePromise ??= webcrypto().then((c) => c.subtle);
2202
+ return subtlePromise;
2203
+ }
2204
+ async function sha1(b) {
2205
+ return new Uint8Array(await (await subtle()).digest("SHA-1", b));
2206
+ }
2207
+
2208
+ // src/pades/x509.ts
2209
+ var OID = {
2210
+ rsaEncryption: "1.2.840.113549.1.1.1",
2211
+ rsaPSS: "1.2.840.113549.1.1.10",
2212
+ sha256WithRSA: "1.2.840.113549.1.1.11",
2213
+ sha384WithRSA: "1.2.840.113549.1.1.12",
2214
+ sha512WithRSA: "1.2.840.113549.1.1.13",
2215
+ ecPublicKey: "1.2.840.10045.2.1",
2216
+ ecdsaSHA256: "1.2.840.10045.4.3.2",
2217
+ ecdsaSHA384: "1.2.840.10045.4.3.3",
2218
+ ecdsaSHA512: "1.2.840.10045.4.3.4",
2219
+ p256: "1.2.840.10045.3.1.7",
2220
+ p384: "1.3.132.0.34",
2221
+ p521: "1.3.132.0.35",
2222
+ sha1: "1.3.14.3.2.26",
2223
+ sha256: "2.16.840.1.101.3.4.2.1",
2224
+ sha384: "2.16.840.1.101.3.4.2.2",
2225
+ sha512: "2.16.840.1.101.3.4.2.3",
2226
+ commonName: "2.5.4.3",
2227
+ surname: "2.5.4.4",
2228
+ givenName: "2.5.4.42",
2229
+ extKeyUsage: "2.5.29.37",
2230
+ keyUsage: "2.5.29.15",
2231
+ basicConstraints: "2.5.29.19",
2232
+ crlDP: "2.5.29.31",
2233
+ aia: "1.3.6.1.5.5.7.1.1",
2234
+ ocspMethod: "1.3.6.1.5.5.7.48.1",
2235
+ kpTimeStamping: "1.3.6.1.5.5.7.3.8",
2236
+ kpOCSPSigning: "1.3.6.1.5.5.7.3.9",
2237
+ kpAny: "2.5.29.37.0"
2238
+ };
2239
+ var CertError = class extends Error {
2240
+ };
2241
+ function bitString(n) {
2242
+ expect(n, TAG.BIT_STRING, "BIT STRING");
2243
+ if (n.value.length === 0) throw new Asn1Error("BIT STRING \u0445\u043E\u043E\u0441\u043E\u043D");
2244
+ return n.value.subarray(1);
2245
+ }
2246
+ function decodeDirString(n) {
2247
+ if (n.tag === 30) {
2248
+ let s = "";
2249
+ for (let i = 0; i + 1 < n.value.length; i += 2) s += String.fromCharCode(n.value[i] << 8 | n.value[i + 1]);
2250
+ return s;
2251
+ }
2252
+ if (n.tag === TAG.UTF8String) return new TextDecoder().decode(n.value);
2253
+ return String.fromCharCode(...n.value);
2254
+ }
2255
+ function nameAttrs(name) {
2256
+ const out = [];
2257
+ for (const rdn of children(name)) {
2258
+ for (const atv of children(rdn)) {
2259
+ const [t, v] = children(atv);
2260
+ if (!t || !v) continue;
2261
+ out.push([oidString(t.value), decodeDirString(v)]);
2262
+ }
2263
+ }
2264
+ return out;
2265
+ }
2266
+ function parseCertificate(der) {
2267
+ try {
2268
+ const top = parseOne(der);
2269
+ expect(top, TAG.SEQUENCE, "Certificate");
2270
+ const [tbsN, sigAlg, sigN] = children(top);
2271
+ const tbs = expect(tbsN, TAG.SEQUENCE, "TBSCertificate");
2272
+ const f = children(tbs);
2273
+ let i = 0;
2274
+ let version = 1;
2275
+ if (f[0]?.tag === 160) {
2276
+ const v = expect(children(f[0])[0], TAG.INTEGER, "version");
2277
+ version = v.value[0] + 1;
2278
+ i++;
2279
+ }
2280
+ const serial = expect(f[i++], TAG.INTEGER, "serial").value;
2281
+ i++;
2282
+ const issuer = expect(f[i++], TAG.SEQUENCE, "issuer");
2283
+ const validity = children(expect(f[i++], TAG.SEQUENCE, "validity"));
2284
+ const subject = expect(f[i++], TAG.SEQUENCE, "subject");
2285
+ const spki = expect(f[i++], TAG.SEQUENCE, "SPKI");
2286
+ const [algN, pkN] = children(spki);
2287
+ const algParts = children(expect(algN, TAG.SEQUENCE, "AlgorithmIdentifier"));
2288
+ const keyAlg = oidString(expect(algParts[0], TAG.OID, "alg").value);
2289
+ const curve = keyAlg === OID.ecPublicKey && algParts[1]?.tag === TAG.OID ? oidString(algParts[1].value) : void 0;
2290
+ const extensions = [];
2291
+ for (; i < f.length; i++) {
2292
+ if (f[i].tag !== 163) continue;
2293
+ for (const e of children(expect(children(f[i])[0], TAG.SEQUENCE, "extensions"))) {
2294
+ const p = children(e);
2295
+ const o = oidString(expect(p[0], TAG.OID, "ext oid").value);
2296
+ let critical = false;
2297
+ let val = p[1];
2298
+ if (val?.tag === TAG.BOOLEAN) {
2299
+ critical = val.value[0] !== 0;
2300
+ val = p[2];
2301
+ }
2302
+ extensions.push({ oid: o, critical, value: expect(val, TAG.OCTET_STRING, "ext value").value });
2303
+ }
2304
+ }
2305
+ const subj = nameAttrs(subject);
2306
+ const iss = nameAttrs(issuer);
2307
+ const last = (a, o) => a.filter(([k]) => k === o).map(([, v]) => v).pop() ?? "";
2308
+ const cert = {
2309
+ raw: top.raw,
2310
+ tbs: tbs.raw,
2311
+ version,
2312
+ serial,
2313
+ sigAlg: expect(sigAlg, TAG.SEQUENCE, "sigAlg"),
2314
+ signature: bitString(sigN),
2315
+ rawIssuer: issuer.raw,
2316
+ rawSubject: subject.raw,
2317
+ spki: spki.raw,
2318
+ keyAlg,
2319
+ curve,
2320
+ publicKey: bitString(pkN),
2321
+ notBefore: parseTime(validity[0]),
2322
+ notAfter: parseTime(validity[1]),
2323
+ extensions,
2324
+ subjectCN: last(subj, OID.commonName),
2325
+ issuerCN: last(iss, OID.commonName),
2326
+ // Go SignerDisplayName: Subject.Names-ийн давталтын сүүлийн утга.
2327
+ givenName: last(subj, OID.givenName),
2328
+ surname: last(subj, OID.surname),
2329
+ ocspServers: [],
2330
+ crlDPs: [],
2331
+ isCA: false,
2332
+ basicConstraintsValid: false,
2333
+ keyUsage: 0,
2334
+ extKeyUsage: []
2335
+ };
2336
+ for (const e of extensions) parseExtension(cert, e);
2337
+ return cert;
2338
+ } catch (e) {
2339
+ throw new CertError(`\u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0437\u0430\u0434\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439: ${e instanceof Error ? e.message : String(e)}`);
2340
+ }
2341
+ }
2342
+ function parseExtension(c, e) {
2343
+ const n = parseOne(e.value);
2344
+ switch (e.oid) {
2345
+ case OID.basicConstraints: {
2346
+ c.basicConstraintsValid = true;
2347
+ const p = children(n);
2348
+ c.isCA = p[0]?.tag === TAG.BOOLEAN && p[0].value[0] !== 0;
2349
+ break;
2350
+ }
2351
+ case OID.keyUsage: {
2352
+ const bs = n.value;
2353
+ let ku = 0;
2354
+ for (let i = 1; i < bs.length; i++) {
2355
+ for (let b = 0; b < 8; b++) if (bs[i] & 128 >> b) ku |= 1 << (i - 1) * 8 + b;
2356
+ }
2357
+ c.keyUsage = ku;
2358
+ break;
2359
+ }
2360
+ case OID.extKeyUsage:
2361
+ c.extKeyUsage = children(n).map((x) => oidString(x.value));
2362
+ break;
2363
+ case OID.aia:
2364
+ for (const ad of children(n)) {
2365
+ const [m, loc] = children(ad);
2366
+ if (m && loc && oidString(m.value) === OID.ocspMethod && loc.tag === 134) c.ocspServers.push(String.fromCharCode(...loc.value));
2367
+ }
2368
+ break;
2369
+ case OID.crlDP:
2370
+ for (const dp of children(n)) {
2371
+ for (const f of children(dp)) {
2372
+ if (f.tag !== 160) continue;
2373
+ for (const fn of children(f)) {
2374
+ if (fn.tag !== 160) continue;
2375
+ for (const gn of children(fn)) if (gn.tag === 134) c.crlDPs.push(String.fromCharCode(...gn.value));
2376
+ }
2377
+ }
2378
+ }
2379
+ break;
2380
+ }
2381
+ }
2382
+ var HASH_BY_OID = {
2383
+ [OID.sha1]: "SHA-1",
2384
+ [OID.sha256]: "SHA-256",
2385
+ [OID.sha384]: "SHA-384",
2386
+ [OID.sha512]: "SHA-512"
2387
+ };
2388
+ function schemeFor(alg) {
2389
+ const p = children(alg);
2390
+ const o = oidString(expect(p[0], TAG.OID, "sigAlg").value);
2391
+ switch (o) {
2392
+ case "1.2.840.113549.1.1.5":
2393
+ return { kind: "rsa", hash: "SHA-1" };
2394
+ case OID.sha256WithRSA:
2395
+ return { kind: "rsa", hash: "SHA-256" };
2396
+ case OID.sha384WithRSA:
2397
+ return { kind: "rsa", hash: "SHA-384" };
2398
+ case OID.sha512WithRSA:
2399
+ return { kind: "rsa", hash: "SHA-512" };
2400
+ case OID.ecdsaSHA256:
2401
+ return { kind: "ecdsa", hash: "SHA-256" };
2402
+ case OID.ecdsaSHA384:
2403
+ return { kind: "ecdsa", hash: "SHA-384" };
2404
+ case OID.ecdsaSHA512:
2405
+ return { kind: "ecdsa", hash: "SHA-512" };
2406
+ case OID.rsaPSS: {
2407
+ let hash = "SHA-1";
2408
+ let salt = 20;
2409
+ for (const f of p[1] ? children(p[1]) : []) {
2410
+ if (f.tag === 160) hash = HASH_BY_OID[oidString(children(children(f)[0])[0].value)] ?? hash;
2411
+ if (f.tag === 162) salt = Number(children(f)[0].value[0]);
2412
+ }
2413
+ return { kind: "pss", hash, saltLength: salt };
2414
+ }
2415
+ }
2416
+ throw new CertError(`\u0433\u0430\u0440\u044B\u043D \u04AF\u0441\u0433\u0438\u0439\u043D \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C \u0434\u044D\u043C\u0436\u0438\u0433\u0434\u044D\u0445\u0433\u04AF\u0439: ${o}`);
2417
+ }
2418
+ var CURVES = {
2419
+ [OID.p256]: { name: "P-256", size: 32 },
2420
+ [OID.p384]: { name: "P-384", size: 48 },
2421
+ [OID.p521]: { name: "P-521", size: 66 }
2422
+ };
2423
+ function ecdsaDerToRaw(der, size) {
2424
+ const [r, s] = children(parseOne(der));
2425
+ const out = new Uint8Array(2 * size);
2426
+ const put = (v, at) => {
2427
+ let x = v;
2428
+ while (x.length > 0 && x[0] === 0) x = x.subarray(1);
2429
+ if (x.length > size) throw new CertError("ECDSA \u0443\u0442\u0433\u0430 \u0445\u044D\u0442 \u0443\u0440\u0442");
2430
+ out.set(x, at + size - x.length);
2431
+ };
2432
+ put(expect(r, TAG.INTEGER, "r").value, 0);
2433
+ put(expect(s, TAG.INTEGER, "s").value, size);
2434
+ return out;
2435
+ }
2436
+ async function verifySignature(pub, alg, data, sig) {
2437
+ const sch = schemeFor(alg);
2438
+ return verifyWith(pub, sch, data, sig);
2439
+ }
2440
+ async function verifyWith(pub, sch, data, sig) {
2441
+ const s = await subtle();
2442
+ try {
2443
+ if (sch.kind === "ecdsa") {
2444
+ if (pub.keyAlg !== OID.ecPublicKey) return false;
2445
+ const c = CURVES[pub.curve ?? ""];
2446
+ if (!c) return false;
2447
+ const key2 = await s.importKey("spki", pub.spki, { name: "ECDSA", namedCurve: c.name }, false, ["verify"]);
2448
+ return await s.verify({ name: "ECDSA", hash: sch.hash }, key2, ecdsaDerToRaw(sig, c.size), data);
2449
+ }
2450
+ if (pub.keyAlg !== OID.rsaEncryption) return false;
2451
+ if (sch.kind === "pss") {
2452
+ const key2 = await s.importKey("spki", pub.spki, { name: "RSA-PSS", hash: sch.hash }, false, ["verify"]);
2453
+ return await s.verify({ name: "RSA-PSS", saltLength: sch.saltLength ?? 32 }, key2, sig, data);
2454
+ }
2455
+ const key = await s.importKey("spki", pub.spki, { name: "RSASSA-PKCS1-v1_5", hash: sch.hash }, false, ["verify"]);
2456
+ return await s.verify({ name: "RSASSA-PKCS1-v1_5" }, key, sig, data);
2457
+ } catch {
2458
+ return false;
2459
+ }
2460
+ }
2461
+ function checkSignature(parent, alg, signed, sig) {
2462
+ return verifySignature(parent, alg, signed, sig);
2463
+ }
2464
+ var HANDLED = /* @__PURE__ */ new Set([
2465
+ OID.keyUsage,
2466
+ OID.extKeyUsage,
2467
+ OID.basicConstraints,
2468
+ "2.5.29.17",
2469
+ // SAN
2470
+ "2.5.29.30",
2471
+ // name constraints
2472
+ "2.5.29.32",
2473
+ // policies
2474
+ OID.crlDP,
2475
+ OID.aia,
2476
+ "2.5.29.14",
2477
+ "2.5.29.35"
2478
+ ]);
2479
+
2480
+ // src/pades/cms.ts
2481
+ var HASHES2 = {
2482
+ [OID.sha1]: "SHA-1",
2483
+ [OID.sha256]: "SHA-256",
2484
+ [OID.sha384]: "SHA-384",
2485
+ [OID.sha512]: "SHA-512"
2486
+ };
2487
+
2488
+ // src/pades/ltv.ts
2489
+ var MAX_TSA_SKEW_MS = 10 * 6e4;
2490
+ async function ocspRequest(c, issuer) {
2491
+ const keyHash = await sha1(issuer.publicKey);
2492
+ const nameHash = await sha1(issuer.rawSubject);
2493
+ const certID = seq(seq(oid(OID.sha1), nullDer()), octet(nameHash), octet(keyHash), Uint8Array.of(TAG.INTEGER, ...lenOf(c.serial.length), ...c.serial));
2494
+ return seq(seq(seq(seq(certID))));
2495
+ }
2496
+ function lenOf(n) {
2497
+ if (n < 128) return [n];
2498
+ const out = [];
2499
+ for (let x = n; x > 0; x = Math.floor(x / 256)) out.unshift(x & 255);
2500
+ return [128 | out.length, ...out];
2501
+ }
2502
+ var OID_OCSP_BASIC = "1.3.6.1.5.5.7.48.1.1";
2503
+ async function parseOCSPForCert(der, c, issuer) {
2504
+ const top = children(expect(parseOne(der), TAG.SEQUENCE, "OCSPResponse"));
2505
+ if (expect(top[0], TAG.ENUMERATED, "status").value[0] !== 0) throw new Error("OCSP response status");
2506
+ const rb = children(expect(children(expect(top[1], 160, "responseBytes"))[0], TAG.SEQUENCE, "ResponseBytes"));
2507
+ if (oidString(expect(rb[0], TAG.OID, "responseType").value) !== OID_OCSP_BASIC) throw new Error("bad OCSP response type");
2508
+ const basic = children(expect(parseOne(expect(rb[1], TAG.OCTET_STRING, "response").value), TAG.SEQUENCE, "BasicOCSPResponse"));
2509
+ const tbs = expect(basic[0], TAG.SEQUENCE, "tbsResponseData");
2510
+ const sigAlg = expect(basic[1], TAG.SEQUENCE, "sigAlg");
2511
+ const sigBits = expect(basic[2], TAG.BIT_STRING, "signature").value.subarray(1);
2512
+ const tf = children(tbs);
2513
+ let i = 0;
2514
+ if (tf[0]?.tag === 160) i++;
2515
+ const rid = tf[i++];
2516
+ if (rid.tag !== 161 && rid.tag !== 162) throw new Error("invalid responder id tag");
2517
+ i++;
2518
+ const responses = children(expect(tf[i], TAG.SEQUENCE, "responses"));
2519
+ if (responses.length === 0) throw new Error("OCSP response contains bad number of responses");
2520
+ const serial = intValue(c.serial);
2521
+ let single;
2522
+ for (const r of responses) {
2523
+ const f = children(r);
2524
+ const cid2 = children(f[0]);
2525
+ if (intValue(expect(cid2[3], TAG.INTEGER, "serial").value) === serial) {
2526
+ single = f;
2527
+ break;
2528
+ }
2529
+ }
2530
+ if (!single) throw new Error("no response matching the supplied certificate");
2531
+ let certificate;
2532
+ if (basic[3]?.tag === 160) {
2533
+ const certs = children(expect(children(basic[3])[0], TAG.SEQUENCE, "certs"));
2534
+ if (certs[0]) certificate = parseCertificate(certs[0].raw);
2535
+ }
2536
+ if (certificate) {
2537
+ if (!await verifySignature(certificate, sigAlg, tbs.raw, sigBits)) throw new Error("bad signature on embedded certificate");
2538
+ if (!await checkSignature(issuer, certificate.sigAlg, certificate.tbs, certificate.signature)) throw new Error("bad OCSP signature");
2539
+ } else if (!await verifySignature(issuer, sigAlg, tbs.raw, sigBits)) {
2540
+ throw new Error("bad OCSP signature");
2541
+ }
2542
+ const cid = children(single[0]);
2543
+ const hashAlg = oidString(children(cid[0])[0].value);
2544
+ if (![OID.sha1, OID.sha256, OID.sha384, OID.sha512].includes(hashAlg)) throw new Error("unsupported issuer hash algorithm");
2545
+ const st = single[1];
2546
+ const status = st.tag === 128 ? "good" : st.tag === 161 ? "revoked" : st.tag === 130 ? "unknown" : void 0;
2547
+ if (!status) throw new Error("OCSP certStatus");
2548
+ const thisUpdate = parsePreciseTime(expect(single[2], TAG.GeneralizedTime, "thisUpdate"));
2549
+ let nextUpdate;
2550
+ for (const x of single.slice(3)) {
2551
+ if (x.tag === 160) nextUpdate = parsePreciseTime(children(x)[0]).date;
2552
+ if (x.tag === 161) {
2553
+ for (const ext of children(children(x)[0])) {
2554
+ const e = children(ext);
2555
+ if (e[1]?.tag === TAG.BOOLEAN && e[1].value[0] !== 0) throw new Error("unsupported critical extension");
2556
+ }
2557
+ }
2558
+ }
2559
+ return { status, thisUpdate, nextUpdate, certificate };
2560
+ }
2561
+
2562
+ // src/ocsp.ts
2563
+ var CACHE_TTL_MS = 5 * 6e4;
2564
+ var CACHE_MAX = 1024;
2565
+ var MAX_BYTES = 16 << 10;
2566
+ var CLOCK_SKEW_MS = 5 * 6e4;
2567
+ var MAX_AGE_MS = 48 * 36e5;
2568
+ function ocspRevocationChecker(opts = {}) {
2569
+ const doFetch = opts.fetchImpl ?? globalThis.fetch;
2570
+ const timeoutMs = opts.timeoutMs ?? 2e3;
2571
+ const totalMs = Math.max(timeoutMs, 3e3);
2572
+ const now = opts.now ?? Date.now;
2573
+ const cache = /* @__PURE__ */ new Map();
2574
+ async function ask(leaf, issuer) {
2575
+ const body = await ocspRequest(leaf, issuer);
2576
+ const deadline = Date.now() + totalMs;
2577
+ for (const raw of leaf.ocspServers) {
2578
+ const left = deadline - Date.now();
2579
+ if (left <= 0) break;
2580
+ try {
2581
+ const u = new URL(raw);
2582
+ if (u.protocol !== "http:" && u.protocol !== "https:") continue;
2583
+ const res = await doFetch(u.toString(), {
2584
+ method: "POST",
2585
+ headers: { "Content-Type": "application/ocsp-request", Accept: "application/ocsp-response" },
2586
+ body,
2587
+ redirect: "manual",
2588
+ signal: AbortSignal.timeout(Math.min(timeoutMs, left))
2589
+ });
2590
+ if (res.status !== 200) continue;
2591
+ const der = new Uint8Array(await res.arrayBuffer());
2592
+ if (der.length > MAX_BYTES) continue;
2593
+ const resp = await parseOCSPForCert(der, leaf, issuer);
2594
+ const t = now();
2595
+ const rc = resp.certificate;
2596
+ if (rc && !equal(rc.raw, issuer.raw)) {
2597
+ if (!rc.extKeyUsage.includes(OID.kpOCSPSigning)) continue;
2598
+ if (t < rc.notBefore.getTime() || t > rc.notAfter.getTime()) continue;
2599
+ }
2600
+ const tu = resp.thisUpdate.date.getTime();
2601
+ if (tu > t + CLOCK_SKEW_MS || tu < t - MAX_AGE_MS) continue;
2602
+ if (resp.nextUpdate && resp.nextUpdate.getTime() < t) continue;
2603
+ return resp.status === "good" ? "good" : "revoked";
2604
+ } catch {
2605
+ }
2606
+ }
2607
+ return void 0;
2608
+ }
2609
+ return async (cert, issuer) => {
2610
+ if (!issuer) return "unknown";
2611
+ let leaf, iss;
2612
+ try {
2613
+ leaf = parseCertificate(new Uint8Array(cert.raw));
2614
+ iss = parseCertificate(new Uint8Array(issuer.raw));
2615
+ } catch {
2616
+ return "unknown";
2617
+ }
2618
+ const key = (0, import_node_crypto5.createHash)("sha256").update(issuer.raw).digest("hex").slice(0, 16) + ":" + cert.serialNumber;
2619
+ const exp = cache.get(key);
2620
+ if (exp !== void 0 && now() < exp) return "good";
2621
+ const status = await ask(leaf, iss);
2622
+ if (!status) return "unknown";
2623
+ if (status === "good") {
2624
+ if (cache.size >= CACHE_MAX) cache.clear();
2625
+ cache.set(key, now() + CACHE_TTL_MS);
2626
+ }
2627
+ return status;
2628
+ };
2629
+ }
2630
+
1966
2631
  // src/devicelink.ts
1967
2632
  var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
1968
2633
  var VC_RE = /^[0-9]{5}$/;
@@ -2006,7 +2671,7 @@ function openOrShowQR(session, opts) {
2006
2671
  }
2007
2672
 
2008
2673
  // src/devicelinkv3.ts
2009
- var import_node_crypto5 = require("crypto");
2674
+ var import_node_crypto6 = require("crypto");
2010
2675
  var TYPES = ["QR", "Web2App", "App2App"];
2011
2676
  var PROTOCOL = { auth: "ACSP_V2", sign: "RAW_DIGEST_SIGNATURE", cert: "" };
2012
2677
  var TOKEN_RE = /^[A-Za-z0-9_-]+$/;
@@ -2063,7 +2728,7 @@ function buildDeviceLink(p) {
2063
2728
  callback,
2064
2729
  unprotected
2065
2730
  ].join("|");
2066
- const authCode = (0, import_node_crypto5.createHmac)("sha256", Buffer.from(p.sessionSecret, "base64")).update(payload, "utf8").digest("base64url");
2731
+ const authCode = (0, import_node_crypto6.createHmac)("sha256", Buffer.from(p.sessionSecret, "base64")).update(payload, "utf8").digest("base64url");
2067
2732
  return `${unprotected}&authCode=${authCode}`;
2068
2733
  }
2069
2734
  function deviceLinkInput(s, deviceLinkType, lang) {
@@ -2107,7 +2772,7 @@ function qrDeviceLinkTicker(input, onLink, opts) {
2107
2772
  }
2108
2773
 
2109
2774
  // src/login.ts
2110
- var import_node_crypto6 = require("crypto");
2775
+ var import_node_crypto7 = require("crypto");
2111
2776
  function memoryLoginStore() {
2112
2777
  const m = /* @__PURE__ */ new Map();
2113
2778
  return {
@@ -2233,13 +2898,43 @@ function readCookie(h, name) {
2233
2898
  }
2234
2899
  return "";
2235
2900
  }
2236
- var sha256hex = (s) => (0, import_node_crypto6.createHash)("sha256").update(s).digest("hex");
2901
+ var sha256hex = (s) => (0, import_node_crypto7.createHash)("sha256").update(s).digest("hex");
2237
2902
  function safeEqualHex(a, b) {
2238
2903
  const x = Buffer.from(a, "hex");
2239
2904
  const y = Buffer.from(b, "hex");
2240
- return x.length === y.length && x.length > 0 && (0, import_node_crypto6.timingSafeEqual)(x, y);
2905
+ return x.length === y.length && x.length > 0 && (0, import_node_crypto7.timingSafeEqual)(x, y);
2906
+ }
2907
+ function sleep(ms2, signal) {
2908
+ return new Promise((resolve) => {
2909
+ if (ms2 <= 0 || signal?.aborted) return resolve();
2910
+ const done = () => {
2911
+ clearTimeout(t);
2912
+ signal?.removeEventListener("abort", done);
2913
+ resolve();
2914
+ };
2915
+ const t = setTimeout(done, ms2);
2916
+ signal?.addEventListener("abort", done, { once: true });
2917
+ });
2918
+ }
2919
+ function isRefusal(e) {
2920
+ if (!(e instanceof ApiError) || e.status < 400 || e.status >= 500 || e.status === 401 || e.status === 403 || e.status === 429) {
2921
+ return false;
2922
+ }
2923
+ try {
2924
+ JSON.parse(e.body);
2925
+ return true;
2926
+ } catch {
2927
+ return false;
2928
+ }
2241
2929
  }
2242
- function toIdentity(sessionId, r, v, flowType) {
2930
+ var RevocationFailure = class extends ValidationError {
2931
+ constructor(message, revoked) {
2932
+ super(message);
2933
+ this.revoked = revoked;
2934
+ }
2935
+ revoked;
2936
+ };
2937
+ function toIdentity(sessionId, r, v, flowType, revocation) {
2243
2938
  const s = v.parsedSubject;
2244
2939
  const etsi = s.serialNumber ?? "";
2245
2940
  const m = /^PNOMN-([0-9]+)$/.exec(etsi);
@@ -2255,7 +2950,8 @@ function toIdentity(sessionId, r, v, flowType) {
2255
2950
  certificateLevel: v.certificateLevel,
2256
2951
  certificateDerB64: r.certificateDerB64 ?? "",
2257
2952
  flowType,
2258
- person: personMatchesCertificate(r.person, s) ? r.person ?? null : null
2953
+ person: personMatchesCertificate(r.person, s) ? r.person ?? null : null,
2954
+ revocation
2259
2955
  };
2260
2956
  }
2261
2957
  function createEidLogin(opts) {
@@ -2277,6 +2973,28 @@ function createEidLogin(opts) {
2277
2973
  const failureRedirect = opts.failureRedirect ?? "/";
2278
2974
  const onError = opts.onError ?? ((e) => console.error("eid-login:", e instanceof Error ? e.message : e));
2279
2975
  const rateLimit = buildRateLimit(opts.rateLimit);
2976
+ const decoyOn = opts.decoy !== false;
2977
+ const pushFloorMs = opts.pushFloorMs ?? 1e3;
2978
+ const revocation = opts.revocation === false ? void 0 : opts.revocation ?? ocspRevocationChecker();
2979
+ const revocationMode = opts.revocationMode ?? "soft-fail";
2980
+ const auditKey = opts.auditKey ?? (0, import_node_crypto7.randomBytes)(32);
2981
+ const pushKey = (idCode) => (0, import_node_crypto7.createHmac)("sha256", auditKey).update(`eid-push-audit:${idCode}`).digest("hex").slice(0, 32);
2982
+ async function emit(kind, sessionId, meta, ctx2) {
2983
+ if (!opts.onEvent) return;
2984
+ try {
2985
+ await opts.onEvent({ kind, sessionId, meta });
2986
+ } catch (e) {
2987
+ onError(e, ctx2);
2988
+ }
2989
+ }
2990
+ function endEvent(tx, endResult, ctx2) {
2991
+ const kind = endResult?.startsWith("USER_REFUSED") ? "refused" : endResult === "TIMEOUT" ? "expired" : "failed";
2992
+ return emit(kind, tx.sessionId, { mode: tx.mode, endResult: endResult ?? "UNKNOWN" }, ctx2);
2993
+ }
2994
+ function unverified(tx, e, ctx2) {
2995
+ const reason = e instanceof RevocationFailure ? e.revoked ? "revoked" : "revocation_unavailable" : "validation";
2996
+ return emit("unverified", tx.sessionId, { mode: tx.mode, reason }, ctx2);
2997
+ }
2280
2998
  const displayText = (opts.displayText ?? `${client.rpName}-\u0434 \u043D\u044D\u0432\u0442\u0440\u044D\u0445`).slice(0, 60);
2281
2999
  const interactions = [{ type: "displayTextAndPIN", displayText60: displayText }];
2282
3000
  const json = (status2, body, extra = {}) => ({
@@ -2367,11 +3085,18 @@ function createEidLogin(opts) {
2367
3085
  });
2368
3086
  }
2369
3087
  }
2370
- const binding = (0, import_node_crypto6.randomBytes)(32).toString("base64url");
3088
+ return { ...await save(tx), appLink };
3089
+ }
3090
+ async function save(tx) {
3091
+ const binding = (0, import_node_crypto7.randomBytes)(32).toString("base64url");
2371
3092
  const full = { ...tx, bindingHash: sha256hex(binding), expiresAt: Date.now() + ttl };
2372
3093
  await store.set(full.sessionId, full, ttl);
2373
3094
  const setCookie = `${cookieName}=${full.sessionId}.${binding}; ${cookieAttrs(Math.ceil(ttl / 1e3))}`;
2374
- return { tx: full, setCookie, appLink };
3095
+ return { tx: full, setCookie, appLink: void 0 };
3096
+ }
3097
+ function startDecoy() {
3098
+ const acsp = void 0;
3099
+ return save({ sessionId: (0, import_node_crypto7.randomUUID)(), mode: "push", vc: String((0, import_node_crypto7.randomInt)(1e5)).padStart(5, "0"), acsp, decoy: true });
2375
3100
  }
2376
3101
  async function start(req) {
2377
3102
  const url = new URL(req.url, "http://x");
@@ -2397,22 +3122,40 @@ function createEidLogin(opts) {
2397
3122
  const mode = body.mode;
2398
3123
  if (mode !== "qr" && mode !== "push" && mode !== "web2app") return json(400, { error: "invalid_mode" });
2399
3124
  const lang = body.lang === "en" ? "eng" : "mon";
2400
- const ctx = { request: req, mode };
3125
+ const ctx2 = { request: req, mode };
2401
3126
  if (mode === "web2app" && !opts.callbackUrl) return json(400, { error: "web2app_not_configured" });
2402
3127
  const idCode = typeof body.idCode === "string" ? body.idCode.replace(/\s+/g, "").toUpperCase() : "";
2403
3128
  if (mode === "push" && !ID_RE.test(idCode)) return json(400, { error: "invalid_id" });
2404
- const limited = () => json(429, { error: "rate_limited" }, { "Retry-After": "60" });
3129
+ const limited = async () => {
3130
+ if (mode === "push") await emit("push_limited", null, { mode, pushKey: pushKey(idCode) }, ctx2);
3131
+ return json(429, { error: "rate_limited" }, { "Retry-After": "60" });
3132
+ };
2405
3133
  const ip = req.ip || (opts.trustProxy ? forwardedClientIp(req.headers, opts.trustProxy) : void 0);
2406
3134
  if (ip && !await rateLimit(`start:${ip}`)) return limited();
2407
3135
  if (mode === "push" && !await rateLimit(`push:${idCode}`)) return limited();
3136
+ const began = Date.now();
2408
3137
  let r;
3138
+ let err;
3139
+ let decoy = false;
2409
3140
  try {
2410
3141
  r = await begin(req, mode, idCode, lang);
2411
3142
  } catch (e) {
2412
- onError(e, ctx);
2413
- const status2 = e instanceof ApiError && e.status === 429 ? 429 : 502;
2414
- return json(status2, { error: status2 === 429 ? "rate_limited" : "eid_unavailable" });
3143
+ err = e;
3144
+ }
3145
+ if (err !== void 0 && mode === "push" && decoyOn && isRefusal(err)) {
3146
+ const ae = err;
3147
+ onError(new Error(`eid-login: eID push-\u0438\u0439\u0433 \u0442\u0430\u0442\u0433\u0430\u043B\u0437\u0430\u0432 (${ae.status}${ae.code ? ` ${ae.code}` : ""}); decoy session-\u043E\u043E\u0440 \u0445\u0430\u0440\u0438\u0443\u043B\u0430\u0432`), ctx2);
3148
+ err = void 0;
3149
+ r = await startDecoy();
3150
+ decoy = true;
2415
3151
  }
3152
+ if (mode === "push" && pushFloorMs > 0) await sleep(began + pushFloorMs - Date.now(), req.signal);
3153
+ if (err !== void 0 || !r) {
3154
+ onError(err, ctx2);
3155
+ if (err instanceof ApiError && err.status === 429) return limited();
3156
+ return json(502, { error: "eid_unavailable" });
3157
+ }
3158
+ if (mode === "push") await emit("push_sent", r.tx.sessionId, { mode, pushKey: pushKey(idCode), decoy }, ctx2);
2416
3159
  if (mode === "web2app") {
2417
3160
  return { status: 302, headers: { Location: r.appLink, "Cache-Control": "no-store", "Referrer-Policy": "no-referrer", "Set-Cookie": r.setCookie }, body: "" };
2418
3161
  }
@@ -2424,30 +3167,49 @@ function createEidLogin(opts) {
2424
3167
  }
2425
3168
  async function finish(req, tx, r, flow, verifier) {
2426
3169
  const v = client.validator.validateAuth(r, tx.acsp, { expectedFlowType: flow, userChallengeVerifier: verifier });
3170
+ const ctx2 = { request: req, mode: tx.mode };
3171
+ let rev;
3172
+ let status2;
3173
+ const checker = revocation && (async (c, i) => status2 = await revocation(c, i));
3174
+ try {
3175
+ rev = await client.validator.checkRevocationWith(v.certificate, checker, revocationMode);
3176
+ } catch (e) {
3177
+ throw new RevocationFailure(`revocation: ${e instanceof Error ? e.message : String(e)}`, status2 === "revoked");
3178
+ }
3179
+ if (rev === "unchecked") onError(new Error("eid-login: OCSP \u0445\u0430\u0440\u0438\u0443\u0433\u04AF\u0439 \u2014 \u0446\u0443\u0446\u043B\u0430\u043B\u0442 \u0448\u0430\u043B\u0433\u0430\u043B\u0433\u04AF\u0439 \u0437\u04E9\u0432\u0448\u04E9\u04E9\u0440\u04E9\u0432 (soft-fail)"), ctx2);
2427
3180
  if (!await store.delete(tx.sessionId)) return void 0;
2428
- const out = await opts.onSuccess(toIdentity(tx.sessionId, r, v, flow), { request: req, mode: tx.mode }) ?? {};
3181
+ const out = await opts.onSuccess(toIdentity(tx.sessionId, r, v, flow, rev), ctx2) ?? {};
3182
+ await emit("success", tx.sessionId, { mode: tx.mode, flow, revocation: rev }, ctx2);
2429
3183
  return out;
2430
3184
  }
2431
3185
  async function status(req) {
2432
3186
  if (req.method !== "GET") return json(405, { error: "method_not_allowed" });
2433
3187
  const tx = await loadTx(req);
2434
3188
  if (!tx) return json(200, { state: "EXPIRED" }, { "Set-Cookie": clearCookie });
2435
- const ctx = { request: req, mode: tx.mode };
3189
+ const ctx2 = { request: req, mode: tx.mode };
2436
3190
  let r;
2437
- try {
2438
- r = await client.session.poll(tx.sessionId, pollMs);
2439
- } catch (e) {
2440
- if (e instanceof ApiError && e.status === 404) {
2441
- await store.delete(tx.sessionId);
2442
- return json(200, { state: "EXPIRED" }, { "Set-Cookie": clearCookie });
3191
+ if (tx.decoy) {
3192
+ await sleep(pollMs, req.signal);
3193
+ r = { state: "RUNNING" };
3194
+ } else {
3195
+ try {
3196
+ r = await client.session.poll(tx.sessionId, pollMs);
3197
+ } catch (e) {
3198
+ if (e instanceof ApiError && e.status === 404) {
3199
+ await store.delete(tx.sessionId);
3200
+ await emit("expired", tx.sessionId, { mode: tx.mode }, ctx2);
3201
+ return json(200, { state: "EXPIRED" }, { "Set-Cookie": clearCookie });
3202
+ }
3203
+ onError(e, ctx2);
3204
+ return json(502, { error: "eid_unavailable" });
2443
3205
  }
2444
- onError(e, ctx);
2445
- return json(502, { error: "eid_unavailable" });
2446
3206
  }
2447
3207
  const expiresIn = Math.max(0, Math.ceil((tx.expiresAt - Date.now()) / 1e3));
2448
3208
  if (r.state !== "COMPLETE") return json(200, { state: "PENDING", qr: qrLink(tx), vc: tx.vc, expiresIn });
2449
3209
  if (r.endResult !== "OK") {
2450
3210
  await store.delete(tx.sessionId);
3211
+ await endEvent(tx, r.endResult, ctx2);
3212
+ if (tx.mode === "push" && r.endResult === "TIMEOUT") return json(200, { state: "EXPIRED" }, { "Set-Cookie": clearCookie });
2451
3213
  return json(200, { state: "FAILED", error: r.endResult ?? "UNKNOWN" }, { "Set-Cookie": clearCookie });
2452
3214
  }
2453
3215
  if (tx.mode === "web2app") return json(200, { state: "WAIT_CALLBACK" });
@@ -2456,9 +3218,10 @@ function createEidLogin(opts) {
2456
3218
  if (!out) return json(200, { state: "EXPIRED" }, { "Set-Cookie": clearCookie });
2457
3219
  return json(200, { state: "OK", redirect: out.redirect, data: out.data }, mergeHeaders({ "Set-Cookie": clearCookie }, out.headers));
2458
3220
  } catch (e) {
2459
- onError(e, ctx);
3221
+ onError(e, ctx2);
2460
3222
  if (!(e instanceof ValidationError || e instanceof SessionFailedError)) return json(500, { error: "internal" });
2461
3223
  await store.delete(tx.sessionId);
3224
+ await unverified(tx, e, ctx2);
2462
3225
  return json(200, { state: "FAILED", error: "VALIDATION" }, { "Set-Cookie": clearCookie });
2463
3226
  }
2464
3227
  }
@@ -2469,19 +3232,23 @@ function createEidLogin(opts) {
2469
3232
  const verifier = q.get("userChallengeVerifier") ?? "";
2470
3233
  const fail2 = (code) => redirect(failTo(code), { "Set-Cookie": clearCookie });
2471
3234
  const tx = await loadTx(req);
2472
- if (!tx || tx.sessionId !== sid || tx.mode !== "web2app") return fail2(tx ? "unbound" : "expired");
2473
- if (!verifier) return fail2("unbound");
2474
- const ctx = { request: req, mode: tx.mode };
3235
+ if (!tx) return fail2("expired");
3236
+ const ctx2 = { request: req, mode: tx.mode };
3237
+ if (tx.sessionId !== sid || tx.mode !== "web2app" || !verifier) {
3238
+ await emit("flow_refused", tx.sessionId, { mode: tx.mode, reason: "unbound" }, ctx2);
3239
+ return fail2("unbound");
3240
+ }
2475
3241
  let r;
2476
3242
  try {
2477
3243
  r = await client.session.poll(tx.sessionId, 1e4);
2478
3244
  } catch (e) {
2479
- onError(e, ctx);
3245
+ onError(e, ctx2);
2480
3246
  return fail2("eid_unavailable");
2481
3247
  }
2482
3248
  if (r.state !== "COMPLETE") return fail2("not_complete");
2483
3249
  if (r.endResult !== "OK") {
2484
3250
  await store.delete(tx.sessionId);
3251
+ await endEvent(tx, r.endResult, ctx2);
2485
3252
  return fail2(r.endResult ?? "UNKNOWN");
2486
3253
  }
2487
3254
  try {
@@ -2489,9 +3256,10 @@ function createEidLogin(opts) {
2489
3256
  if (!out) return fail2("expired");
2490
3257
  return redirect(out.redirect ?? successRedirect, mergeHeaders({ "Set-Cookie": clearCookie }, out.headers));
2491
3258
  } catch (e) {
2492
- onError(e, ctx);
3259
+ onError(e, ctx2);
2493
3260
  if (!(e instanceof ValidationError || e instanceof SessionFailedError)) return fail2("internal");
2494
3261
  await store.delete(tx.sessionId);
3262
+ await unverified(tx, e, ctx2);
2495
3263
  return fail2("VALIDATION");
2496
3264
  }
2497
3265
  }
@@ -2521,7 +3289,7 @@ function createEidLogin(opts) {
2521
3289
  mode: "qr"
2522
3290
  });
2523
3291
  }
2524
- const r = await handleRequest({ method: req.method, url: req.url, headers, body, ip });
3292
+ const r = await handleRequest({ method: req.method, url: req.url, headers, body, ip, signal: req.signal });
2525
3293
  const h = new Headers();
2526
3294
  for (const [k, v] of Object.entries(r.headers)) for (const x of [v].flat()) h.append(k, x);
2527
3295
  return new Response(r.status === 302 || r.status === 303 || r.body === "" ? null : r.body, { status: r.status, headers: h });
@@ -2569,6 +3337,7 @@ function createEidLogin(opts) {
2569
3337
  interactionsToBase64,
2570
3338
  isMobileUserAgent,
2571
3339
  memoryLoginStore,
3340
+ ocspRevocationChecker,
2572
3341
  openOrShowQR,
2573
3342
  parseExpiresAt,
2574
3343
  parseRetryAfter,