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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,8 @@
1
+ import {
2
+ deviceLink,
3
+ isMobileUserAgent,
4
+ openOrShowQR
5
+ } from "./chunk-REWNE7PO.js";
1
6
  import {
2
7
  ApiError,
3
8
  AuthenticationError,
@@ -13,12 +18,9 @@ import {
13
18
  ResponseTooLargeError,
14
19
  SessionFailedError,
15
20
  ValidationError,
16
- deviceLink,
17
- isMobileUserAgent,
18
21
  maskPath,
19
- openOrShowQR,
20
22
  parseRetryAfter
21
- } from "./chunk-DXVHOZO7.js";
23
+ } from "./chunk-TB2KQH3W.js";
22
24
 
23
25
  // src/http.ts
24
26
  var DEFAULT_TIMEOUT_MS = 15e3;
@@ -34,15 +36,6 @@ var Http = class {
34
36
  get(path, timeoutMs) {
35
37
  return this.json("GET", path, void 0, void 0, timeoutMs);
36
38
  }
37
- /** multipart/form-data POST (Content-Type-ийг boundary-тай нь fetch тавина). JSON хариу. */
38
- postMultipart(path, form, timeoutMs) {
39
- return this.json("POST", path, form, void 0, timeoutMs);
40
- }
41
- /** GET — хариуг байтаар (ж: гарын үсэгтэй PDF), `maxBytes` хүртэл; хэтэрвэл {@link ResponseTooLargeError}. */
42
- async getBytes(path, timeoutMs, maxBytes = 64 << 20) {
43
- const res = await this.send("GET", path, void 0, void 0, timeoutMs);
44
- return readLimited(res, maxBytes, `GET ${maskPath(path)}`);
45
- }
46
39
  async json(method, path, body, contentType, timeoutMs) {
47
40
  const res = await this.send(method, path, body, contentType, timeoutMs);
48
41
  const text = new TextDecoder().decode(await readLimited(res, MAX_JSON_BYTES, `${method} ${maskPath(path)}`));
@@ -147,8 +140,6 @@ var RP_ROUTES = {
147
140
  signDeviceLinkDocument: { method: "POST", path: "/signature/device-link/document/{documentNumber}" },
148
141
  certChoiceDeviceLink: { method: "POST", path: "/signature/certificate-choice/device-link/anonymous" },
149
142
  certChoiceEtsi: { method: "POST", path: "/signature/certificate-choice/notification/etsi/{semanticsIdentifier}" },
150
- pdfPrepare: { method: "POST", path: "/pdf/sign/prepare" },
151
- pdfDocument: { method: "GET", path: "/pdf/sign/{sessionID}/document" },
152
143
  orgRepresentations: { method: "GET", path: "/organization/representations/etsi/{personEtsi}" },
153
144
  session: { method: "GET", path: "/session/{sessionId}" },
154
145
  personCertificates: { method: "GET", path: "/certificates/etsi/{personEtsi}" },
@@ -157,15 +148,29 @@ var RP_ROUTES = {
157
148
  personSummary: { method: "GET", path: "/person/summary/etsi/{personEtsi}" },
158
149
  personChildren: { method: "GET", path: "/person/children/etsi/{personEtsi}" },
159
150
  tokenCertificate: { method: "POST", path: "/token/certificate" },
160
- tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" }
151
+ tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" },
152
+ pdfSignatures: { method: "POST", path: "/pdf/signatures" }
161
153
  };
162
154
  var RP_ROUTES_UNDISCOVERED = {
163
155
  tokenAuthChallenge: { method: "POST", path: "/token/auth/challenge" },
164
- tokenAuthVerify: { method: "POST", path: "/token/auth/verify" },
165
- pdfTokenPrepare: { method: "POST", path: "/pdf/token/prepare" },
166
- pdfTokenFinalize: { method: "POST", path: "/pdf/token/{signingID}/finalize" },
167
- pdfTokenDocument: { method: "GET", path: "/pdf/token/{signingID}/document" }
156
+ tokenAuthVerify: { method: "POST", path: "/token/auth/verify" }
168
157
  };
158
+ var PDF_SIGNATURE_REGISTER_FIELDS = [
159
+ "docID",
160
+ "revIndex",
161
+ "medium",
162
+ "sessionID",
163
+ "cms",
164
+ "fileName",
165
+ "inSha256",
166
+ "pageSha256",
167
+ "sigRevSha256",
168
+ "outSha256",
169
+ "priorCmsSha256",
170
+ "priorRevisionSha256",
171
+ "onBehalfOf",
172
+ "signerEtsi"
173
+ ];
169
174
  function routePath(route, ...args) {
170
175
  let i = 0;
171
176
  const out = route.path.replace(/\{[^}]*\}/g, () => {
@@ -328,6 +333,84 @@ var AuthApi = class {
328
333
  };
329
334
 
330
335
  // src/sign.ts
336
+ function registrationFromCompleted(done, extra) {
337
+ const out = {
338
+ docID: done.docId,
339
+ revIndex: done.revIndex,
340
+ cms: done.cms,
341
+ fileName: extra.fileName,
342
+ inSha256: done.inSha256,
343
+ sigRevSha256: done.sigRevSha256,
344
+ outSha256: done.outSha256
345
+ };
346
+ if (extra.medium) out.medium = extra.medium;
347
+ if (extra.sessionID) out.sessionID = extra.sessionID;
348
+ if (done.hasPage && done.pageSha256) out.pageSha256 = done.pageSha256;
349
+ if (done.priorCmsSha256.length) out.priorCmsSha256 = done.priorCmsSha256;
350
+ if (extra.priorRevisionSha256?.length) out.priorRevisionSha256 = extra.priorRevisionSha256;
351
+ if (extra.onBehalfOf) out.onBehalfOf = extra.onBehalfOf;
352
+ if (extra.signerEtsi) out.signerEtsi = extra.signerEtsi;
353
+ return out;
354
+ }
355
+ var HEX64 = /^[0-9a-f]{64}$/;
356
+ var MAX_CMS_BYTES = 28672;
357
+ function buildRegisterBody(input) {
358
+ const fail2 = (m) => {
359
+ throw new ValidationError(`registerSignature: ${m}`);
360
+ };
361
+ if (!UUID_RE.test(input.docID ?? "")) fail2("docID UUID \u0431\u0438\u0448");
362
+ if (!Number.isInteger(input.revIndex) || input.revIndex < 1 || input.revIndex > 16) fail2("revIndex 1\u201316");
363
+ const hex = (name, v) => {
364
+ const x = v?.trim().toLowerCase() ?? "";
365
+ if (!HEX64.test(x)) fail2(`${name} SHA-256 hex (64) \u0431\u0438\u0448`);
366
+ return x;
367
+ };
368
+ let cmsB64;
369
+ if (typeof input.cms === "string") {
370
+ const raw = Buffer.from(input.cms, "base64");
371
+ if (raw.length === 0 || raw.toString("base64") !== input.cms) fail2("cms \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442 base64 \u0431\u0438\u0448");
372
+ cmsB64 = input.cms;
373
+ if (raw.length > MAX_CMS_BYTES) fail2(`cms > ${MAX_CMS_BYTES} \u0431\u0430\u0439\u0442`);
374
+ } else {
375
+ if (!input.cms?.length) fail2("cms \u0445\u043E\u043E\u0441\u043E\u043D");
376
+ if (input.cms.length > MAX_CMS_BYTES) fail2(`cms > ${MAX_CMS_BYTES} \u0431\u0430\u0439\u0442`);
377
+ cmsB64 = Buffer.from(input.cms).toString("base64");
378
+ }
379
+ const fileName = input.fileName?.trim() ?? "";
380
+ 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`);
381
+ const sessionID = input.sessionID?.trim() ?? "";
382
+ const medium = input.medium ?? (sessionID ? "phone" : "token");
383
+ if (medium !== "phone" && medium !== "token") fail2("medium: phone | token");
384
+ if (medium === "phone" && !sessionID) fail2("phone-\u0434 sessionID \u0437\u0430\u0430\u0432\u0430\u043B");
385
+ const priorCms = (input.priorCmsSha256 ?? []).map((h, i) => hex(`priorCmsSha256[${i}]`, h));
386
+ const priorRev = (input.priorRevisionSha256 ?? []).map((h, i) => hex(`priorRevisionSha256[${i}]`, h));
387
+ if (priorCms.length + priorRev.length !== input.revIndex - 1) fail2("priorCmsSha256 + priorRevisionSha256-\u0438\u0439\u043D \u0442\u043E\u043E = revIndex \u2212 1");
388
+ if (input.revIndex === 1 && !input.pageSha256) fail2("revIndex 1-\u0434 pageSha256 \u0437\u0430\u0430\u0432\u0430\u043B");
389
+ const body = { docID: input.docID, revIndex: input.revIndex, medium };
390
+ if (sessionID) body.sessionID = sessionID;
391
+ body.cms = cmsB64;
392
+ body.fileName = fileName;
393
+ body.inSha256 = hex("inSha256", input.inSha256);
394
+ if (input.pageSha256) body.pageSha256 = hex("pageSha256", input.pageSha256);
395
+ body.sigRevSha256 = hex("sigRevSha256", input.sigRevSha256);
396
+ body.outSha256 = hex("outSha256", input.outSha256);
397
+ if (priorCms.length) body.priorCmsSha256 = priorCms;
398
+ if (priorRev.length) body.priorRevisionSha256 = priorRev;
399
+ if (input.onBehalfOf?.trim()) body.onBehalfOf = input.onBehalfOf.trim();
400
+ if (input.signerEtsi?.trim()) body.signerEtsi = input.signerEtsi.trim();
401
+ 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}`);
402
+ return body;
403
+ }
404
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
405
+ 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: ";
406
+ var PDF_MAX_FILE_NAME = 120;
407
+ function dtbsBase64(v) {
408
+ const raw = typeof v === "string" ? Buffer.from(v, "base64") : Buffer.from(v);
409
+ if (raw.length !== 32 || typeof v === "string" && raw.toString("base64") !== v) {
410
+ throw new ValidationError("pdfDigest: dtbs \u043D\u044C 32 \u0431\u0430\u0439\u0442 (SHA-256) \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439");
411
+ }
412
+ return raw.toString("base64");
413
+ }
331
414
  function rec2(v) {
332
415
  return v && typeof v === "object" ? v : {};
333
416
  }
@@ -412,6 +495,77 @@ var SignApi = class {
412
495
  const { body, sign } = this.body(digestB64, interactions, opts, "rsassa-pss");
413
496
  return this.notification(routePath(RP_ROUTES.signNotificationDocument, documentNumber), body, sign);
414
497
  }
498
+ /**
499
+ * Клиент талын PAdES (`@gerege-systems/eid-mongolia-sdk/pades`): `prepare()`-ийн DTBS-д иргэнээр гарын үсэг зуруулна.
500
+ * PDF CA руу ОЧИХГҮЙ — зөвхөн 32 байт. CA-ийн өөрийн PAdES session-тэй ижил: `RAW_DIGEST_SIGNATURE`,
501
+ * `sha256WithRSAEncryption` (CMS-д PKCS#1 v1.5; legacy ECDSA иргэнд сервер `ecdsa-with-SHA256`), `displayTextAndPIN`
502
+ * «Баримтад гарын үсэг зурах: <fileName>» (60 тэмдэгтэд тайрна).
503
+ *
504
+ * Дараа нь `session.waitForResult(sessionId)` → `validator.validateSign(result, s.digest)` → `signature.value`-г
505
+ * (base64 → байт) `complete(state, signature)`-д.
506
+ */
507
+ async pdfDigest(input) {
508
+ const digest = dtbsBase64(input.dtbs);
509
+ const fileName = input.fileName?.trim() ?? "";
510
+ if (!fileName || Array.from(fileName).length > PDF_MAX_FILE_NAME) {
511
+ throw new ValidationError(`pdfDigest: fileName \u0437\u0430\u0430\u0432\u0430\u043B, \u2264 ${PDF_MAX_FILE_NAME} \u0442\u044D\u043C\u0434\u044D\u0433\u0442`);
512
+ }
513
+ const signer = input.signer;
514
+ const etsi = signer?.etsi?.trim();
515
+ const doc = signer?.documentNumber?.trim();
516
+ 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");
517
+ const deviceLink2 = input.flow === "device-link";
518
+ const display = Array.from(PDF_DISPLAY_TEXT + fileName).slice(0, 60).join("");
519
+ const interactions = interactionsToBase64([{ type: "displayTextAndPIN", displayText60: display }]);
520
+ const body = {
521
+ relyingPartyUUID: this.creds.rpUUID,
522
+ relyingPartyName: this.creds.rpName,
523
+ certificateLevel: input.certificateLevel ?? this.defaults.certificateLevel,
524
+ signatureProtocol: "RAW_DIGEST_SIGNATURE",
525
+ signatureProtocolParameters: {
526
+ digest,
527
+ signatureAlgorithm: "sha256WithRSAEncryption",
528
+ signatureAlgorithmParameters: { hashAlgorithm: "SHA-256" }
529
+ },
530
+ interactions,
531
+ fileName
532
+ };
533
+ if (input.initialCallbackUrl?.trim()) body.initialCallbackUrl = input.initialCallbackUrl.trim();
534
+ if (input.onBehalfOf?.trim()) body.onBehalfOf = input.onBehalfOf.trim();
535
+ const route = deviceLink2 ? etsi ? RP_ROUTES.signDeviceLinkEtsi : RP_ROUTES.signDeviceLinkDocument : etsi ? RP_ROUTES.signNotificationEtsi : RP_ROUTES.signNotificationDocument;
536
+ const r = rec2(await this.http.post(routePath(route, etsi ?? doc), body));
537
+ const receivedAt = Date.now();
538
+ const vc = typeof r.vc === "object" && r.vc !== null ? rec2(r.vc).value : r.vc;
539
+ let dl = null;
540
+ if (deviceLink2) {
541
+ if (!r.sessionToken || !r.sessionSecret || !r.deviceLinkBase) {
542
+ throw new ValidationError("pdfDigest: device-link \u0445\u0430\u0440\u0438\u0443\u043D\u0434 sessionToken/sessionSecret/deviceLinkBase \u0430\u043B\u0433\u0430");
543
+ }
544
+ dl = { sessionToken: String(r.sessionToken), sessionSecret: String(r.sessionSecret), deviceLinkBase: String(r.deviceLinkBase) };
545
+ }
546
+ return { sessionId: String(r.sessionID), vc: vc != null ? String(vc) : null, digest, interactions, deviceLink: dl, receivedAt };
547
+ }
548
+ /**
549
+ * `POST /pdf/signatures` — `pades.complete()`-ийн дараа CMS-ийг CA-д бүртгэнэ (`/verify/{docID}`, иргэний түүх). PDF
550
+ * илгээгдэхгүй. SIGN эрх. CA CMS-ийг шалгана: 04-ийн SIGN cert, session digest = SHA-256(signedAttrs), claimedRole ↔
551
+ * төлөөлөл. Алдаа: 403 `REPRESENTATION_REVOKED`, 409 `CLAIMED_ROLE_MISMATCH` / `REGISTRY_CONFLICT` /
552
+ * `DOC_ID_CONFLICT` / `PDF_FOREIGN_SIGNATURE` (`ApiError.code`). Бүртгэлгүй файл ч Adobe/EU DSS-д хүчинтэй — зөвхөн
553
+ * `/verify` «бүртгэлгүй» гэнэ.
554
+ */
555
+ async registerSignature(input) {
556
+ return this.registerBody(buildRegisterBody(input));
557
+ }
558
+ async registerBody(body) {
559
+ const raw = await this.http.post(routePath(RP_ROUTES.pdfSignatures), body);
560
+ const r = rec2(raw);
561
+ return {
562
+ docID: String(r.docID ?? body.docID),
563
+ revIndex: Number(r.revIndex ?? 0),
564
+ registeredAt: typeof r.registeredAt === "string" ? r.registeredAt : "",
565
+ verifyUrl: String(r.verifyUrl ?? ""),
566
+ raw
567
+ };
568
+ }
415
569
  /** Текстийн SHA-256 digest-ийг дотроо тооцоод push sign (богино текст баримтад тохиромжтой). */
416
570
  async textByEtsi(id, text, interactions, opts) {
417
571
  return this.digestByEtsi(id, sha256Base64(text), interactions, opts);
@@ -527,7 +681,6 @@ function parseSessionResult(raw) {
527
681
  signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
528
682
  interactionTypeUsed: str(r.interactionTypeUsed),
529
683
  onBehalfOf: parseOrg(r.onBehalfOf),
530
- pdf: parsePdf(r.pdf),
531
684
  person: parsePerson(r.person),
532
685
  tokenCertificate: parseTokenCert(r.tokenCertificate),
533
686
  raw
@@ -565,28 +718,6 @@ function personMatchesCertificate(person, subject) {
565
718
  const serial = typeof subject === "string" ? subject : subject?.serialNumber;
566
719
  return !!person?.civilId && !!serial && serial === `PNOMN-${person.civilId}`;
567
720
  }
568
- function parsePdf(raw) {
569
- const p = asRecord(raw);
570
- const docID = str(p.docID);
571
- if (!docID) return null;
572
- const out = { docID, documentStatus: String(p.documentStatus ?? "") };
573
- if (p.errorCode != null) out.errorCode = String(p.errorCode);
574
- if (p.outSha256 != null) out.outSha256 = String(p.outSha256);
575
- if (p.size != null) out.size = Number(p.size);
576
- if (p.expiresAt != null) out.expiresAt = String(p.expiresAt);
577
- if (p.signatureLevel != null) out.signatureLevel = String(p.signatureLevel);
578
- const sg = asRecord(p.signer);
579
- if (sg.etsi != null) {
580
- out.signer = { etsi: String(sg.etsi), certSerial: String(sg.certSerial ?? "") };
581
- if (sg.onBehalfOf) out.signer.onBehalfOf = String(sg.onBehalfOf);
582
- if (sg.claimedRole) out.signer.claimedRole = String(sg.claimedRole);
583
- }
584
- const v = asRecord(p.validation);
585
- if (v.indication != null) {
586
- out.validation = { indication: String(v.indication), ltv: v.ltv === true, signatures: Number(v.signatures ?? 0) };
587
- }
588
- return out;
589
- }
590
721
  function parseSigParams(raw) {
591
722
  const p = asRecord(raw);
592
723
  const hash = str(p.hashAlgorithm);
@@ -645,160 +776,6 @@ var SessionApi = class {
645
776
  }
646
777
  };
647
778
 
648
- // src/pdf.ts
649
- import { createHash as createHash2 } from "crypto";
650
- var DEFAULT_PDF_MAX_BYTES = 64 << 20;
651
- var DEFAULT_PDF_TIMEOUT_MS = 8e4;
652
- function rec3(v) {
653
- return v && typeof v === "object" ? v : {};
654
- }
655
- var PdfApi = class {
656
- constructor(http, creds, defaults) {
657
- this.http = http;
658
- this.creds = creds;
659
- this.defaults = defaults;
660
- }
661
- http;
662
- creds;
663
- defaults;
664
- /**
665
- * POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. Түр алдаа: `ApiError` 429
666
- * PDF_PREPARE_BUSY, 503 LTV_UNAVAILABLE / TSA_UNAVAILABLE — `retryAfterMs`-ийн дараа дахин (`retryable`).
667
- */
668
- async prepare(input) {
669
- const request = {
670
- relyingPartyUUID: this.creds.rpUUID,
671
- relyingPartyName: this.creds.rpName,
672
- certificateLevel: input.certificateLevel ?? this.defaults.certificateLevel,
673
- signer: input.signer,
674
- flow: input.flow ?? "notification",
675
- fileName: input.fileName
676
- };
677
- if (input.initialCallbackUrl?.trim()) request.initialCallbackUrl = input.initialCallbackUrl.trim();
678
- if (input.docID?.trim()) request.docID = input.docID.trim();
679
- if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
680
- const form = new FormData();
681
- form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
682
- form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
683
- const r = rec3(await this.http.postMultipart(routePath(RP_ROUTES.pdfPrepare), form, this.timeout));
684
- const receivedAt = Date.now();
685
- const vc = rec3(r.vc).value;
686
- const dl = rec3(r.deviceLink);
687
- const digest = typeof r.digest === "string" && r.digest ? r.digest : typeof dl.digest === "string" && dl.digest ? dl.digest : void 0;
688
- return {
689
- sessionId: String(r.sessionID),
690
- docId: String(r.docID),
691
- vc: vc != null ? String(vc) : null,
692
- ...digest !== void 0 ? { digest } : {},
693
- deviceLink: dl.sessionToken ? {
694
- sessionToken: String(dl.sessionToken),
695
- sessionSecret: String(dl.sessionSecret),
696
- deviceLinkBase: String(dl.deviceLinkBase),
697
- digest: String(dl.digest),
698
- interactions: String(dl.interactions)
699
- } : null,
700
- receivedAt
701
- };
702
- }
703
- /**
704
- * GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
705
- * session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`. Баримт
706
- * бэлэн болоогүй бол `ApiError` 503 `.code` DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE — `retryAfterMs`-ийн
707
- * дараа дахин татна (`retryable`).
708
- */
709
- document(sessionId) {
710
- return this.http.getBytes(routePath(RP_ROUTES.pdfDocument, sessionId), this.timeout, this.maxBytes);
711
- }
712
- get timeout() {
713
- return this.defaults.timeoutMs ?? DEFAULT_PDF_TIMEOUT_MS;
714
- }
715
- get maxBytes() {
716
- return this.defaults.maxBytes ?? DEFAULT_PDF_MAX_BYTES;
717
- }
718
- /**
719
- * USB токены PAdES (CA docs/PADES_SIGNING.md §16.5, TOKEN_CERT.md) — session / утас оролцохгүй. POST /pdf/token/prepare
720
- * (multipart: `request` JSON + `pdf`), SIGN эрх. Дараа нь токеноор `hashToSign`-ийг CKM_ECDSA (prehashed, P-256)-аар
721
- * зурж {@link tokenFinalize}. Түр алдаа: `ApiError` 429 PDF_PREPARE_BUSY / RATE_LIMITED, 503 TSA_UNAVAILABLE /
722
- * LTV_UNAVAILABLE / STATE_UNAVAILABLE (`retryable`). Гэрчилгээ токенд холбогдоогүй / цуцлагдсан → 409
723
- * TOKEN_CERT_INVALID. Зам нь CA-д бий ч `/.well-known/eid`-д хараахан алга (гэрээний тест WARN).
724
- */
725
- async tokenPrepare(input) {
726
- if (!input.pdf?.length || !input.certificate?.length || !input.signerEtsi?.trim() || !input.fileName?.trim()) {
727
- throw new Error("eid: pdf token: pdf, certificate, signerEtsi, fileName \u0448\u0430\u0430\u0440\u0434\u043B\u0430\u0433\u0430\u0442\u0430\u0439");
728
- }
729
- const request = {
730
- relyingPartyUUID: this.creds.rpUUID,
731
- relyingPartyName: this.creds.rpName,
732
- signer: { etsi: input.signerEtsi.trim() },
733
- certificate: Buffer.from(input.certificate).toString("base64"),
734
- fileName: input.fileName
735
- };
736
- if (input.docID?.trim()) request.docID = input.docID.trim();
737
- if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
738
- const form = new FormData();
739
- form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
740
- form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
741
- const raw = await this.http.postMultipart(routePath(RP_ROUTES_UNDISCOVERED.pdfTokenPrepare), form, this.timeout);
742
- const r = rec3(raw);
743
- const hashToSign = new Uint8Array(Buffer.from(String(r.hashToSign ?? ""), "base64"));
744
- if (!r.signingID || hashToSign.length !== 32) {
745
- throw new ValidationError("pdf token prepare: signingID / hashToSign (32 \u0431\u0430\u0439\u0442) \u0431\u0443\u0440\u0443\u0443");
746
- }
747
- return {
748
- signingId: String(r.signingID),
749
- docId: String(r.docID ?? ""),
750
- hashToSign,
751
- hashAlgorithm: String(r.hashAlgorithm ?? ""),
752
- signatureAlgorithm: String(r.signatureAlgorithm ?? ""),
753
- signatureFormat: String(r.signatureFormat ?? ""),
754
- expiresAt: String(r.expiresAt ?? ""),
755
- raw
756
- };
757
- }
758
- /**
759
- * POST /pdf/token/{signingID}/finalize `{signature: base64 түүхий r||s}` — `signature` 64 байт (P-256 r||s, DER БИШ).
760
- * Амжвал READY баримтын блок (`outSha256` — {@link tokenDocument}-оор татсан файлтай тулга). READY-ээс хойш идэмпотент.
761
- * Алдаа: 400 SIGNATURE_INVALID (3 удаа → FAILED), 409 DOCUMENT_FAILED, 410 DOCUMENT_EXPIRED, 503 DOCUMENT_PENDING /
762
- * TSA_UNAVAILABLE / LTV_UNAVAILABLE — ижил гарын үсгийг `retryAfterMs`-ийн дараа дахин илгээнэ.
763
- */
764
- async tokenFinalize(signingId, signature) {
765
- 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)");
766
- const r = await this.http.post(
767
- routePath(RP_ROUTES_UNDISCOVERED.pdfTokenFinalize, signingId),
768
- { signature: Buffer.from(signature).toString("base64") },
769
- this.timeout
770
- );
771
- return r;
772
- }
773
- /**
774
- * GET /pdf/token/{signingID}/document — гарын үсэгтэй PDF (зөвхөн үүсгэсэн RP, expiresAt хүртэл), `pdfMaxBytes`
775
- * хүртэл. SHA-256-ийг {@link tokenFinalize}-ийн `outSha256`-тай заавал тулга.
776
- */
777
- tokenDocument(signingId) {
778
- return this.http.getBytes(routePath(RP_ROUTES_UNDISCOVERED.pdfTokenDocument, signingId), this.timeout, this.maxBytes);
779
- }
780
- /**
781
- * {@link document} + тулгалт: татсан байтын SHA-256 нь session poll-ийн `pdf.outSha256`-тай тэнцүү байх ёстой.
782
- * `expected` — poll-ийн `result.pdf` блок (`documentStatus` READY + `outSha256` заавал) эсвэл SHA-256 hex.
783
- *
784
- * @throws {ValidationError} блок READY биш / `outSha256` алга / hash таарахгүй (файлд бүү итгэ).
785
- */
786
- async documentVerified(sessionId, expected) {
787
- let want;
788
- if (typeof expected === "string") {
789
- want = expected;
790
- } else {
791
- if (expected.documentStatus !== "READY") throw new ValidationError(`pdf.documentStatus ${expected.documentStatus} \u2260 READY`);
792
- want = expected.outSha256;
793
- }
794
- 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");
795
- const bytes = await this.document(sessionId);
796
- const got = createHash2("sha256").update(bytes).digest("hex");
797
- 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");
798
- return bytes;
799
- }
800
- };
801
-
802
779
  // src/organization.ts
803
780
  var OrganizationApi = class {
804
781
  constructor(http) {
@@ -814,7 +791,7 @@ var OrganizationApi = class {
814
791
  };
815
792
 
816
793
  // src/person.ts
817
- function rec4(v) {
794
+ function rec3(v) {
818
795
  return v && typeof v === "object" ? v : {};
819
796
  }
820
797
  var PersonApi = class {
@@ -824,7 +801,7 @@ var PersonApi = class {
824
801
  http;
825
802
  async get(path) {
826
803
  const raw = await this.http.get(path);
827
- return { ...rec4(raw), raw };
804
+ return { ...rec3(raw), raw };
828
805
  }
829
806
  /** Иргэний бүх гэрчилгээ + статусын тоолол. */
830
807
  certificates(personEtsi) {
@@ -854,7 +831,7 @@ var PersonApi = class {
854
831
  };
855
832
 
856
833
  // src/token.ts
857
- import { X509Certificate as X509Certificate2, createHash as createHash3, createPublicKey, timingSafeEqual } from "crypto";
834
+ import { X509Certificate as X509Certificate2, createHash as createHash2, createPublicKey, timingSafeEqual } from "crypto";
858
835
 
859
836
  // src/subject.ts
860
837
  import { X509Certificate } from "crypto";
@@ -995,12 +972,12 @@ function certExtensions(der) {
995
972
  }
996
973
 
997
974
  // src/token.ts
998
- function rec5(v) {
975
+ function rec4(v) {
999
976
  return v && typeof v === "object" ? v : {};
1000
977
  }
1001
978
  function hexSha256Equal(hex, data) {
1002
979
  if (typeof hex !== "string" || !/^[0-9a-fA-F]{64}$/.test(hex)) return false;
1003
- return timingSafeEqual(createHash3("sha256").update(data).digest(), Buffer.from(hex, "hex"));
980
+ return timingSafeEqual(createHash2("sha256").update(data).digest(), Buffer.from(hex, "hex"));
1004
981
  }
1005
982
  var TOKEN_RESULT_TIMEOUT_MS = 3e4;
1006
983
  var TokenApi = class {
@@ -1029,11 +1006,11 @@ var TokenApi = class {
1029
1006
  flow: "notification"
1030
1007
  };
1031
1008
  if (input.tokenLabel) body.tokenLabel = input.tokenLabel;
1032
- const r = rec5(await this.http.post(routePath(RP_ROUTES.tokenCertificate), body));
1009
+ const r = rec4(await this.http.post(routePath(RP_ROUTES.tokenCertificate), body));
1033
1010
  if (!hexSha256Equal(r.csrAuthSha256, input.csrAuth) || !hexSha256Equal(r.csrSignSha256, input.csrSign)) {
1034
1011
  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");
1035
1012
  }
1036
- const vc = rec5(r.vc).value ?? (typeof r.vc === "string" ? r.vc : void 0);
1013
+ const vc = rec4(r.vc).value ?? (typeof r.vc === "string" ? r.vc : void 0);
1037
1014
  return {
1038
1015
  sessionId: String(r.sessionID ?? ""),
1039
1016
  vc: vc != null ? String(vc) : null,
@@ -1048,7 +1025,7 @@ var TokenApi = class {
1048
1025
  */
1049
1026
  async certificateResult(sessionId) {
1050
1027
  const raw = await this.http.get(routePath(RP_ROUTES.tokenCertificateResult, sessionId), TOKEN_RESULT_TIMEOUT_MS);
1051
- const r = rec5(raw);
1028
+ const r = rec4(raw);
1052
1029
  const item = (v) => v && typeof v === "object" ? v : null;
1053
1030
  return {
1054
1031
  status: String(r.status ?? ""),
@@ -1064,7 +1041,7 @@ var TokenApi = class {
1064
1041
  }
1065
1042
  /** POST /token/auth/challenge — энэ RP-д холбогдсон нэг удаагийн challenge (2 мин). */
1066
1043
  async authChallenge() {
1067
- const r = rec5(
1044
+ const r = rec4(
1068
1045
  await this.http.post(routePath(RP_ROUTES_UNDISCOVERED.tokenAuthChallenge), {
1069
1046
  relyingPartyUUID: this.creds.rpUUID,
1070
1047
  relyingPartyName: this.creds.rpName
@@ -1095,12 +1072,12 @@ var TokenApi = class {
1095
1072
  certificate: Buffer.from(certDer).toString("base64"),
1096
1073
  signature: Buffer.from(signature).toString("base64")
1097
1074
  });
1098
- const r = rec5(raw);
1099
- if (rec5(r.result).endResult !== "OK") throw new ValidationError("token auth: endResult OK \u0431\u0438\u0448");
1100
- const id = rec5(r.identity);
1075
+ const r = rec4(raw);
1076
+ if (rec4(r.result).endResult !== "OK") throw new ValidationError("token auth: endResult OK \u0431\u0438\u0448");
1077
+ const id = rec4(r.identity);
1101
1078
  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");
1102
- const org = rec5(r.organization);
1103
- const cert = rec5(r.cert);
1079
+ const org = rec4(r.organization);
1080
+ const cert = rec4(r.cert);
1104
1081
  return {
1105
1082
  identity: { etsi: String(id.etsi), givenName: String(id.givenName ?? ""), surname: String(id.surname ?? "") },
1106
1083
  serialHex: String(cert.serialHex ?? ""),
@@ -1148,13 +1125,13 @@ function tokenAuthDigest(rpUUID, challengeB64) {
1148
1125
  }
1149
1126
  const ch = Buffer.from(challengeB64, "base64");
1150
1127
  if (ch.length !== 32 || ch.toString("base64") !== challengeB64) throw new Error("token auth: challenge 32 \u0431\u0430\u0439\u0442 base64 \u0431\u0438\u0448");
1151
- return createHash3("sha256").update("eID-TOKEN-AUTH", "ascii").update(rpUUID, "ascii").update(ch).digest();
1128
+ return createHash2("sha256").update("eID-TOKEN-AUTH", "ascii").update(rpUUID, "ascii").update(ch).digest();
1152
1129
  }
1153
1130
  function tokenLimitOf(err) {
1154
1131
  if (!(err instanceof ApiError) || err.code !== "TOKEN_LIMIT_REACHED") return void 0;
1155
1132
  let b;
1156
1133
  try {
1157
- b = rec5(JSON.parse(err.body));
1134
+ b = rec4(JSON.parse(err.body));
1158
1135
  } catch {
1159
1136
  return void 0;
1160
1137
  }
@@ -1167,7 +1144,7 @@ function tokenLimitOf(err) {
1167
1144
  }
1168
1145
 
1169
1146
  // src/validator.ts
1170
- import { X509Certificate as X509Certificate3, constants, createHash as createHash4, publicDecrypt, verify } from "crypto";
1147
+ import { X509Certificate as X509Certificate3, constants, createHash as createHash3, publicDecrypt, verify } from "crypto";
1171
1148
  var CITIZEN_ISSUING_CA_SKI = "b8a37f2c64ad677912db7d9e34f246358a2fb909";
1172
1149
  var ResponseValidator = class {
1173
1150
  anchors;
@@ -1497,7 +1474,7 @@ function signedCallbackUrl(fromResponse, rpCallback, flowType) {
1497
1474
  return fromResponse;
1498
1475
  }
1499
1476
  function userChallengeOf(verifier) {
1500
- return createHash4("sha256").update(verifier, "utf8").digest("base64url");
1477
+ return createHash3("sha256").update(verifier, "utf8").digest("base64url");
1501
1478
  }
1502
1479
  function verifyPayloadSignature(payload, sigB64, key, alg, hash) {
1503
1480
  const sig = Buffer.from(sigB64, "base64");
@@ -1599,7 +1576,7 @@ function mgf1(hash, seed, length) {
1599
1576
  for (let i = 0, n = 0; n < length; i++, n += hashLen(hash)) {
1600
1577
  const c = Buffer.alloc(4);
1601
1578
  c.writeUInt32BE(i, 0);
1602
- out.push(createHash4(nodeHash(hash)).update(seed).update(c).digest());
1579
+ out.push(createHash3(nodeHash(hash)).update(seed).update(c).digest());
1603
1580
  }
1604
1581
  return Buffer.concat(out).subarray(0, length);
1605
1582
  }
@@ -1623,7 +1600,7 @@ function emsaPssVerify(mHash, em, emBits, hash) {
1623
1600
  const psLen = emLen - hLen - sLen - 2;
1624
1601
  if (!db.subarray(0, psLen).every((b) => b === 0) || db[psLen] !== 1) return false;
1625
1602
  const salt = db.subarray(psLen + 1);
1626
- const h2 = createHash4(nodeHash(hash)).update(Buffer.alloc(8)).update(mHash).update(salt).digest();
1603
+ const h2 = createHash3(nodeHash(hash)).update(Buffer.alloc(8)).update(mHash).update(salt).digest();
1627
1604
  return h.equals(h2);
1628
1605
  }
1629
1606
  function keyUsageBits(cert) {
@@ -1757,7 +1734,6 @@ function resolveBaseUrl(baseUrl) {
1757
1734
  var EidClient = class {
1758
1735
  auth;
1759
1736
  sign;
1760
- pdf;
1761
1737
  organization;
1762
1738
  person;
1763
1739
  token;
@@ -1779,11 +1755,6 @@ var EidClient = class {
1779
1755
  const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
1780
1756
  this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
1781
1757
  this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level, sessionTtlMs: cfg.signSessionTtlMs });
1782
- this.pdf = new PdfApi(http, cfg.credentials, {
1783
- certificateLevel: level,
1784
- timeoutMs: cfg.pdfTimeoutMs ?? DEFAULT_PDF_TIMEOUT_MS,
1785
- maxBytes: cfg.pdfMaxBytes && cfg.pdfMaxBytes > 0 ? cfg.pdfMaxBytes : DEFAULT_PDF_MAX_BYTES
1786
- });
1787
1758
  this.organization = new OrganizationApi(http);
1788
1759
  this.person = new PersonApi(http);
1789
1760
  this.token = new TokenApi(http, cfg.credentials);
@@ -1791,6 +1762,14 @@ var EidClient = class {
1791
1762
  this.validator = new ResponseValidator(cfg.trust ?? {});
1792
1763
  this.rpName = cfg.credentials.rpName;
1793
1764
  }
1765
+ /** {@link SignApi.pdfDigest} — клиент талын PAdES-ийн DTBS-д гарын үсгийн session (PDF CA руу очихгүй). */
1766
+ signPdfDigest(input) {
1767
+ return this.sign.pdfDigest(input);
1768
+ }
1769
+ /** {@link SignApi.registerSignature} — `pades.complete()`-ийн CMS-ийг CA-д бүртгэнэ (`POST /pdf/signatures`). */
1770
+ registerSignature(input) {
1771
+ return this.sign.registerSignature(input);
1772
+ }
1794
1773
  };
1795
1774
 
1796
1775
  // src/devicelinkv3.ts
@@ -1895,7 +1874,7 @@ function qrDeviceLinkTicker(input, onLink, opts) {
1895
1874
  }
1896
1875
 
1897
1876
  // src/login.ts
1898
- import { createHash as createHash5, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "crypto";
1877
+ import { createHash as createHash4, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "crypto";
1899
1878
  function memoryLoginStore() {
1900
1879
  const m = /* @__PURE__ */ new Map();
1901
1880
  return {
@@ -1920,6 +1899,51 @@ function memoryLoginStore() {
1920
1899
  }
1921
1900
  };
1922
1901
  }
1902
+ var defaultRateLimit = Object.freeze({
1903
+ push: Object.freeze({ limit: 3, windowMs: 6e4 })
1904
+ });
1905
+ var RATE_KINDS = ["start", "push"];
1906
+ function windowLimiter(limit, windowMs) {
1907
+ const hits = /* @__PURE__ */ new Map();
1908
+ return (key) => {
1909
+ const now = Date.now();
1910
+ const h = (hits.get(key) ?? []).filter((t) => now - t < windowMs);
1911
+ if (hits.size > 1e4) hits.clear();
1912
+ if (h.length >= limit) {
1913
+ hits.set(key, h);
1914
+ return false;
1915
+ }
1916
+ h.push(now);
1917
+ hits.set(key, h);
1918
+ return true;
1919
+ };
1920
+ }
1921
+ function buildRateLimit(opt) {
1922
+ const extra = typeof opt === "function" ? opt : void 0;
1923
+ const user = opt && typeof opt === "object" ? opt : {};
1924
+ for (const k of Object.keys(user)) {
1925
+ if (!RATE_KINDS.includes(k)) throw new Error(`createEidLogin: rateLimit.${k} \u2014 \u0437\u04E9\u0432\u0445\u04E9\u043D start | push`);
1926
+ }
1927
+ const merged = { ...defaultRateLimit, ...user };
1928
+ const perKind = /* @__PURE__ */ new Map();
1929
+ for (const k of RATE_KINDS) {
1930
+ const r = merged[k];
1931
+ if (r === void 0 || r === false) continue;
1932
+ if (typeof r === "function") {
1933
+ perKind.set(k, r);
1934
+ continue;
1935
+ }
1936
+ if (!(Number.isInteger(r.limit) && r.limit > 0 && r.windowMs > 0)) {
1937
+ throw new Error(`createEidLogin: rateLimit.${k} \u2014 limit (\u0431\u04AF\u0445\u044D\u043B > 0), windowMs (> 0)`);
1938
+ }
1939
+ perKind.set(k, windowLimiter(r.limit, r.windowMs));
1940
+ }
1941
+ return async (key) => {
1942
+ const fn = perKind.get(key.slice(0, key.indexOf(":")));
1943
+ if (fn && !await fn(key)) return false;
1944
+ return extra ? extra(key) : true;
1945
+ };
1946
+ }
1923
1947
  var CALLBACK_PATH = "/auth/eid/callback";
1924
1948
  function forwardedClientIp(headers, trustProxy) {
1925
1949
  if (trustProxy === false || trustProxy === 0) return void 0;
@@ -1932,7 +1956,7 @@ function forwardedClientIp(headers, trustProxy) {
1932
1956
  return header(headers, "x-real-ip").trim() || void 0;
1933
1957
  }
1934
1958
  var ID_RE = /^(\p{L}{2}[0-9]{8}|[0-9]{8,14}|PNOMN-[0-9]{8,14})$/u;
1935
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
1959
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
1936
1960
  var B64URL_RE = /^[A-Za-z0-9_-]{43}$/;
1937
1961
  var IOS_BROWSERS = [
1938
1962
  [/CriOS/i, "googlechromes"],
@@ -1976,7 +2000,7 @@ function readCookie(h, name) {
1976
2000
  }
1977
2001
  return "";
1978
2002
  }
1979
- var sha256hex = (s) => createHash5("sha256").update(s).digest("hex");
2003
+ var sha256hex = (s) => createHash4("sha256").update(s).digest("hex");
1980
2004
  function safeEqualHex(a, b) {
1981
2005
  const x = Buffer.from(a, "hex");
1982
2006
  const y = Buffer.from(b, "hex");
@@ -2019,17 +2043,7 @@ function createEidLogin(opts) {
2019
2043
  const successRedirect = opts.successRedirect ?? "/";
2020
2044
  const failureRedirect = opts.failureRedirect ?? "/";
2021
2045
  const onError = opts.onError ?? ((e) => console.error("eid-login:", e instanceof Error ? e.message : e));
2022
- const pushHits = /* @__PURE__ */ new Map();
2023
- const rateLimit = opts.rateLimit ?? ((key) => {
2024
- if (!key.startsWith("push:")) return true;
2025
- const now = Date.now();
2026
- const hits = (pushHits.get(key) ?? []).filter((t) => now - t < 6e4);
2027
- if (pushHits.size > 1e4) pushHits.clear();
2028
- if (hits.length >= 3) return false;
2029
- hits.push(now);
2030
- pushHits.set(key, hits);
2031
- return true;
2032
- });
2046
+ const rateLimit = buildRateLimit(opts.rateLimit);
2033
2047
  const displayText = (opts.displayText ?? `${client.rpName}-\u0434 \u043D\u044D\u0432\u0442\u0440\u044D\u0445`).slice(0, 60);
2034
2048
  const interactions = [{ type: "displayTextAndPIN", displayText60: displayText }];
2035
2049
  const json = (status2, body, extra = {}) => ({
@@ -2063,7 +2077,7 @@ function createEidLogin(opts) {
2063
2077
  const dot = raw.indexOf(".");
2064
2078
  const sid = raw.slice(0, dot);
2065
2079
  const binding = raw.slice(dot + 1);
2066
- if (dot < 0 || !UUID_RE.test(sid) || !B64URL_RE.test(binding)) return void 0;
2080
+ if (dot < 0 || !UUID_RE2.test(sid) || !B64URL_RE.test(binding)) return void 0;
2067
2081
  const tx = await store.get(sid);
2068
2082
  if (!tx || !safeEqualHex(tx.bindingHash, sha256hex(binding))) return void 0;
2069
2083
  if (tx.expiresAt <= Date.now()) {
@@ -2288,8 +2302,6 @@ export {
2288
2302
  CITIZEN_ISSUING_CA_SKI,
2289
2303
  CertChoiceInvalidError,
2290
2304
  DEFAULT_BASE_URL,
2291
- DEFAULT_PDF_MAX_BYTES,
2292
- DEFAULT_PDF_TIMEOUT_MS,
2293
2305
  DEFAULT_SIGN_SESSION_TTL_MS,
2294
2306
  DeviceLinkError,
2295
2307
  EidClient,
@@ -2300,7 +2312,7 @@ export {
2300
2312
  NoSigningCertificateError,
2301
2313
  ON_BEHALF_ERROR_CODES,
2302
2314
  OrganizationApi,
2303
- PdfApi,
2315
+ PDF_SIGNATURE_REGISTER_FIELDS,
2304
2316
  PersonApi,
2305
2317
  RETRYABLE_ERROR_CODES,
2306
2318
  RequestInvalidError,
@@ -2314,7 +2326,9 @@ export {
2314
2326
  acspV2Digest,
2315
2327
  buildAcspV2Payload,
2316
2328
  buildDeviceLink,
2329
+ buildRegisterBody,
2317
2330
  createEidLogin,
2331
+ defaultRateLimit,
2318
2332
  deviceLink,
2319
2333
  deviceLinkInput,
2320
2334
  forwardedClientIp,
@@ -2329,6 +2343,7 @@ export {
2329
2343
  personMatchesCertificate,
2330
2344
  qrDeviceLinkTicker,
2331
2345
  randomChallenge,
2346
+ registrationFromCompleted,
2332
2347
  resolveBaseUrl,
2333
2348
  sessionExpiry,
2334
2349
  sha256Base64,