@gerege-systems/eid-mongolia-sdk 0.14.1 → 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.js CHANGED
@@ -1,21 +1,26 @@
1
+ import {
2
+ deviceLink,
3
+ isMobileUserAgent,
4
+ openOrShowQR
5
+ } from "./chunk-REWNE7PO.js";
1
6
  import {
2
7
  ApiError,
3
8
  AuthenticationError,
9
+ CertChoiceInvalidError,
4
10
  DeviceLinkError,
5
11
  EidError,
6
12
  ForbiddenError,
7
13
  NetworkError,
14
+ NoSigningCertificateError,
8
15
  ON_BEHALF_ERROR_CODES,
9
16
  RETRYABLE_ERROR_CODES,
17
+ RequestInvalidError,
10
18
  ResponseTooLargeError,
11
19
  SessionFailedError,
12
20
  ValidationError,
13
- deviceLink,
14
- isMobileUserAgent,
15
21
  maskPath,
16
- openOrShowQR,
17
22
  parseRetryAfter
18
- } from "./chunk-QPJDZLHR.js";
23
+ } from "./chunk-TB2KQH3W.js";
19
24
 
20
25
  // src/http.ts
21
26
  var DEFAULT_TIMEOUT_MS = 15e3;
@@ -31,15 +36,6 @@ var Http = class {
31
36
  get(path, timeoutMs) {
32
37
  return this.json("GET", path, void 0, void 0, timeoutMs);
33
38
  }
34
- /** multipart/form-data POST (Content-Type-ийг boundary-тай нь fetch тавина). JSON хариу. */
35
- postMultipart(path, form, timeoutMs) {
36
- return this.json("POST", path, form, void 0, timeoutMs);
37
- }
38
- /** GET — хариуг байтаар (ж: гарын үсэгтэй PDF), `maxBytes` хүртэл; хэтэрвэл {@link ResponseTooLargeError}. */
39
- async getBytes(path, timeoutMs, maxBytes = 64 << 20) {
40
- const res = await this.send("GET", path, void 0, void 0, timeoutMs);
41
- return readLimited(res, maxBytes, `GET ${maskPath(path)}`);
42
- }
43
39
  async json(method, path, body, contentType, timeoutMs) {
44
40
  const res = await this.send(method, path, body, contentType, timeoutMs);
45
41
  const text = new TextDecoder().decode(await readLimited(res, MAX_JSON_BYTES, `${method} ${maskPath(path)}`));
@@ -85,7 +81,8 @@ var Http = class {
85
81
  { headers, retryAfterMs }
86
82
  );
87
83
  }
88
- throw new ApiError(res.status, text, code, { headers, retryAfterMs });
84
+ const Typed = code === "NO_SIGNING_CERTIFICATE" ? NoSigningCertificateError : code === "CERT_CHOICE_INVALID" ? CertChoiceInvalidError : code === "REQUEST_INVALID" ? RequestInvalidError : ApiError;
85
+ throw new Typed(res.status, text, code, { headers, retryAfterMs });
89
86
  }
90
87
  };
