@gerege-systems/eid-mongolia-sdk 0.14.2 → 0.15.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
@@ -26,8 +26,6 @@ __export(index_exports, {
26
26
  CITIZEN_ISSUING_CA_SKI: () => CITIZEN_ISSUING_CA_SKI,
27
27
  CertChoiceInvalidError: () => CertChoiceInvalidError,
28
28
  DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
29
- DEFAULT_PDF_MAX_BYTES: () => DEFAULT_PDF_MAX_BYTES,
30
- DEFAULT_PDF_TIMEOUT_MS: () => DEFAULT_PDF_TIMEOUT_MS,
31
29
  DEFAULT_SIGN_SESSION_TTL_MS: () => DEFAULT_SIGN_SESSION_TTL_MS,
32
30
  DeviceLinkError: () => DeviceLinkError,
33
31
  EidClient: () => EidClient,
@@ -38,7 +36,7 @@ __export(index_exports, {
38
36
  NoSigningCertificateError: () => NoSigningCertificateError,
39
37
  ON_BEHALF_ERROR_CODES: () => ON_BEHALF_ERROR_CODES,
40
38
  OrganizationApi: () => OrganizationApi,
41
- PdfApi: () => PdfApi,
39
+ PDF_SIGNATURE_REGISTER_FIELDS: () => PDF_SIGNATURE_REGISTER_FIELDS,
42
40
  PersonApi: () => PersonApi,
43
41
  RETRYABLE_ERROR_CODES: () => RETRYABLE_ERROR_CODES,
44
42
  RequestInvalidError: () => RequestInvalidError,
@@ -52,6 +50,7 @@ __export(index_exports, {
52
50
  acspV2Digest: () => acspV2Digest,
53
51
  buildAcspV2Payload: () => buildAcspV2Payload,
54
52
  buildDeviceLink: () => buildDeviceLink,
53
+ buildRegisterBody: () => buildRegisterBody,
55
54
  createEidLogin: () => createEidLogin,
56
55
  deviceLink: () => deviceLink,
57
56
  deviceLinkInput: () => deviceLinkInput,
@@ -67,6 +66,7 @@ __export(index_exports, {
67
66
  personMatchesCertificate: () => personMatchesCertificate,
68
67
  qrDeviceLinkTicker: () => qrDeviceLinkTicker,
69
68
  randomChallenge: () => randomChallenge,
69
+ registrationFromCompleted: () => registrationFromCompleted,
70
70
  resolveBaseUrl: () => resolveBaseUrl,
71
71
  sessionExpiry: () => sessionExpiry,
72
72
  sha256Base64: () => sha256Base64,
@@ -226,15 +226,6 @@ var Http = class {
226
226
  get(path, timeoutMs) {
227
227
  return this.json("GET", path, void 0, void 0, timeoutMs);
228
228
  }
229
- /** multipart/form-data POST (Content-Type-ийг boundary-тай нь fetch тавина). JSON хариу. */
230
- postMultipart(path, form, timeoutMs) {
231
- return this.json("POST", path, form, void 0, timeoutMs);
232
- }
233
- /** GET — хариуг байтаар (ж: гарын үсэгтэй PDF), `maxBytes` хүртэл; хэтэрвэл {@link ResponseTooLargeError}. */
234
- async getBytes(path, timeoutMs, maxBytes = 64 << 20) {
235
- const res = await this.send("GET", path, void 0, void 0, timeoutMs);
236
- return readLimited(res, maxBytes, `GET ${maskPath(path)}`);
237
- }
238
229
  async json(method, path, body, contentType, timeoutMs) {
239
230
  const res = await this.send(method, path, body, contentType, timeoutMs);
240
231
  const text = new TextDecoder().decode(await readLimited(res, MAX_JSON_BYTES, `${method} ${maskPath(path)}`));
@@ -339,8 +330,6 @@ var RP_ROUTES = {
339
330
  signDeviceLinkDocument: { method: "POST", path: "/signature/device-link/document/{documentNumber}" },
340
331
  certChoiceDeviceLink: { method: "POST", path: "/signature/certificate-choice/device-link/anonymous" },
341
332
  certChoiceEtsi: { method: "POST", path: "/signature/certificate-choice/notification/etsi/{semanticsIdentifier}" },
342
- pdfPrepare: { method: "POST", path: "/pdf/sign/prepare" },
343
- pdfDocument: { method: "GET", path: "/pdf/sign/{sessionID}/document" },
344
333
  orgRepresentations: { method: "GET", path: "/organization/representations/etsi/{personEtsi}" },
345
334
  session: { method: "GET", path: "/session/{sessionId}" },
346
335
  personCertificates: { method: "GET", path: "/certificates/etsi/{personEtsi}" },
@@ -349,15 +338,29 @@ var RP_ROUTES = {
349
338
  personSummary: { method: "GET", path: "/person/summary/etsi/{personEtsi}" },
350
339
  personChildren: { method: "GET", path: "/person/children/etsi/{personEtsi}" },
351
340
  tokenCertificate: { method: "POST", path: "/token/certificate" },
352
- tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" }
341
+ tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" },
342
+ pdfSignatures: { method: "POST", path: "/pdf/signatures" }
353
343
  };
354
344
  var RP_ROUTES_UNDISCOVERED = {
355
345
  tokenAuthChallenge: { method: "POST", path: "/token/auth/challenge" },
356
- tokenAuthVerify: { method: "POST", path: "/token/auth/verify" },
357
- pdfTokenPrepare: { method: "POST", path: "/pdf/token/prepare" },
358
- pdfTokenFinalize: { method: "POST", path: "/pdf/token/{signingID}/finalize" },
359
- pdfTokenDocument: { method: "GET", path: "/pdf/token/{signingID}/document" }
346
+ tokenAuthVerify: { method: "POST", path: "/token/auth/verify" }
360
347
  };
348
+ var PDF_SIGNATURE_REGISTER_FIELDS = [
349
+ "docID",
350
+ "revIndex",
351
+ "medium",
352
+ "sessionID",
353
+ "cms",
354
+ "fileName",
355
+ "inSha256",
356
+ "pageSha256",
357
+ "sigRevSha256",
358
+ "outSha256",
359
+ "priorCmsSha256",
360
+ "priorRevisionSha256",
361
+ "onBehalfOf",
362
+ "signerEtsi"
363
+ ];
361
364
  function routePath(route, ...args) {
362
365
  let i = 0;
363
366
  const out = route.path.replace(/\{[^}]*\}/g, () => {
@@ -520,6 +523,84 @@ var AuthApi = class {
520
523
  };
521
524
 
522
525
  // src/sign.ts
526
+ function registrationFromCompleted(done, extra) {
527
+ const out = {
528
+ docID: done.docId,
529
+ revIndex: done.revIndex,
530
+ cms: done.cms,
531
+ fileName: extra.fileName,
532
+ inSha256: done.inSha256,
533
+ sigRevSha256: done.sigRevSha256,
534
+ outSha256: done.outSha256
535
+ };
536
+ if (extra.medium) out.medium = extra.medium;
537
+ if (extra.sessionID) out.sessionID = extra.sessionID;
538
+ if (done.hasPage && done.pageSha256) out.pageSha256 = done.pageSha256;
539
+ if (done.priorCmsSha256.length) out.priorCmsSha256 = done.priorCmsSha256;
540
+ if (extra.priorRevisionSha256?.length) out.priorRevisionSha256 = extra.priorRevisionSha256;
541
+ if (extra.onBehalfOf) out.onBehalfOf = extra.onBehalfOf;
542
+ if (extra.signerEtsi) out.signerEtsi = extra.signerEtsi;
543
+ return out;
544
+ }
545
+ var HEX64 = /^[0-9a-f]{64}$/;
546
+ var MAX_CMS_BYTES = 28672;
547
+ function buildRegisterBody(input) {
548
+ const fail2 = (m) => {
549
+ throw new ValidationError(`registerSignature: ${m}`);
550
+ };
551
+ if (!UUID_RE.test(input.docID ?? "")) fail2("docID UUID \u0431\u0438\u0448");
552
+ if (!Number.isInteger(input.revIndex) || input.revIndex < 1 || input.revIndex > 16) fail2("revIndex 1\u201316");
553
+ const hex = (name, v) => {
554
+ const x = v?.trim().toLowerCase() ?? "";
555
+ if (!HEX64.test(x)) fail2(`${name} SHA-256 hex (64) \u0431\u0438\u0448`);
556
+ return x;
557
+ };
558
+ let cmsB64;
559
+ if (typeof input.cms === "string") {
560
+ const raw = Buffer.from(input.cms, "base64");
561
+ if (raw.length === 0 || raw.toString("base64") !== input.cms) fail2("cms \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442 base64 \u0431\u0438\u0448");
562
+ cmsB64 = input.cms;
563
+ if (raw.length > MAX_CMS_BYTES) fail2(`cms > ${MAX_CMS_BYTES} \u0431\u0430\u0439\u0442`);
564
+ } else {
565
+ if (!input.cms?.length) fail2("cms \u0445\u043E\u043E\u0441\u043E\u043D");
566
+ if (input.cms.length > MAX_CMS_BYTES) fail2(`cms > ${MAX_CMS_BYTES} \u0431\u0430\u0439\u0442`);
567
+ cmsB64 = Buffer.from(input.cms).toString("base64");
568
+ }
569
+ const fileName = input.fileName?.trim() ?? "";
570
+ if (!fileName || Array.from(fileName).length > PDF_MAX_FILE_NAME) fail2(`fileName \u0437\u0430\u0430\u0432\u0430\u043B, \u2264 ${PDF_MAX_FILE_NAME} \u0442\u044D\u043C\u0434\u044D\u0433\u0442`);
571
+ const sessionID = input.sessionID?.trim() ?? "";
572
+ const medium = input.medium ?? (sessionID ? "phone" : "token");
573
+ if (medium !== "phone" && medium !== "token") fail2("medium: phone | token");
574
+ if (medium === "phone" && !sessionID) fail2("phone-\u0434 sessionID \u0437\u0430\u0430\u0432\u0430\u043B");
575
+ const priorCms = (input.priorCmsSha256 ?? []).map((h, i) => hex(`priorCmsSha256[${i}]`, h));
576
+ const priorRev = (input.priorRevisionSha256 ?? []).map((h, i) => hex(`priorRevisionSha256[${i}]`, h));
577
+ if (priorCms.length + priorRev.length !== input.revIndex - 1) fail2("priorCmsSha256 + priorRevisionSha256-\u0438\u0439\u043D \u0442\u043E\u043E = revIndex \u2212 1");
578
+ if (input.revIndex === 1 && !input.pageSha256) fail2("revIndex 1-\u0434 pageSha256 \u0437\u0430\u0430\u0432\u0430\u043B");
579
+ const body = { docID: input.docID, revIndex: input.revIndex, medium };
580
+ if (sessionID) body.sessionID = sessionID;
581
+ body.cms = cmsB64;
582
+ body.fileName = fileName;
583
+ body.inSha256 = hex("inSha256", input.inSha256);
584
+ if (input.pageSha256) body.pageSha256 = hex("pageSha256", input.pageSha256);
585
+ body.sigRevSha256 = hex("sigRevSha256", input.sigRevSha256);
586
+ body.outSha256 = hex("outSha256", input.outSha256);
587
+ if (priorCms.length) body.priorCmsSha256 = priorCms;
588
+ if (priorRev.length) body.priorRevisionSha256 = priorRev;
589
+ if (input.onBehalfOf?.trim()) body.onBehalfOf = input.onBehalfOf.trim();
590
+ if (input.signerEtsi?.trim()) body.signerEtsi = input.signerEtsi.trim();
591
+ for (const k of Object.keys(body)) if (!PDF_SIGNATURE_REGISTER_FIELDS.includes(k)) fail2(`\u0434\u043E\u0442\u043E\u043E\u0434: \u04AF\u043B \u043C\u044D\u0434\u044D\u0433\u0434\u044D\u0445 \u0442\u0430\u043B\u0431\u0430\u0440 ${k}`);
592
+ return body;
593
+ }
594
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
595
+ var PDF_DISPLAY_TEXT = "\u0411\u0430\u0440\u0438\u043C\u0442\u0430\u0434 \u0433\u0430\u0440\u044B\u043D \u04AF\u0441\u044D\u0433 \u0437\u0443\u0440\u0430\u0445: ";
596
+ var PDF_MAX_FILE_NAME = 120;
597
+ function dtbsBase64(v) {
598
+ const raw = typeof v === "string" ? Buffer.from(v, "base64") : Buffer.from(v);
599
+ if (raw.length !== 32 || typeof v === "string" && raw.toString("base64") !== v) {
600
+ throw new ValidationError("pdfDigest: dtbs \u043D\u044C 32 \u0431\u0430\u0439\u0442 (SHA-256) \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439");
601
+ }
602
+ return raw.toString("base64");
603
+ }
523
604
  function rec2(v) {
524
605
  return v && typeof v === "object" ? v : {};
525
606
  }
@@ -604,6 +685,77 @@ var SignApi = class {
604
685
  const { body, sign } = this.body(digestB64, interactions, opts, "rsassa-pss");
605
686
  return this.notification(routePath(RP_ROUTES.signNotificationDocument, documentNumber), body, sign);
606
687
  }
688
+ /**
689
+ * Клиент талын PAdES (`@gerege-systems/eid-mongolia-sdk/pades`): `prepare()`-ийн DTBS-д иргэнээр гарын үсэг зуруулна.
690
+ * PDF CA руу ОЧИХГҮЙ — зөвхөн 32 байт. CA-ийн өөрийн PAdES session-тэй ижил: `RAW_DIGEST_SIGNATURE`,
691
+ * `sha256WithRSAEncryption` (CMS-д PKCS#1 v1.5; legacy ECDSA иргэнд сервер `ecdsa-with-SHA256`), `displayTextAndPIN`
692
+ * «Баримтад гарын үсэг зурах: <fileName>» (60 тэмдэгтэд тайрна).
693
+ *
694
+ * Дараа нь `session.waitForResult(sessionId)` → `validator.validateSign(result, s.digest)` → `signature.value`-г
695
+ * (base64 → байт) `complete(state, signature)`-д.
696
+ */
697
+ async pdfDigest(input) {
698
+ const digest = dtbsBase64(input.dtbs);
699
+ const fileName = input.fileName?.trim() ?? "";
700
+ if (!fileName || Array.from(fileName).length > PDF_MAX_FILE_NAME) {
701
+ throw new ValidationError(`pdfDigest: fileName \u0437\u0430\u0430\u0432\u0430\u043B, \u2264 ${PDF_MAX_FILE_NAME} \u0442\u044D\u043C\u0434\u044D\u0433\u0442`);
702
+ }
703
+ const signer = input.signer;
704
+ const etsi = signer?.etsi?.trim();
705
+ const doc = signer?.documentNumber?.trim();
706
+ if (!etsi === !doc) throw new ValidationError("pdfDigest: signer \u2014 etsi \u044D\u0441\u0432\u044D\u043B documentNumber-\u0438\u0439\u043D \u044F\u0433 \u043D\u044D\u0433 \u043D\u044C");
707
+ const deviceLink2 = input.flow === "device-link";
708
+ const display = Array.from(PDF_DISPLAY_TEXT + fileName).slice(0, 60).join("");
709
+ const interactions = interactionsToBase64([{ type: "displayTextAndPIN", displayText60: display }]);
710
+ const body = {
711
+ relyingPartyUUID: this.creds.rpUUID,
712
+ relyingPartyName: this.creds.rpName,
713
+ certificateLevel: input.certificateLevel ?? this.defaults.certificateLevel,
714
+ signatureProtocol: "RAW_DIGEST_SIGNATURE",
715
+ signatureProtocolParameters: {
716
+ digest,
717
+ signatureAlgorithm: "sha256WithRSAEncryption",
718
+ signatureAlgorithmParameters: { hashAlgorithm: "SHA-256" }
719
+ },
720
+ interactions,
721
+ fileName
722
+ };
723
+ if (input.initialCallbackUrl?.trim()) body.initialCallbackUrl = input.initialCallbackUrl.trim();
724
+ if (input.onBehalfOf?.trim()) body.onBehalfOf = input.onBehalfOf.trim();
725
+ const route = deviceLink2 ? etsi ? RP_ROUTES.signDeviceLinkEtsi : RP_ROUTES.signDeviceLinkDocument : etsi ? RP_ROUTES.signNotificationEtsi : RP_ROUTES.signNotificationDocument;
726
+ const r = rec2(await this.http.post(routePath(route, etsi ?? doc), body));
727
+ const receivedAt = Date.now();
728
+ const vc = typeof r.vc === "object" && r.vc !== null ? rec2(r.vc).value : r.vc;
729
+ let dl = null;
730
+ if (deviceLink2) {
731
+ if (!r.sessionToken || !r.sessionSecret || !r.deviceLinkBase) {
732
+ throw new ValidationError("pdfDigest: device-link \u0445\u0430\u0440\u0438\u0443\u043D\u0434 sessionToken/sessionSecret/deviceLinkBase \u0430\u043B\u0433\u0430");
733
+ }
734
+ dl = { sessionToken: String(r.sessionToken), sessionSecret: String(r.sessionSecret), deviceLinkBase: String(r.deviceLinkBase) };
735
+ }
736
+ return { sessionId: String(r.sessionID), vc: vc != null ? String(vc) : null, digest, interactions, deviceLink: dl, receivedAt };
737
+ }
738
+ /**
739
+ * `POST /pdf/signatures` — `pades.complete()`-ийн дараа CMS-ийг CA-д бүртгэнэ (`/verify/{docID}`, иргэний түүх). PDF
740
+ * илгээгдэхгүй. SIGN эрх. CA CMS-ийг шалгана: 04-ийн SIGN cert, session digest = SHA-256(signedAttrs), claimedRole ↔
741
+ * төлөөлөл. Алдаа: 403 `REPRESENTATION_REVOKED`, 409 `CLAIMED_ROLE_MISMATCH` / `REGISTRY_CONFLICT` /
742
+ * `DOC_ID_CONFLICT` / `PDF_FOREIGN_SIGNATURE` (`ApiError.code`). Бүртгэлгүй файл ч Adobe/EU DSS-д хүчинтэй — зөвхөн
743
+ * `/verify` «бүртгэлгүй» гэнэ.
744
+ */
745
+ async registerSignature(input) {
746
+ return this.registerBody(buildRegisterBody(input));
747
+ }
748
+ async registerBody(body) {
749
+ const raw = await this.http.post(routePath(RP_ROUTES.pdfSignatures), body);
750
+ const r = rec2(raw);
751
+ return {
752
+ docID: String(r.docID ?? body.docID),
753
+ revIndex: Number(r.revIndex ?? 0),
754
+ registeredAt: typeof r.registeredAt === "string" ? r.registeredAt : "",
755
+ verifyUrl: String(r.verifyUrl ?? ""),
756
+ raw
757
+ };
758
+ }
607
759
  /** Текстийн SHA-256 digest-ийг дотроо тооцоод push sign (богино текст баримтад тохиромжтой). */
608
760
  async textByEtsi(id, text, interactions, opts) {
609
761
  return this.digestByEtsi(id, sha256Base64(text), interactions, opts);
@@ -719,7 +871,6 @@ function parseSessionResult(raw) {
719
871
  signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
720
872
  interactionTypeUsed: str(r.interactionTypeUsed),
721
873
  onBehalfOf: parseOrg(r.onBehalfOf),
722
- pdf: parsePdf(r.pdf),
723
874
  person: parsePerson(r.person),
724
875
  tokenCertificate: parseTokenCert(r.tokenCertificate),
725
876
  raw
@@ -757,28 +908,6 @@ function personMatchesCertificate(person, subject) {
757
908
  const serial = typeof subject === "string" ? subject : subject?.serialNumber;
758
909
  return !!person?.civilId && !!serial && serial === `PNOMN-${person.civilId}`;
759
910
  }
760
- function parsePdf(raw) {
761
- const p = asRecord(raw);
762
- const docID = str(p.docID);
763
- if (!docID) return null;
764
- const out = { docID, documentStatus: String(p.documentStatus ?? "") };
765
- if (p.errorCode != null) out.errorCode = String(p.errorCode);
766
- if (p.outSha256 != null) out.outSha256 = String(p.outSha256);
767
- if (p.size != null) out.size = Number(p.size);
768
- if (p.expiresAt != null) out.expiresAt = String(p.expiresAt);
769
- if (p.signatureLevel != null) out.signatureLevel = String(p.signatureLevel);
770
- const sg = asRecord(p.signer);
771
- if (sg.etsi != null) {
772
- out.signer = { etsi: String(sg.etsi), certSerial: String(sg.certSerial ?? "") };
773
- if (sg.onBehalfOf) out.signer.onBehalfOf = String(sg.onBehalfOf);
774
- if (sg.claimedRole) out.signer.claimedRole = String(sg.claimedRole);
775
- }
776
- const v = asRecord(p.validation);
777
- if (v.indication != null) {
778
- out.validation = { indication: String(v.indication), ltv: v.ltv === true, signatures: Number(v.signatures ?? 0) };
779
- }
780
- return out;
781
- }
782
911
  function parseSigParams(raw) {
783
912
  const p = asRecord(raw);
784
913
  const hash = str(p.hashAlgorithm);
@@ -837,160 +966,6 @@ var SessionApi = class {
837
966
  }
838
967
  };
839
968
 
840
- // src/pdf.ts
841
- var import_node_crypto2 = require("crypto");
842
- var DEFAULT_PDF_MAX_BYTES = 64 << 20;
843
- var DEFAULT_PDF_TIMEOUT_MS = 8e4;
844
- function rec3(v) {
845
- return v && typeof v === "object" ? v : {};
846
- }
847
- var PdfApi = class {
848
- constructor(http, creds, defaults) {
849
- this.http = http;
850
- this.creds = creds;
851
- this.defaults = defaults;
852
- }
853
- http;
854
- creds;
855
- defaults;
856
- /**
857
- * POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. Түр алдаа: `ApiError` 429
858
- * PDF_PREPARE_BUSY, 503 LTV_UNAVAILABLE / TSA_UNAVAILABLE — `retryAfterMs`-ийн дараа дахин (`retryable`).
859
- */
860
- async prepare(input) {
861
- const request = {
862
- relyingPartyUUID: this.creds.rpUUID,
863
- relyingPartyName: this.creds.rpName,
864
- certificateLevel: input.certificateLevel ?? this.defaults.certificateLevel,
865
- signer: input.signer,
866
- flow: input.flow ?? "notification",
867
- fileName: input.fileName
868
- };
869
- if (input.initialCallbackUrl?.trim()) request.initialCallbackUrl = input.initialCallbackUrl.trim();
870
- if (input.docID?.trim()) request.docID = input.docID.trim();
871
- if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
872
- const form = new FormData();
873
- form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
874
- form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
875
- const r = rec3(await this.http.postMultipart(routePath(RP_ROUTES.pdfPrepare), form, this.timeout));
876
- const receivedAt = Date.now();
877
- const vc = rec3(r.vc).value;
878
- const dl = rec3(r.deviceLink);
879
- const digest = typeof r.digest === "string" && r.digest ? r.digest : typeof dl.digest === "string" && dl.digest ? dl.digest : void 0;
880
- return {
881
- sessionId: String(r.sessionID),
882
- docId: String(r.docID),
883
- vc: vc != null ? String(vc) : null,
884
- ...digest !== void 0 ? { digest } : {},
885
- deviceLink: dl.sessionToken ? {
886
- sessionToken: String(dl.sessionToken),
887
- sessionSecret: String(dl.sessionSecret),
888
- deviceLinkBase: String(dl.deviceLinkBase),
889
- digest: String(dl.digest),
890
- interactions: String(dl.interactions)
891
- } : null,
892
- receivedAt
893
- };
894
- }
895
- /**
896
- * GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
897
- * session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`. Баримт
898
- * бэлэн болоогүй бол `ApiError` 503 `.code` DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE — `retryAfterMs`-ийн
899
- * дараа дахин татна (`retryable`).
900
- */
901
- document(sessionId) {
902
- return this.http.getBytes(routePath(RP_ROUTES.pdfDocument, sessionId), this.timeout, this.maxBytes);
903
- }
904
- get timeout() {
905
- return this.defaults.timeoutMs ?? DEFAULT_PDF_TIMEOUT_MS;
906
- }
907
- get maxBytes() {
908
- return this.defaults.maxBytes ?? DEFAULT_PDF_MAX_BYTES;
909
- }
910
- /**
911
- * USB токены PAdES (CA docs/PADES_SIGNING.md §16.5, TOKEN_CERT.md) — session / утас оролцохгүй. POST /pdf/token/prepare
912
- * (multipart: `request` JSON + `pdf`), SIGN эрх. Дараа нь токеноор `hashToSign`-ийг CKM_ECDSA (prehashed, P-256)-аар
913
- * зурж {@link tokenFinalize}. Түр алдаа: `ApiError` 429 PDF_PREPARE_BUSY / RATE_LIMITED, 503 TSA_UNAVAILABLE /
914
- * LTV_UNAVAILABLE / STATE_UNAVAILABLE (`retryable`). Гэрчилгээ токенд холбогдоогүй / цуцлагдсан → 409
915
- * TOKEN_CERT_INVALID. Зам нь CA-д бий ч `/.well-known/eid`-д хараахан алга (гэрээний тест WARN).
916
- */
917
- async tokenPrepare(input) {
918
- if (!input.pdf?.length || !input.certificate?.length || !input.signerEtsi?.trim() || !input.fileName?.trim()) {
919
- throw new Error("eid: pdf token: pdf, certificate, signerEtsi, fileName \u0448\u0430\u0430\u0440\u0434\u043B\u0430\u0433\u0430\u0442\u0430\u0439");
920
- }
921
- const request = {
922
- relyingPartyUUID: this.creds.rpUUID,
923
- relyingPartyName: this.creds.rpName,
924
- signer: { etsi: input.signerEtsi.trim() },
925
- certificate: Buffer.from(input.certificate).toString("base64"),
926
- fileName: input.fileName
927
- };
928
- if (input.docID?.trim()) request.docID = input.docID.trim();
929
- if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
930
- const form = new FormData();
931
- form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
932
- form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
933
- const raw = await this.http.postMultipart(routePath(RP_ROUTES_UNDISCOVERED.pdfTokenPrepare), form, this.timeout);
934
- const r = rec3(raw);
935
- const hashToSign = new Uint8Array(Buffer.from(String(r.hashToSign ?? ""), "base64"));
936
- if (!r.signingID || hashToSign.length !== 32) {
937
- throw new ValidationError("pdf token prepare: signingID / hashToSign (32 \u0431\u0430\u0439\u0442) \u0431\u0443\u0440\u0443\u0443");
938
- }
939
- return {
940
- signingId: String(r.signingID),
941
- docId: String(r.docID ?? ""),
942
- hashToSign,
943
- hashAlgorithm: String(r.hashAlgorithm ?? ""),
944
- signatureAlgorithm: String(r.signatureAlgorithm ?? ""),
945
- signatureFormat: String(r.signatureFormat ?? ""),
946
- expiresAt: String(r.expiresAt ?? ""),
947
- raw
948
- };
949
- }
950
- /**
951
- * POST /pdf/token/{signingID}/finalize `{signature: base64 түүхий r||s}` — `signature` 64 байт (P-256 r||s, DER БИШ).
952
- * Амжвал READY баримтын блок (`outSha256` — {@link tokenDocument}-оор татсан файлтай тулга). READY-ээс хойш идэмпотент.
953
- * Алдаа: 400 SIGNATURE_INVALID (3 удаа → FAILED), 409 DOCUMENT_FAILED, 410 DOCUMENT_EXPIRED, 503 DOCUMENT_PENDING /
954
- * TSA_UNAVAILABLE / LTV_UNAVAILABLE — ижил гарын үсгийг `retryAfterMs`-ийн дараа дахин илгээнэ.
955
- */
956
- async tokenFinalize(signingId, signature) {
957
- if (signature?.length !== 64) throw new Error("eid: pdf token: signature \u043D\u044C \u0442\u04AF\u04AF\u0445\u0438\u0439 r||s 64 \u0431\u0430\u0439\u0442 \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439 (P-256, DER \u0431\u0438\u0448)");
958
- const r = await this.http.post(
959
- routePath(RP_ROUTES_UNDISCOVERED.pdfTokenFinalize, signingId),
960
- { signature: Buffer.from(signature).toString("base64") },
961
- this.timeout
962
- );
963
- return r;
964
- }
965
- /**
966
- * GET /pdf/token/{signingID}/document — гарын үсэгтэй PDF (зөвхөн үүсгэсэн RP, expiresAt хүртэл), `pdfMaxBytes`
967
- * хүртэл. SHA-256-ийг {@link tokenFinalize}-ийн `outSha256`-тай заавал тулга.
968
- */
969
- tokenDocument(signingId) {
970
- return this.http.getBytes(routePath(RP_ROUTES_UNDISCOVERED.pdfTokenDocument, signingId), this.timeout, this.maxBytes);
971
- }
972
- /**
973
- * {@link document} + тулгалт: татсан байтын SHA-256 нь session poll-ийн `pdf.outSha256`-тай тэнцүү байх ёстой.
974
- * `expected` — poll-ийн `result.pdf` блок (`documentStatus` READY + `outSha256` заавал) эсвэл SHA-256 hex.
975
- *
976
- * @throws {ValidationError} блок READY биш / `outSha256` алга / hash таарахгүй (файлд бүү итгэ).
977
- */
978
- async documentVerified(sessionId, expected) {
979
- let want;
980
- if (typeof expected === "string") {
981
- want = expected;
982
- } else {
983
- if (expected.documentStatus !== "READY") throw new ValidationError(`pdf.documentStatus ${expected.documentStatus} \u2260 READY`);
984
- want = expected.outSha256;
985
- }
986
- if (!want || !/^[0-9a-fA-F]{64}$/.test(want)) throw new ValidationError("pdf.outSha256 \u0430\u043B\u0433\u0430 \u044D\u0441\u0432\u044D\u043B SHA-256 hex \u0431\u0438\u0448");
987
- const bytes = await this.document(sessionId);
988
- const got = (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex");
989
- if (got !== want.toLowerCase()) throw new ValidationError("\u0422\u0430\u0442\u0441\u0430\u043D PDF-\u0438\u0439\u043D SHA-256 \u043D\u044C pdf.outSha256-\u0442\u0430\u0439 \u0442\u0430\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439");
990
- return bytes;
991
- }
992
- };
993
-
994
969
  // src/organization.ts
995
970
  var OrganizationApi = class {
996
971
  constructor(http) {
@@ -1006,7 +981,7 @@ var OrganizationApi = class {
1006
981
  };
1007
982
 
1008
983
  // src/person.ts
1009
- function rec4(v) {
984
+ function rec3(v) {
1010
985
  return v && typeof v === "object" ? v : {};
1011
986
  }
1012
987
  var PersonApi = class {
@@ -1016,7 +991,7 @@ var PersonApi = class {
1016
991
  http;
1017
992
  async get(path) {
1018
993
  const raw = await this.http.get(path);
1019
- return { ...rec4(raw), raw };
994
+ return { ...rec3(raw), raw };
1020
995
  }
1021
996
  /** Иргэний бүх гэрчилгээ + статусын тоолол. */
1022
997
  certificates(personEtsi) {
@@ -1046,10 +1021,10 @@ var PersonApi = class {
1046
1021
  };
1047
1022
 
1048
1023
  // src/token.ts
1049
- var import_node_crypto4 = require("crypto");
1024
+ var import_node_crypto3 = require("crypto");
1050
1025
 
1051
1026
  // src/subject.ts
1052
- var import_node_crypto3 = require("crypto");
1027
+ var import_node_crypto2 = require("crypto");
1053
1028
  var OIDS = {
1054
1029
  "2.5.4.42": "givenName",
1055
1030
  "2.5.4.4": "surname",
@@ -1114,7 +1089,7 @@ function stringOf(buf, t) {
1114
1089
  }
1115
1090
  }
1116
1091
  function parseSubject(cert) {
1117
- const der = cert instanceof import_node_crypto3.X509Certificate ? new Uint8Array(cert.raw) : typeof cert === "string" ? new Uint8Array(Buffer.from(cert, "base64")) : cert;
1092
+ const der = cert instanceof import_node_crypto2.X509Certificate ? new Uint8Array(cert.raw) : typeof cert === "string" ? new Uint8Array(Buffer.from(cert, "base64")) : cert;
1118
1093
  const top = tlv(der, 0, der.length);
1119
1094
  if (top.tag !== 48) throw new ValidationError("subject: Certificate SEQUENCE \u0431\u0438\u0448");
1120
1095
  const tbs = tlv(der, top.start, top.end);
@@ -1187,12 +1162,12 @@ function certExtensions(der) {
1187
1162
  }
1188
1163
 
1189
1164
  // src/token.ts
1190
- function rec5(v) {
1165
+ function rec4(v) {
1191
1166
  return v && typeof v === "object" ? v : {};
1192
1167
  }
1193
1168
  function hexSha256Equal(hex, data) {
1194
1169
  if (typeof hex !== "string" || !/^[0-9a-fA-F]{64}$/.test(hex)) return false;
1195
- return (0, import_node_crypto4.timingSafeEqual)((0, import_node_crypto4.createHash)("sha256").update(data).digest(), Buffer.from(hex, "hex"));
1170
+ return (0, import_node_crypto3.timingSafeEqual)((0, import_node_crypto3.createHash)("sha256").update(data).digest(), Buffer.from(hex, "hex"));
1196
1171
  }
1197
1172
  var TOKEN_RESULT_TIMEOUT_MS = 3e4;
1198
1173
  var TokenApi = class {
@@ -1221,11 +1196,11 @@ var TokenApi = class {
1221
1196
  flow: "notification"
1222
1197
  };
1223
1198
  if (input.tokenLabel) body.tokenLabel = input.tokenLabel;
1224
- const r = rec5(await this.http.post(routePath(RP_ROUTES.tokenCertificate), body));
1199
+ const r = rec4(await this.http.post(routePath(RP_ROUTES.tokenCertificate), body));
1225
1200
  if (!hexSha256Equal(r.csrAuthSha256, input.csrAuth) || !hexSha256Equal(r.csrSignSha256, input.csrSign)) {
1226
1201
  throw new ValidationError("token: CA-\u0438\u0439\u043D csrAuthSha256/csrSignSha256 \u0438\u043B\u0433\u044D\u044D\u0441\u044D\u043D CSR-\u0442\u044D\u0439 \u0442\u0430\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439");
1227
1202
  }
1228
- const vc = rec5(r.vc).value ?? (typeof r.vc === "string" ? r.vc : void 0);
1203
+ const vc = rec4(r.vc).value ?? (typeof r.vc === "string" ? r.vc : void 0);
1229
1204
  return {
1230
1205
  sessionId: String(r.sessionID ?? ""),
1231
1206
  vc: vc != null ? String(vc) : null,
@@ -1240,7 +1215,7 @@ var TokenApi = class {
1240
1215
  */
1241
1216
  async certificateResult(sessionId) {
1242
1217
  const raw = await this.http.get(routePath(RP_ROUTES.tokenCertificateResult, sessionId), TOKEN_RESULT_TIMEOUT_MS);
1243
- const r = rec5(raw);
1218
+ const r = rec4(raw);
1244
1219
  const item = (v) => v && typeof v === "object" ? v : null;
1245
1220
  return {
1246
1221
  status: String(r.status ?? ""),
@@ -1256,7 +1231,7 @@ var TokenApi = class {
1256
1231
  }
1257
1232
  /** POST /token/auth/challenge — энэ RP-д холбогдсон нэг удаагийн challenge (2 мин). */
1258
1233
  async authChallenge() {
1259
- const r = rec5(
1234
+ const r = rec4(
1260
1235
  await this.http.post(routePath(RP_ROUTES_UNDISCOVERED.tokenAuthChallenge), {
1261
1236
  relyingPartyUUID: this.creds.rpUUID,
1262
1237
  relyingPartyName: this.creds.rpName
@@ -1277,7 +1252,7 @@ var TokenApi = class {
1277
1252
  async authVerify(challengeId, certDer, signature) {
1278
1253
  let serial;
1279
1254
  try {
1280
- serial = parseSubject(new import_node_crypto4.X509Certificate(certDer)).serialNumber;
1255
+ serial = parseSubject(new import_node_crypto3.X509Certificate(certDer)).serialNumber;
1281
1256
  } catch {
1282
1257
  throw new Error("token auth: \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0437\u0430\u0434\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439");
1283
1258
  }
@@ -1287,12 +1262,12 @@ var TokenApi = class {
1287
1262
  certificate: Buffer.from(certDer).toString("base64"),
1288
1263
  signature: Buffer.from(signature).toString("base64")
1289
1264
  });
1290
- const r = rec5(raw);
1291
- if (rec5(r.result).endResult !== "OK") throw new ValidationError("token auth: endResult OK \u0431\u0438\u0448");
1292
- const id = rec5(r.identity);
1265
+ const r = rec4(raw);
1266
+ if (rec4(r.result).endResult !== "OK") throw new ValidationError("token auth: endResult OK \u0431\u0438\u0448");
1267
+ const id = rec4(r.identity);
1293
1268
  if (!id.etsi || id.etsi !== serial) throw new ValidationError("token auth: identity.etsi \u043D\u044C \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0438\u0439 SERIALNUMBER \u0431\u0438\u0448");
1294
- const org = rec5(r.organization);
1295
- const cert = rec5(r.cert);
1269
+ const org = rec4(r.organization);
1270
+ const cert = rec4(r.cert);
1296
1271
  return {
1297
1272
  identity: { etsi: String(id.etsi), givenName: String(id.givenName ?? ""), surname: String(id.surname ?? "") },
1298
1273
  serialHex: String(cert.serialHex ?? ""),
@@ -1308,7 +1283,7 @@ function csrSpki(csr) {
1308
1283
  const version = tlv(csr, info.start, info.end);
1309
1284
  const subject = tlv(csr, version.end, info.end);
1310
1285
  const spki = tlv(csr, subject.end, info.end);
1311
- return (0, import_node_crypto4.createPublicKey)({ key: Buffer.from(csr.subarray(subject.end, spki.end)), format: "der", type: "spki" }).export({
1286
+ return (0, import_node_crypto3.createPublicKey)({ key: Buffer.from(csr.subarray(subject.end, spki.end)), format: "der", type: "spki" }).export({
1312
1287
  format: "der",
1313
1288
  type: "spki"
1314
1289
  });
@@ -1327,7 +1302,7 @@ function verifyTokenCertificates(result, csrAuth, csrSign) {
1327
1302
  }
1328
1303
  let got;
1329
1304
  try {
1330
- got = new import_node_crypto4.X509Certificate(Buffer.from(item.certificate, "base64")).publicKey.export({ format: "der", type: "spki" });
1305
+ got = new import_node_crypto3.X509Certificate(Buffer.from(item.certificate, "base64")).publicKey.export({ format: "der", type: "spki" });
1331
1306
  } catch {
1332
1307
  throw new ValidationError(`token: ${name} \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0437\u0430\u0434\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439`);
1333
1308
  }
@@ -1340,13 +1315,13 @@ function tokenAuthDigest(rpUUID, challengeB64) {
1340
1315
  }
1341
1316
  const ch = Buffer.from(challengeB64, "base64");
1342
1317
  if (ch.length !== 32 || ch.toString("base64") !== challengeB64) throw new Error("token auth: challenge 32 \u0431\u0430\u0439\u0442 base64 \u0431\u0438\u0448");
1343
- return (0, import_node_crypto4.createHash)("sha256").update("eID-TOKEN-AUTH", "ascii").update(rpUUID, "ascii").update(ch).digest();
1318
+ return (0, import_node_crypto3.createHash)("sha256").update("eID-TOKEN-AUTH", "ascii").update(rpUUID, "ascii").update(ch).digest();
1344
1319
  }
1345
1320
  function tokenLimitOf(err) {
1346
1321
  if (!(err instanceof ApiError) || err.code !== "TOKEN_LIMIT_REACHED") return void 0;
1347
1322
  let b;
1348
1323
  try {
1349
- b = rec5(JSON.parse(err.body));
1324
+ b = rec4(JSON.parse(err.body));
1350
1325
  } catch {
1351
1326
  return void 0;
1352
1327
  }
@@ -1359,7 +1334,7 @@ function tokenLimitOf(err) {
1359
1334
  }
1360
1335
 
1361
1336
  // src/validator.ts
1362
- var import_node_crypto5 = require("crypto");
1337
+ var import_node_crypto4 = require("crypto");
1363
1338
  var CITIZEN_ISSUING_CA_SKI = "b8a37f2c64ad677912db7d9e34f246358a2fb909";
1364
1339
  var ResponseValidator = class {
1365
1340
  anchors;
@@ -1373,8 +1348,8 @@ var ResponseValidator = class {
1373
1348
  checkKeyUsage;
1374
1349
  schemeName;
1375
1350
  constructor(trust = {}) {
1376
- this.anchors = (trust.trustAnchorsPem ?? []).map((p) => new import_node_crypto5.X509Certificate(p));
1377
- this.intermediates = (trust.intermediatesPem ?? []).map((p) => new import_node_crypto5.X509Certificate(p));
1351
+ this.anchors = (trust.trustAnchorsPem ?? []).map((p) => new import_node_crypto4.X509Certificate(p));
1352
+ this.intermediates = (trust.intermediatesPem ?? []).map((p) => new import_node_crypto4.X509Certificate(p));
1378
1353
  this.requiredLevel = trust.requiredLevel ?? "QUALIFIED";
1379
1354
  this.skew = trust.clockSkewMs ?? 0;
1380
1355
  this.allowUntrusted = trust.allowUntrusted ?? false;
@@ -1555,7 +1530,7 @@ var ResponseValidator = class {
1555
1530
  leafOf(result, protocol) {
1556
1531
  if (!result.certificateDerB64) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0430\u043B\u0433\u0430");
1557
1532
  if (!result.documentNumber) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 documentNumber \u0430\u043B\u0433\u0430");
1558
- const leaf = new import_node_crypto5.X509Certificate(Buffer.from(result.certificateDerB64, "base64"));
1533
+ const leaf = new import_node_crypto4.X509Certificate(Buffer.from(result.certificateDerB64, "base64"));
1559
1534
  this.checkValidity(leaf);
1560
1535
  this.checkLevel(String(result.certificateLevel ?? ""));
1561
1536
  this.checkPurpose(leaf, protocol);
@@ -1689,16 +1664,16 @@ function signedCallbackUrl(fromResponse, rpCallback, flowType) {
1689
1664
  return fromResponse;
1690
1665
  }
1691
1666
  function userChallengeOf(verifier) {
1692
- return (0, import_node_crypto5.createHash)("sha256").update(verifier, "utf8").digest("base64url");
1667
+ return (0, import_node_crypto4.createHash)("sha256").update(verifier, "utf8").digest("base64url");
1693
1668
  }
1694
1669
  function verifyPayloadSignature(payload, sigB64, key, alg, hash) {
1695
1670
  const sig = Buffer.from(sigB64, "base64");
1696
1671
  const h = nodeHash(hash);
1697
1672
  if (alg === "rsassa-pss") {
1698
- return (0, import_node_crypto5.verify)(h, payload, { key, padding: import_node_crypto5.constants.RSA_PKCS1_PSS_PADDING, saltLength: hashLen(hash) }, sig);
1673
+ return (0, import_node_crypto4.verify)(h, payload, { key, padding: import_node_crypto4.constants.RSA_PKCS1_PSS_PADDING, saltLength: hashLen(hash) }, sig);
1699
1674
  }
1700
- if (alg === "ecdsa-with-SHA256") return (0, import_node_crypto5.verify)(h, payload, { key, dsaEncoding: "der" }, sig);
1701
- return (0, import_node_crypto5.verify)(h, payload, { key, padding: import_node_crypto5.constants.RSA_PKCS1_PADDING }, sig);
1675
+ if (alg === "ecdsa-with-SHA256") return (0, import_node_crypto4.verify)(h, payload, { key, dsaEncoding: "der" }, sig);
1676
+ return (0, import_node_crypto4.verify)(h, payload, { key, padding: import_node_crypto4.constants.RSA_PKCS1_PADDING }, sig);
1702
1677
  }
1703
1678
  function verifyPrehashedSignature(digest, sigB64, key, alg, hash) {
1704
1679
  const sig = Buffer.from(sigB64, "base64");
@@ -1707,7 +1682,7 @@ function verifyPrehashedSignature(digest, sigB64, key, alg, hash) {
1707
1682
  if (!modBits || sig.length !== Math.ceil(modBits / 8)) return false;
1708
1683
  let em;
1709
1684
  try {
1710
- em = (0, import_node_crypto5.publicDecrypt)({ key, padding: import_node_crypto5.constants.RSA_NO_PADDING }, sig);
1685
+ em = (0, import_node_crypto4.publicDecrypt)({ key, padding: import_node_crypto4.constants.RSA_NO_PADDING }, sig);
1711
1686
  } catch {
1712
1687
  return false;
1713
1688
  }
@@ -1791,7 +1766,7 @@ function mgf1(hash, seed, length) {
1791
1766
  for (let i = 0, n = 0; n < length; i++, n += hashLen(hash)) {
1792
1767
  const c = Buffer.alloc(4);
1793
1768
  c.writeUInt32BE(i, 0);
1794
- out.push((0, import_node_crypto5.createHash)(nodeHash(hash)).update(seed).update(c).digest());
1769
+ out.push((0, import_node_crypto4.createHash)(nodeHash(hash)).update(seed).update(c).digest());
1795
1770
  }
1796
1771
  return Buffer.concat(out).subarray(0, length);
1797
1772
  }
@@ -1815,7 +1790,7 @@ function emsaPssVerify(mHash, em, emBits, hash) {
1815
1790
  const psLen = emLen - hLen - sLen - 2;
1816
1791
  if (!db.subarray(0, psLen).every((b) => b === 0) || db[psLen] !== 1) return false;
1817
1792
  const salt = db.subarray(psLen + 1);
1818
- const h2 = (0, import_node_crypto5.createHash)(nodeHash(hash)).update(Buffer.alloc(8)).update(mHash).update(salt).digest();
1793
+ const h2 = (0, import_node_crypto4.createHash)(nodeHash(hash)).update(Buffer.alloc(8)).update(mHash).update(salt).digest();
1819
1794
  return h.equals(h2);
1820
1795
  }
1821
1796
  function keyUsageBits(cert) {
@@ -1949,7 +1924,6 @@ function resolveBaseUrl(baseUrl) {
1949
1924
  var EidClient = class {
1950
1925
  auth;
1951
1926
  sign;
1952
- pdf;
1953
1927
  organization;
1954
1928
  person;
1955
1929
  token;
@@ -1971,11 +1945,6 @@ var EidClient = class {
1971
1945
  const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
1972
1946
  this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
1973
1947
  this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level, sessionTtlMs: cfg.signSessionTtlMs });
1974
- this.pdf = new PdfApi(http, cfg.credentials, {
1975
- certificateLevel: level,
1976
- timeoutMs: cfg.pdfTimeoutMs ?? DEFAULT_PDF_TIMEOUT_MS,
1977
- maxBytes: cfg.pdfMaxBytes && cfg.pdfMaxBytes > 0 ? cfg.pdfMaxBytes : DEFAULT_PDF_MAX_BYTES
1978
- });
1979
1948
  this.organization = new OrganizationApi(http);
1980
1949
  this.person = new PersonApi(http);
1981
1950
  this.token = new TokenApi(http, cfg.credentials);
@@ -1983,6 +1952,14 @@ var EidClient = class {
1983
1952
  this.validator = new ResponseValidator(cfg.trust ?? {});
1984
1953
  this.rpName = cfg.credentials.rpName;
1985
1954
  }
1955
+ /** {@link SignApi.pdfDigest} — клиент талын PAdES-ийн DTBS-д гарын үсгийн session (PDF CA руу очихгүй). */
1956
+ signPdfDigest(input) {
1957
+ return this.sign.pdfDigest(input);
1958
+ }
1959
+ /** {@link SignApi.registerSignature} — `pades.complete()`-ийн CMS-ийг CA-д бүртгэнэ (`POST /pdf/signatures`). */
1960
+ registerSignature(input) {
1961
+ return this.sign.registerSignature(input);
1962
+ }
1986
1963
  };
1987
1964
 
1988
1965
  // src/devicelink.ts
@@ -2028,7 +2005,7 @@ function openOrShowQR(session, opts) {
2028
2005
  }
2029
2006
 
2030
2007
  // src/devicelinkv3.ts
2031
- var import_node_crypto6 = require("crypto");
2008
+ var import_node_crypto5 = require("crypto");
2032
2009
  var TYPES = ["QR", "Web2App", "App2App"];
2033
2010
  var PROTOCOL = { auth: "ACSP_V2", sign: "RAW_DIGEST_SIGNATURE", cert: "" };
2034
2011
  var TOKEN_RE = /^[A-Za-z0-9_-]+$/;
@@ -2085,7 +2062,7 @@ function buildDeviceLink(p) {
2085
2062
  callback,
2086
2063
  unprotected
2087
2064
  ].join("|");
2088
- const authCode = (0, import_node_crypto6.createHmac)("sha256", Buffer.from(p.sessionSecret, "base64")).update(payload, "utf8").digest("base64url");
2065
+ const authCode = (0, import_node_crypto5.createHmac)("sha256", Buffer.from(p.sessionSecret, "base64")).update(payload, "utf8").digest("base64url");
2089
2066
  return `${unprotected}&authCode=${authCode}`;
2090
2067
  }
2091
2068
  function deviceLinkInput(s, deviceLinkType, lang) {
@@ -2129,7 +2106,7 @@ function qrDeviceLinkTicker(input, onLink, opts) {
2129
2106
  }
2130
2107
 
2131
2108
  // src/login.ts
2132
- var import_node_crypto7 = require("crypto");
2109
+ var import_node_crypto6 = require("crypto");
2133
2110
  function memoryLoginStore() {
2134
2111
  const m = /* @__PURE__ */ new Map();
2135
2112
  return {
@@ -2166,7 +2143,7 @@ function forwardedClientIp(headers, trustProxy) {
2166
2143
  return header(headers, "x-real-ip").trim() || void 0;
2167
2144
  }
2168
2145
  var ID_RE = /^(\p{L}{2}[0-9]{8}|[0-9]{8,14}|PNOMN-[0-9]{8,14})$/u;
2169
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
2146
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
2170
2147
  var B64URL_RE = /^[A-Za-z0-9_-]{43}$/;
2171
2148
  var IOS_BROWSERS = [
2172
2149
  [/CriOS/i, "googlechromes"],
@@ -2210,11 +2187,11 @@ function readCookie(h, name) {
2210
2187
  }
2211
2188
  return "";
2212
2189
  }
2213
- var sha256hex = (s) => (0, import_node_crypto7.createHash)("sha256").update(s).digest("hex");
2190
+ var sha256hex = (s) => (0, import_node_crypto6.createHash)("sha256").update(s).digest("hex");
2214
2191
  function safeEqualHex(a, b) {
2215
2192
  const x = Buffer.from(a, "hex");
2216
2193
  const y = Buffer.from(b, "hex");
2217
- return x.length === y.length && x.length > 0 && (0, import_node_crypto7.timingSafeEqual)(x, y);
2194
+ return x.length === y.length && x.length > 0 && (0, import_node_crypto6.timingSafeEqual)(x, y);
2218
2195
  }
2219
2196
  function toIdentity(sessionId, r, v, flowType) {
2220
2197
  const s = v.parsedSubject;
@@ -2297,7 +2274,7 @@ function createEidLogin(opts) {
2297
2274
  const dot = raw.indexOf(".");
2298
2275
  const sid = raw.slice(0, dot);
2299
2276
  const binding = raw.slice(dot + 1);
2300
- if (dot < 0 || !UUID_RE.test(sid) || !B64URL_RE.test(binding)) return void 0;
2277
+ if (dot < 0 || !UUID_RE2.test(sid) || !B64URL_RE.test(binding)) return void 0;
2301
2278
  const tx = await store.get(sid);
2302
2279
  if (!tx || !safeEqualHex(tx.bindingHash, sha256hex(binding))) return void 0;
2303
2280
  if (tx.expiresAt <= Date.now()) {
@@ -2354,7 +2331,7 @@ function createEidLogin(opts) {
2354
2331
  });
2355
2332
  }
2356
2333
  }
2357
- const binding = (0, import_node_crypto7.randomBytes)(32).toString("base64url");
2334
+ const binding = (0, import_node_crypto6.randomBytes)(32).toString("base64url");
2358
2335
  const full = { ...tx, bindingHash: sha256hex(binding), expiresAt: Date.now() + ttl };
2359
2336
  await store.set(full.sessionId, full, ttl);
2360
2337
  const setCookie = `${cookieName}=${full.sessionId}.${binding}; ${cookieAttrs(Math.ceil(ttl / 1e3))}`;
@@ -2523,8 +2500,6 @@ function createEidLogin(opts) {
2523
2500
  CITIZEN_ISSUING_CA_SKI,
2524
2501
  CertChoiceInvalidError,
2525
2502
  DEFAULT_BASE_URL,
2526
- DEFAULT_PDF_MAX_BYTES,
2527
- DEFAULT_PDF_TIMEOUT_MS,
2528
2503
  DEFAULT_SIGN_SESSION_TTL_MS,
2529
2504
  DeviceLinkError,
2530
2505
  EidClient,
@@ -2535,7 +2510,7 @@ function createEidLogin(opts) {
2535
2510
  NoSigningCertificateError,
2536
2511
  ON_BEHALF_ERROR_CODES,
2537
2512
  OrganizationApi,
2538
- PdfApi,
2513
+ PDF_SIGNATURE_REGISTER_FIELDS,
2539
2514
  PersonApi,
2540
2515
  RETRYABLE_ERROR_CODES,
2541
2516
  RequestInvalidError,
@@ -2549,6 +2524,7 @@ function createEidLogin(opts) {
2549
2524
  acspV2Digest,
2550
2525
  buildAcspV2Payload,
2551
2526
  buildDeviceLink,
2527
+ buildRegisterBody,
2552
2528
  createEidLogin,
2553
2529
  deviceLink,
2554
2530
  deviceLinkInput,
@@ -2564,6 +2540,7 @@ function createEidLogin(opts) {
2564
2540
  personMatchesCertificate,
2565
2541
  qrDeviceLinkTicker,
2566
2542
  randomChallenge,
2543
+ registrationFromCompleted,
2567
2544
  resolveBaseUrl,
2568
2545
  sessionExpiry,
2569
2546
  sha256Base64,