91
88
  async function readLimited(res, limit, op) {
@@ -143,8 +140,6 @@ var RP_ROUTES = {
143
140
  signDeviceLinkDocument: { method: "POST", path: "/signature/device-link/document/{documentNumber}" },
144
141
  certChoiceDeviceLink: { method: "POST", path: "/signature/certificate-choice/device-link/anonymous" },
145
142
  certChoiceEtsi: { method: "POST", path: "/signature/certificate-choice/notification/etsi/{semanticsIdentifier}" },
146
- pdfPrepare: { method: "POST", path: "/pdf/sign/prepare" },
147
- pdfDocument: { method: "GET", path: "/pdf/sign/{sessionID}/document" },
148
143
  orgRepresentations: { method: "GET", path: "/organization/representations/etsi/{personEtsi}" },
149
144
  session: { method: "GET", path: "/session/{sessionId}" },
150
145
  personCertificates: { method: "GET", path: "/certificates/etsi/{personEtsi}" },
@@ -153,15 +148,29 @@ var RP_ROUTES = {
153
148
  personSummary: { method: "GET", path: "/person/summary/etsi/{personEtsi}" },
154
149
  personChildren: { method: "GET", path: "/person/children/etsi/{personEtsi}" },
155
150
  tokenCertificate: { method: "POST", path: "/token/certificate" },
156
- tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" }
151
+ tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" },
152
+ pdfSignatures: { method: "POST", path: "/pdf/signatures" }
157
153
  };
158
154
  var RP_ROUTES_UNDISCOVERED = {
159
155
  tokenAuthChallenge: { method: "POST", path: "/token/auth/challenge" },
160
- tokenAuthVerify: { method: "POST", path: "/token/auth/verify" },
161
- pdfTokenPrepare: { method: "POST", path: "/pdf/token/prepare" },
162
- pdfTokenFinalize: { method: "POST", path: "/pdf/token/{signingID}/finalize" },
163
- pdfTokenDocument: { method: "GET", path: "/pdf/token/{signingID}/document" }
156
+ tokenAuthVerify: { method: "POST", path: "/token/auth/verify" }
164
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
+ ];
165
174
  function routePath(route, ...args) {
166
175
  let i = 0;
167
176
  const out = route.path.replace(/\{[^}]*\}/g, () => {
@@ -211,6 +220,21 @@ function acspV2Digest(p, hashAlgorithm) {
211
220
  return createHash(nodeHash(hashAlgorithm)).update(buildAcspV2Payload(p)).digest();
212
221
  }
213
222
 
223
+ // src/types.ts
224
+ var DEFAULT_SIGN_SESSION_TTL_MS = 6e5;
225
+ function sessionExpiry(sentAtMs, ttlMs) {
226
+ return sentAtMs + (ttlMs && ttlMs > 0 ? ttlMs : DEFAULT_SIGN_SESSION_TTL_MS);
227
+ }
228
+ function parseExpiresAt(v) {
229
+ if (typeof v !== "string" || !/^\d{4}-\d{2}-\d{2}T/.test(v)) return void 0;
230
+ const t = Date.parse(v);
231
+ return Number.isFinite(t) ? t : void 0;
232
+ }
233
+ function startExpiry(raw, sentAtMs, ttlMs) {
234
+ const r = raw && typeof raw === "object" ? raw : {};
235
+ return parseExpiresAt(r.expiresAt) ?? sessionExpiry(sentAtMs, ttlMs);
236
+ }
237
+
214
238
  // src/auth.ts
215
239
  function rec(v) {
216
240
  return v && typeof v === "object" ? v : {};
@@ -256,11 +280,19 @@ var AuthApi = class {
256
280
  parseNotification(raw, acsp) {
257
281
  const r = rec(raw);
258
282
  const vc = rec(r.vc).value;
259
- return { sessionId: String(r.sessionID), vc: vc != null ? String(vc) : null, rpChallenge: acsp.rpChallenge, acsp };
283
+ const expiresAt = parseExpiresAt(r.expiresAt);
284
+ return {
285
+ sessionId: String(r.sessionID),
286
+ vc: vc != null ? String(vc) : null,
287
+ rpChallenge: acsp.rpChallenge,
288
+ acsp,
289
+ ...expiresAt !== void 0 ? { expiresAt } : {}
290
+ };
260
291
  }
261
292
  parseDeviceLink(raw, acsp) {
262
293
  const r = rec(raw);
263
294
  const vc = typeof r.vc === "object" && r.vc !== null ? rec(r.vc).value : r.vc;
295
+ const expiresAt = parseExpiresAt(r.expiresAt);
264
296
  return {
265
297
  sessionId: String(r.sessionID),
266
298
  sessionToken: r.sessionToken != null ? String(r.sessionToken) : null,
@@ -270,7 +302,8 @@ var AuthApi = class {
270
302
  rpChallenge: acsp.rpChallenge,
271
303
  acsp,
272
304
  receivedAt: Date.now(),
273
- sessionType: "auth"
305
+ sessionType: "auth",
306
+ ...expiresAt !== void 0 ? { expiresAt } : {}
274
307
  };
275
308
  }
276
309
  /** Push — РД/иргэний дугаар/ETSI-ээр (сервер төрлийг таьна). Иргэний утас руу мэдэгдэл очно. */
@@ -299,13 +332,85 @@ var AuthApi = class {
299
332
  }
300
333
  };
301
334
 
302
- // src/types.ts
303
- var DEFAULT_SIGN_SESSION_TTL_MS = 6e5;
304
- function sessionExpiry(sentAtMs, ttlMs) {
305
- return sentAtMs + (ttlMs && ttlMs > 0 ? ttlMs : DEFAULT_SIGN_SESSION_TTL_MS);
306
- }
307
-
308
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
+ }
309
414
  function rec2(v) {
310
415
  return v && typeof v === "object" ? v : {};
311
416
  }
@@ -356,7 +461,7 @@ var SignApi = class {
356
461
  vc: vcOf(r),
357
462
  rpChallenge: "",
358
463
  ...sign ? { sign } : {},
359
- expiresAt: sessionExpiry(sent, this.defaults.sessionTtlMs)
464
+ expiresAt: startExpiry(r, sent, this.defaults.sessionTtlMs)
360
465
  };
361
466
  }
362
467
  async deviceLink(path, body, extra) {
@@ -371,7 +476,7 @@ var SignApi = class {
371
476
  rpChallenge: "",
372
477
  receivedAt: Date.now(),
373
478
  ...extra,
374
- expiresAt: sessionExpiry(sent, this.defaults.sessionTtlMs)
479
+ expiresAt: startExpiry(r, sent, this.defaults.sessionTtlMs)
375
480
  };
376
481
  }
377
482
  /**
@@ -390,6 +495,77 @@ var SignApi = class {
390
495
  const { body, sign } = this.body(digestB64, interactions, opts, "rsassa-pss");
391
496
  return this.notification(routePath(RP_ROUTES.signNotificationDocument, documentNumber), body, sign);
392
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
+ }
393
569
  /** Текстийн SHA-256 digest-ийг дотроо тооцоод push sign (богино текст баримтад тохиромжтой). */
394
570
  async textByEtsi(id, text, interactions, opts) {
395
571
  return this.digestByEtsi(id, sha256Base64(text), interactions, opts);
@@ -505,7 +681,6 @@ function parseSessionResult(raw) {
505
681
  signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
506
682
  interactionTypeUsed: str(r.interactionTypeUsed),
507
683
  onBehalfOf: parseOrg(r.onBehalfOf),
508
- pdf: parsePdf(r.pdf),
509
684
  person: parsePerson(r.person),
510
685
  tokenCertificate: parseTokenCert(r.tokenCertificate),
511
686
  raw
@@ -543,28 +718,6 @@ function personMatchesCertificate(person, subject) {
543
718
  const serial = typeof subject === "string" ? subject : subject?.serialNumber;
544
719
  return !!person?.civilId && !!serial && serial === `PNOMN-${person.civilId}`;
545
720
  }
546
- function parsePdf(raw) {
547
- const p = asRecord(raw);
548
- const docID = str(p.docID);
549
- if (!docID) return null;
550
- const out = { docID, documentStatus: String(p.documentStatus ?? "") };
551
- if (p.errorCode != null) out.errorCode = String(p.errorCode);
552
- if (p.outSha256 != null) out.outSha256 = String(p.outSha256);
553
- if (p.size != null) out.size = Number(p.size);
554
- if (p.expiresAt != null) out.expiresAt = String(p.expiresAt);
555
- if (p.signatureLevel != null) out.signatureLevel = String(p.signatureLevel);
556
- const sg = asRecord(p.signer);
557
- if (sg.etsi != null) {
558
- out.signer = { etsi: String(sg.etsi), certSerial: String(sg.certSerial ?? "") };
559
- if (sg.onBehalfOf) out.signer.onBehalfOf = String(sg.onBehalfOf);
560
- if (sg.claimedRole) out.signer.claimedRole = String(sg.claimedRole);
561
- }
562
- const v = asRecord(p.validation);
563
- if (v.indication != null) {
564
- out.validation = { indication: String(v.indication), ltv: v.ltv === true, signatures: Number(v.signatures ?? 0) };
565
- }
566
- return out;
567
- }
568
721
  function parseSigParams(raw) {
569
722
  const p = asRecord(raw);
570
723
  const hash = str(p.hashAlgorithm);
@@ -623,160 +776,6 @@ var SessionApi = class {
623
776
  }
624
777
  };
625
778
 
626
- // src/pdf.ts
627
- import { createHash as createHash2 } from "crypto";
628
- var DEFAULT_PDF_MAX_BYTES = 64 << 20;
629
- var DEFAULT_PDF_TIMEOUT_MS = 8e4;
630
- function rec3(v) {
631
- return v && typeof v === "object" ? v : {};
632
- }
633
- var PdfApi = class {
634
- constructor(http, creds, defaults) {
635
- this.http = http;
636
- this.creds = creds;
637
- this.defaults = defaults;
638
- }
639
- http;
640
- creds;
641
- defaults;
642
- /**
643
- * POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. Түр алдаа: `ApiError` 429
644
- * PDF_PREPARE_BUSY, 503 LTV_UNAVAILABLE / TSA_UNAVAILABLE — `retryAfterMs`-ийн дараа дахин (`retryable`).
645
- */
646
- async prepare(input) {
647
- const request = {
648
- relyingPartyUUID: this.creds.rpUUID,
649
- relyingPartyName: this.creds.rpName,
650
- certificateLevel: input.certificateLevel ?? this.defaults.certificateLevel,
651
- signer: input.signer,
652
- flow: input.flow ?? "notification",
653
- fileName: input.fileName
654
- };
655
- if (input.initialCallbackUrl?.trim()) request.initialCallbackUrl = input.initialCallbackUrl.trim();
656
- if (input.docID?.trim()) request.docID = input.docID.trim();
657
- if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
658
- const form = new FormData();
659
- form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
660
- form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
661
- const r = rec3(await this.http.postMultipart(routePath(RP_ROUTES.pdfPrepare), form, this.timeout));
662
- const receivedAt = Date.now();
663
- const vc = rec3(r.vc).value;
664
- const dl = rec3(r.deviceLink);
665
- const digest = typeof r.digest === "string" && r.digest ? r.digest : typeof dl.digest === "string" && dl.digest ? dl.digest : void 0;
666
- return {
667
- sessionId: String(r.sessionID),
668
- docId: String(r.docID),
669
- vc: vc != null ? String(vc) : null,
670
- ...digest !== void 0 ? { digest } : {},
671
- deviceLink: dl.sessionToken ? {
672
- sessionToken: String(dl.sessionToken),
673
- sessionSecret: String(dl.sessionSecret),
674
- deviceLinkBase: String(dl.deviceLinkBase),
675
- digest: String(dl.digest),
676
- interactions: String(dl.interactions)
677
- } : null,
678
- receivedAt
679
- };
680
- }
681
- /**
682
- * GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
683
- * session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`. Баримт
684
- * бэлэн болоогүй бол `ApiError` 503 `.code` DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE — `retryAfterMs`-ийн
685
- * дараа дахин татна (`retryable`).
686
- */
687
- document(sessionId) {
688
- return this.http.getBytes(routePath(RP_ROUTES.pdfDocument, sessionId), this.timeout, this.maxBytes);
689
- }
690
- get timeout() {
691
- return this.defaults.timeoutMs ?? DEFAULT_PDF_TIMEOUT_MS;
692
- }
693
- get maxBytes() {
694
- return this.defaults.maxBytes ?? DEFAULT_PDF_MAX_BYTES;
695
- }
696
- /**
697
- * USB токены PAdES (CA docs/PADES_SIGNING.md §16.5, TOKEN_CERT.md) — session / утас оролцохгүй. POST /pdf/token/prepare
698
- * (multipart: `request` JSON + `pdf`), SIGN эрх. Дараа нь токеноор `hashToSign`-ийг CKM_ECDSA (prehashed, P-256)-аар
699
- * зурж {@link tokenFinalize}. Түр алдаа: `ApiError` 429 PDF_PREPARE_BUSY / RATE_LIMITED, 503 TSA_UNAVAILABLE /
700
- * LTV_UNAVAILABLE / STATE_UNAVAILABLE (`retryable`). Гэрчилгээ токенд холбогдоогүй / цуцлагдсан → 409
701
- * TOKEN_CERT_INVALID. Зам нь CA-д бий ч `/.well-known/eid`-д хараахан алга (гэрээний тест WARN).
702
- */
703
- async tokenPrepare(input) {
704
- if (!input.pdf?.length || !input.certificate?.length || !input.signerEtsi?.trim() || !input.fileName?.trim()) {
705
- throw new Error("eid: pdf token: pdf, certificate, signerEtsi, fileName \u0448\u0430\u0430\u0440\u0434\u043B\u0430\u0433\u0430\u0442\u0430\u0439");
706
- }
707
- const request = {
708
- relyingPartyUUID: this.creds.rpUUID,
709
- relyingPartyName: this.creds.rpName,
710
- signer: { etsi: input.signerEtsi.trim() },
711
- certificate: Buffer.from(input.certificate).toString("base64"),
712
- fileName: input.fileName
713
- };
714
- if (input.docID?.trim()) request.docID = input.docID.trim();
715
- if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
716
- const form = new FormData();
717
- form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
718
- form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
719
- const raw = await this.http.postMultipart(routePath(RP_ROUTES_UNDISCOVERED.pdfTokenPrepare), form, this.timeout);
720
- const r = rec3(raw);
721
- const hashToSign = new Uint8Array(Buffer.from(String(r.hashToSign ?? ""), "base64"));
722
- if (!r.signingID || hashToSign.length !== 32) {
723
- throw new ValidationError("pdf token prepare: signingID / hashToSign (32 \u0431\u0430\u0439\u0442) \u0431\u0443\u0440\u0443\u0443");
724
- }
725
- return {
726
- signingId: String(r.signingID),
727
- docId: String(r.docID ?? ""),
728
- hashToSign,
729
- hashAlgorithm: String(r.hashAlgorithm ?? ""),
730
- signatureAlgorithm: String(r.signatureAlgorithm ?? ""),
731
- signatureFormat: String(r.signatureFormat ?? ""),
732
- expiresAt: String(r.expiresAt ?? ""),
733
- raw
734
- };
735
- }
736
- /**
737
- * POST /pdf/token/{signingID}/finalize `{signature: base64 түүхий r||s}` — `signature` 64 байт (P-256 r||s, DER БИШ).
738
- * Амжвал READY баримтын блок (`outSha256` — {@link tokenDocument}-оор татсан файлтай тулга). READY-ээс хойш идэмпотент.
739
- * Алдаа: 400 SIGNATURE_INVALID (3 удаа → FAILED), 409 DOCUMENT_FAILED, 410 DOCUMENT_EXPIRED, 503 DOCUMENT_PENDING /
740
- * TSA_UNAVAILABLE / LTV_UNAVAILABLE — ижил гарын үсгийг `retryAfterMs`-ийн дараа дахин илгээнэ.
741
- */
742
- async tokenFinalize(signingId, signature) {
743
- 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)");
744
- const r = await this.http.post(
745
- routePath(RP_ROUTES_UNDISCOVERED.pdfTokenFinalize, signingId),
746
- { signature: Buffer.from(signature).toString("base64") },
747
- this.timeout
748
- );
749
- return r;
750
- }
751
- /**
752
- * GET /pdf/token/{signingID}/document — гарын үсэгтэй PDF (зөвхөн үүсгэсэн RP, expiresAt хүртэл), `pdfMaxBytes`
753
- * хүртэл. SHA-256-ийг {@link tokenFinalize}-ийн `outSha256`-тай заавал тулга.
754
- */
755
- tokenDocument(signingId) {
756
- return this.http.getBytes(routePath(RP_ROUTES_UNDISCOVERED.pdfTokenDocument, signingId), this.timeout, this.maxBytes);
757
- }
758
- /**
759
- * {@link document} + тулгалт: татсан байтын SHA-256 нь session poll-ийн `pdf.outSha256`-тай тэнцүү байх ёстой.
760
- * `expected` — poll-ийн `result.pdf` блок (`documentStatus` READY + `outSha256` заавал) эсвэл SHA-256 hex.
761
- *
762
- * @throws {ValidationError} блок READY биш / `outSha256` алга / hash таарахгүй (файлд бүү итгэ).
763
- */
764
- async documentVerified(sessionId, expected) {
765
- let want;
766
- if (typeof expected === "string") {
767
- want = expected;
768
- } else {
769
- if (expected.documentStatus !== "READY") throw new ValidationError(`pdf.documentStatus ${expected.documentStatus} \u2260 READY`);
770
- want = expected.outSha256;
771
- }
772
- 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");
773
- const bytes = await this.document(sessionId);
774
- const got = createHash2("sha256").update(bytes).digest("hex");
775
- 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");
776
- return bytes;
777
- }
778
- };
779
-
780
779
  // src/organization.ts
781
780
  var OrganizationApi = class {
782
781
  constructor(http) {
@@ -792,7 +791,7 @@ var OrganizationApi = class {
792
791
  };
793
792
 
794
793
  // src/person.ts
795
- function rec4(v) {
794
+ function rec3(v) {
796
795
  return v && typeof v === "object" ? v : {};
797
796
  }
798
797
  var PersonApi = class {
@@ -802,7 +801,7 @@ var PersonApi = class {
802
801
  http;
803
802
  async get(path) {
804
803
  const raw = await this.http.get(path);
805
- return { ...rec4(raw), raw };
804
+ return { ...rec3(raw), raw };
806
805
  }
807
806
  /** Иргэний бүх гэрчилгээ + статусын тоолол. */
808
807
  certificates(personEtsi) {
@@ -832,7 +831,7 @@ var PersonApi = class {
832
831
  };
833
832
 
834
833
  // src/token.ts
835
- import { X509Certificate as X509Certificate2, createHash as createHash3, createPublicKey, timingSafeEqual } from "crypto";
834
+ import { X509Certificate as X509Certificate2, createHash as createHash2, createPublicKey, timingSafeEqual } from "crypto";
836
835
 
837
836
  // src/subject.ts
838
837
  import { X509Certificate } from "crypto";
@@ -973,12 +972,12 @@ function certExtensions(der) {
973
972
  }
974
973
 
975
974
  // src/token.ts
976
- function rec5(v) {
975
+ function rec4(v) {
977
976
  return v && typeof v === "object" ? v : {};
978
977
  }
979
978
  function hexSha256Equal(hex, data) {
980
979
  if (typeof hex !== "string" || !/^[0-9a-fA-F]{64}$/.test(hex)) return false;
981
- return timingSafeEqual(createHash3("sha256").update(data).digest(), Buffer.from(hex, "hex"));
980
+ return timingSafeEqual(createHash2("sha256").update(data).digest(), Buffer.from(hex, "hex"));
982
981
  }
983
982
  var TOKEN_RESULT_TIMEOUT_MS = 3e4;
984
983
  var TokenApi = class {
@@ -1007,11 +1006,11 @@ var TokenApi = class {
1007
1006
  flow: "notification"
1008
1007
  };
1009
1008
  if (input.tokenLabel) body.tokenLabel = input.tokenLabel;
1010
- 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));
1011
1010
  if (!hexSha256Equal(r.csrAuthSha256, input.csrAuth) || !hexSha256Equal(r.csrSignSha256, input.csrSign)) {
1012
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");
1013
1012
  }
1014
- 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);
1015
1014
  return {
1016
1015
  sessionId: String(r.sessionID ?? ""),
1017
1016
  vc: vc != null ? String(vc) : null,
@@ -1026,7 +1025,7 @@ var TokenApi = class {
1026
1025
  */
1027
1026
  async certificateResult(sessionId) {
1028
1027
  const raw = await this.http.get(routePath(RP_ROUTES.tokenCertificateResult, sessionId), TOKEN_RESULT_TIMEOUT_MS);
1029
- const r = rec5(raw);
1028
+ const r = rec4(raw);
1030
1029
  const item = (v) => v && typeof v === "object" ? v : null;
1031
1030
  return {
1032
1031
  status: String(r.status ?? ""),
@@ -1042,7 +1041,7 @@ var TokenApi = class {
1042
1041
  }
1043
1042
  /** POST /token/auth/challenge — энэ RP-д холбогдсон нэг удаагийн challenge (2 мин). */
1044
1043
  async authChallenge() {
1045
- const r = rec5(
1044
+ const r = rec4(
1046
1045
  await this.http.post(routePath(RP_ROUTES_UNDISCOVERED.tokenAuthChallenge), {
1047
1046
  relyingPartyUUID: this.creds.rpUUID,
1048
1047
  relyingPartyName: this.creds.rpName
@@ -1073,12 +1072,12 @@ var TokenApi = class {
1073
1072
  certificate: Buffer.from(certDer).toString("base64"),
1074
1073
  signature: Buffer.from(signature).toString("base64")
1075
1074
  });
1076
- const r = rec5(raw);
1077
- if (rec5(r.result).endResult !== "OK") throw new ValidationError("token auth: endResult OK \u0431\u0438\u0448");
1078
- 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);
1079
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");
1080
- const org = rec5(r.organization);
1081
- const cert = rec5(r.cert);
1079
+ const org = rec4(r.organization);
1080
+ const cert = rec4(r.cert);
1082
1081
  return {
1083
1082
  identity: { etsi: String(id.etsi), givenName: String(id.givenName ?? ""), surname: String(id.surname ?? "") },
1084
1083
  serialHex: String(cert.serialHex ?? ""),
@@ -1126,13 +1125,13 @@ function tokenAuthDigest(rpUUID, challengeB64) {
1126
1125
  }
1127
1126
  const ch = Buffer.from(challengeB64, "base64");
1128
1127
  if (ch.length !== 32 || ch.toString("base64") !== challengeB64) throw new Error("token auth: challenge 32 \u0431\u0430\u0439\u0442 base64 \u0431\u0438\u0448");
1129
- 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();
1130
1129
  }
1131
1130
  function tokenLimitOf(err) {
1132
1131
  if (!(err instanceof ApiError) || err.code !== "TOKEN_LIMIT_REACHED") return void 0;
1133
1132
  let b;
1134
1133
  try {
1135
- b = rec5(JSON.parse(err.body));
1134
+ b = rec4(JSON.parse(err.body));
1136
1135
  } catch {
1137
1136
  return void 0;
1138
1137
  }
@@ -1145,7 +1144,7 @@ function tokenLimitOf(err) {
1145
1144
  }
1146
1145
 
1147
1146
  // src/validator.ts
1148
- 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";
1149
1148
  var CITIZEN_ISSUING_CA_SKI = "b8a37f2c64ad677912db7d9e34f246358a2fb909";
1150
1149
  var ResponseValidator = class {
1151
1150
  anchors;
@@ -1475,7 +1474,7 @@ function signedCallbackUrl(fromResponse, rpCallback, flowType) {
1475
1474
  return fromResponse;
1476
1475
  }
1477
1476
  function userChallengeOf(verifier) {
1478
- return createHash4("sha256").update(verifier, "utf8").digest("base64url");
1477
+ return createHash3("sha256").update(verifier, "utf8").digest("base64url");
1479
1478
  }
1480
1479
  function verifyPayloadSignature(payload, sigB64, key, alg, hash) {
1481
1480
  const sig = Buffer.from(sigB64, "base64");
@@ -1577,7 +1576,7 @@ function mgf1(hash, seed, length) {
1577
1576
  for (let i = 0, n = 0; n < length; i++, n += hashLen(hash)) {
1578
1577
  const c = Buffer.alloc(4);
1579
1578
  c.writeUInt32BE(i, 0);
1580
- out.push(createHash4(nodeHash(hash)).update(seed).update(c).digest());
1579
+ out.push(createHash3(nodeHash(hash)).update(seed).update(c).digest());
1581
1580
  }
1582
1581
  return Buffer.concat(out).subarray(0, length);
1583
1582
  }
@@ -1601,7 +1600,7 @@ function emsaPssVerify(mHash, em, emBits, hash) {
1601
1600
  const psLen = emLen - hLen - sLen - 2;
1602
1601
  if (!db.subarray(0, psLen).every((b) => b === 0) || db[psLen] !== 1) return false;
1603
1602
  const salt = db.subarray(psLen + 1);
1604
- 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();
1605
1604
  return h.equals(h2);
1606
1605
  }
1607
1606
  function keyUsageBits(cert) {
@@ -1735,7 +1734,6 @@ function resolveBaseUrl(baseUrl) {
1735
1734
  var EidClient = class {
1736
1735
  auth;
1737
1736
  sign;
1738
- pdf;
1739
1737
  organization;
1740
1738
  person;
1741
1739
  token;
@@ -1757,11 +1755,6 @@ var EidClient = class {
1757
1755
  const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
1758
1756
  this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
1759
1757
  this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level, sessionTtlMs: cfg.signSessionTtlMs });
1760
- this.pdf = new PdfApi(http, cfg.credentials, {
1761
- certificateLevel: level,
1762
- timeoutMs: cfg.pdfTimeoutMs ?? DEFAULT_PDF_TIMEOUT_MS,
1763
- maxBytes: cfg.pdfMaxBytes && cfg.pdfMaxBytes > 0 ? cfg.pdfMaxBytes : DEFAULT_PDF_MAX_BYTES
1764
- });
1765
1758
  this.organization = new OrganizationApi(http);
1766
1759
  this.person = new PersonApi(http);
1767
1760
  this.token = new TokenApi(http, cfg.credentials);
@@ -1769,6 +1762,14 @@ var EidClient = class {
1769
1762
  this.validator = new ResponseValidator(cfg.trust ?? {});
1770
1763
  this.rpName = cfg.credentials.rpName;
1771
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
+ }
1772
1773
  };
1773
1774
 
1774
1775
  // src/devicelinkv3.ts
@@ -1873,7 +1874,7 @@ function qrDeviceLinkTicker(input, onLink, opts) {
1873
1874
  }
1874
1875
 
1875
1876
  // src/login.ts
1876
- 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";
1877
1878
  function memoryLoginStore() {
1878
1879
  const m = /* @__PURE__ */ new Map();
1879
1880
  return {
@@ -1910,7 +1911,7 @@ function forwardedClientIp(headers, trustProxy) {
1910
1911
  return header(headers, "x-real-ip").trim() || void 0;
1911
1912
  }
1912
1913
  var ID_RE = /^(\p{L}{2}[0-9]{8}|[0-9]{8,14}|PNOMN-[0-9]{8,14})$/u;
1913
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
1914
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
1914
1915
  var B64URL_RE = /^[A-Za-z0-9_-]{43}$/;
1915
1916
  var IOS_BROWSERS = [
1916
1917
  [/CriOS/i, "googlechromes"],
@@ -1954,7 +1955,7 @@ function readCookie(h, name) {
1954
1955
  }
1955
1956
  return "";
1956
1957
  }
1957
- var sha256hex = (s) => createHash5("sha256").update(s).digest("hex");
1958
+ var sha256hex = (s) => createHash4("sha256").update(s).digest("hex");
1958
1959
  function safeEqualHex(a, b) {
1959
1960
  const x = Buffer.from(a, "hex");
1960
1961
  const y = Buffer.from(b, "hex");
@@ -2041,7 +2042,7 @@ function createEidLogin(opts) {
2041
2042
  const dot = raw.indexOf(".");
2042
2043
  const sid = raw.slice(0, dot);
2043
2044
  const binding = raw.slice(dot + 1);
2044
- if (dot < 0 || !UUID_RE.test(sid) || !B64URL_RE.test(binding)) return void 0;
2045
+ if (dot < 0 || !UUID_RE2.test(sid) || !B64URL_RE.test(binding)) return void 0;
2045
2046
  const tx = await store.get(sid);
2046
2047
  if (!tx || !safeEqualHex(tx.bindingHash, sha256hex(binding))) return void 0;
2047
2048
  if (tx.expiresAt <= Date.now()) {
@@ -2264,9 +2265,8 @@ export {
2264
2265
  AuthApi,
2265
2266
  AuthenticationError,
2266
2267
  CITIZEN_ISSUING_CA_SKI,
2268
+ CertChoiceInvalidError,
2267
2269
  DEFAULT_BASE_URL,
2268
- DEFAULT_PDF_MAX_BYTES,
2269
- DEFAULT_PDF_TIMEOUT_MS,
2270
2270
  DEFAULT_SIGN_SESSION_TTL_MS,
2271
2271
  DeviceLinkError,
2272
2272
  EidClient,
@@ -2274,11 +2274,13 @@ export {
2274
2274
  ForbiddenError,
2275
2275
  Http,
2276
2276
  NetworkError,
2277
+ NoSigningCertificateError,
2277
2278
  ON_BEHALF_ERROR_CODES,
2278
2279
  OrganizationApi,
2279
- PdfApi,
2280
+ PDF_SIGNATURE_REGISTER_FIELDS,
2280
2281
  PersonApi,
2281
2282
  RETRYABLE_ERROR_CODES,
2283
+ RequestInvalidError,
2282
2284
  ResponseTooLargeError,
2283
2285
  ResponseValidator,
2284
2286
  SessionApi,
@@ -2289,6 +2291,7 @@ export {
2289
2291
  acspV2Digest,
2290
2292
  buildAcspV2Payload,
2291
2293
  buildDeviceLink,
2294
+ buildRegisterBody,
2292
2295
  createEidLogin,
2293
2296
  deviceLink,
2294
2297
  deviceLinkInput,
@@ -2297,15 +2300,18 @@ export {
2297
2300
  isMobileUserAgent,
2298
2301
  memoryLoginStore,
2299
2302
  openOrShowQR,
2303
+ parseExpiresAt,
2300
2304
  parseRetryAfter,
2301
2305
  parseSessionResult,
2302
2306
  parseSubject,
2303
2307
  personMatchesCertificate,
2304
2308
  qrDeviceLinkTicker,
2305
2309
  randomChallenge,
2310
+ registrationFromCompleted,
2306
2311
  resolveBaseUrl,
2307
2312
  sessionExpiry,
2308
2313
  sha256Base64,
2314
+ startExpiry,
2309
2315
  tokenAuthDigest,
2310
2316
  tokenLimitOf,
2311
2317
  userChallengeOf,