@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/README.md +140 -46
- package/dist/{browser-CuMH5IVG.d.cts → browser-8k_VDSP8.d.cts} +19 -169
- package/dist/{browser-CuMH5IVG.d.ts → browser-BZ3hvv6-.d.ts} +19 -169
- package/dist/browser.d.cts +2 -1
- package/dist/browser.d.ts +2 -1
- package/dist/browser.js +5 -3
- package/dist/chunk-REWNE7PO.js +51 -0
- package/dist/{chunk-QPJDZLHR.js → chunk-TB2KQH3W.js} +10 -46
- package/dist/errors-Z7SgLVTr.d.cts +149 -0
- package/dist/errors-Z7SgLVTr.d.ts +149 -0
- package/dist/index.cjs +268 -256
- package/dist/index.d.cts +147 -156
- package/dist/index.d.ts +147 -156
- package/dist/index.js +247 -241
- package/dist/pades.cjs +3520 -0
- package/dist/pades.d.cts +128 -0
- package/dist/pades.d.ts +128 -0
- package/dist/pades.js +3484 -0
- package/package.json +16 -6
package/dist/index.cjs
CHANGED
|
@@ -24,9 +24,8 @@ __export(index_exports, {
|
|
|
24
24
|
AuthApi: () => AuthApi,
|
|
25
25
|
AuthenticationError: () => AuthenticationError,
|
|
26
26
|
CITIZEN_ISSUING_CA_SKI: () => CITIZEN_ISSUING_CA_SKI,
|
|
27
|
+
CertChoiceInvalidError: () => CertChoiceInvalidError,
|
|
27
28
|
DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
|
|
28
|
-
DEFAULT_PDF_MAX_BYTES: () => DEFAULT_PDF_MAX_BYTES,
|
|
29
|
-
DEFAULT_PDF_TIMEOUT_MS: () => DEFAULT_PDF_TIMEOUT_MS,
|
|
30
29
|
DEFAULT_SIGN_SESSION_TTL_MS: () => DEFAULT_SIGN_SESSION_TTL_MS,
|
|
31
30
|
DeviceLinkError: () => DeviceLinkError,
|
|
32
31
|
EidClient: () => EidClient,
|
|
@@ -34,11 +33,13 @@ __export(index_exports, {
|
|
|
34
33
|
ForbiddenError: () => ForbiddenError,
|
|
35
34
|
Http: () => Http,
|
|
36
35
|
NetworkError: () => NetworkError,
|
|
36
|
+
NoSigningCertificateError: () => NoSigningCertificateError,
|
|
37
37
|
ON_BEHALF_ERROR_CODES: () => ON_BEHALF_ERROR_CODES,
|
|
38
38
|
OrganizationApi: () => OrganizationApi,
|
|
39
|
-
|
|
39
|
+
PDF_SIGNATURE_REGISTER_FIELDS: () => PDF_SIGNATURE_REGISTER_FIELDS,
|
|
40
40
|
PersonApi: () => PersonApi,
|
|
41
41
|
RETRYABLE_ERROR_CODES: () => RETRYABLE_ERROR_CODES,
|
|
42
|
+
RequestInvalidError: () => RequestInvalidError,
|
|
42
43
|
ResponseTooLargeError: () => ResponseTooLargeError,
|
|
43
44
|
ResponseValidator: () => ResponseValidator,
|
|
44
45
|
SessionApi: () => SessionApi,
|
|
@@ -49,6 +50,7 @@ __export(index_exports, {
|
|
|
49
50
|
acspV2Digest: () => acspV2Digest,
|
|
50
51
|
buildAcspV2Payload: () => buildAcspV2Payload,
|
|
51
52
|
buildDeviceLink: () => buildDeviceLink,
|
|
53
|
+
buildRegisterBody: () => buildRegisterBody,
|
|
52
54
|
createEidLogin: () => createEidLogin,
|
|
53
55
|
deviceLink: () => deviceLink,
|
|
54
56
|
deviceLinkInput: () => deviceLinkInput,
|
|
@@ -57,15 +59,18 @@ __export(index_exports, {
|
|
|
57
59
|
isMobileUserAgent: () => isMobileUserAgent,
|
|
58
60
|
memoryLoginStore: () => memoryLoginStore,
|
|
59
61
|
openOrShowQR: () => openOrShowQR,
|
|
62
|
+
parseExpiresAt: () => parseExpiresAt,
|
|
60
63
|
parseRetryAfter: () => parseRetryAfter,
|
|
61
64
|
parseSessionResult: () => parseSessionResult,
|
|
62
65
|
parseSubject: () => parseSubject,
|
|
63
66
|
personMatchesCertificate: () => personMatchesCertificate,
|
|
64
67
|
qrDeviceLinkTicker: () => qrDeviceLinkTicker,
|
|
65
68
|
randomChallenge: () => randomChallenge,
|
|
69
|
+
registrationFromCompleted: () => registrationFromCompleted,
|
|
66
70
|
resolveBaseUrl: () => resolveBaseUrl,
|
|
67
71
|
sessionExpiry: () => sessionExpiry,
|
|
68
72
|
sha256Base64: () => sha256Base64,
|
|
73
|
+
startExpiry: () => startExpiry,
|
|
69
74
|
tokenAuthDigest: () => tokenAuthDigest,
|
|
70
75
|
tokenLimitOf: () => tokenLimitOf,
|
|
71
76
|
userChallengeOf: () => userChallengeOf,
|
|
@@ -138,6 +143,12 @@ var ApiError = class extends EidError {
|
|
|
138
143
|
return (this.status === 429 || this.status === 503) && (this.retryAfterMs ?? 0) > 0;
|
|
139
144
|
}
|
|
140
145
|
};
|
|
146
|
+
var NoSigningCertificateError = class extends ApiError {
|
|
147
|
+
};
|
|
148
|
+
var CertChoiceInvalidError = class extends ApiError {
|
|
149
|
+
};
|
|
150
|
+
var RequestInvalidError = class extends ApiError {
|
|
151
|
+
};
|
|
141
152
|
var RETRYABLE_ERROR_CODES = [
|
|
142
153
|
"PDF_PREPARE_BUSY",
|
|
143
154
|
"DOCUMENT_PENDING",
|
|
@@ -215,15 +226,6 @@ var Http = class {
|
|
|
215
226
|
get(path, timeoutMs) {
|
|
216
227
|
return this.json("GET", path, void 0, void 0, timeoutMs);
|
|
217
228
|
}
|
|
218
|
-
/** multipart/form-data POST (Content-Type-ийг boundary-тай нь fetch тавина). JSON хариу. */
|
|
219
|
-
postMultipart(path, form, timeoutMs) {
|
|
220
|
-
return this.json("POST", path, form, void 0, timeoutMs);
|
|
221
|
-
}
|
|
222
|
-
/** GET — хариуг байтаар (ж: гарын үсэгтэй PDF), `maxBytes` хүртэл; хэтэрвэл {@link ResponseTooLargeError}. */
|
|
223
|
-
async getBytes(path, timeoutMs, maxBytes = 64 << 20) {
|
|
224
|
-
const res = await this.send("GET", path, void 0, void 0, timeoutMs);
|
|
225
|
-
return readLimited(res, maxBytes, `GET ${maskPath(path)}`);
|
|
226
|
-
}
|
|
227
229
|
async json(method, path, body, contentType, timeoutMs) {
|
|
228
230
|
const res = await this.send(method, path, body, contentType, timeoutMs);
|
|
229
231
|
const text = new TextDecoder().decode(await readLimited(res, MAX_JSON_BYTES, `${method} ${maskPath(path)}`));
|
|
@@ -269,7 +271,8 @@ var Http = class {
|
|
|
269
271
|
{ headers, retryAfterMs }
|
|
270
272
|
);
|
|
271
273
|
}
|
|
272
|
-
|
|
274
|
+
const Typed = code === "NO_SIGNING_CERTIFICATE" ? NoSigningCertificateError : code === "CERT_CHOICE_INVALID" ? CertChoiceInvalidError : code === "REQUEST_INVALID" ? RequestInvalidError : ApiError;
|
|
275
|
+
throw new Typed(res.status, text, code, { headers, retryAfterMs });
|
|
273
276
|
}
|
|
274
277
|
};
|
|
275
278
|
async function readLimited(res, limit, op) {
|
|
@@ -327,8 +330,6 @@ var RP_ROUTES = {
|
|
|
327
330
|
signDeviceLinkDocument: { method: "POST", path: "/signature/device-link/document/{documentNumber}" },
|
|
328
331
|
certChoiceDeviceLink: { method: "POST", path: "/signature/certificate-choice/device-link/anonymous" },
|
|
329
332
|
certChoiceEtsi: { method: "POST", path: "/signature/certificate-choice/notification/etsi/{semanticsIdentifier}" },
|
|
330
|
-
pdfPrepare: { method: "POST", path: "/pdf/sign/prepare" },
|
|
331
|
-
pdfDocument: { method: "GET", path: "/pdf/sign/{sessionID}/document" },
|
|
332
333
|
orgRepresentations: { method: "GET", path: "/organization/representations/etsi/{personEtsi}" },
|
|
333
334
|
session: { method: "GET", path: "/session/{sessionId}" },
|
|
334
335
|
personCertificates: { method: "GET", path: "/certificates/etsi/{personEtsi}" },
|
|
@@ -337,15 +338,29 @@ var RP_ROUTES = {
|
|
|
337
338
|
personSummary: { method: "GET", path: "/person/summary/etsi/{personEtsi}" },
|
|
338
339
|
personChildren: { method: "GET", path: "/person/children/etsi/{personEtsi}" },
|
|
339
340
|
tokenCertificate: { method: "POST", path: "/token/certificate" },
|
|
340
|
-
tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" }
|
|
341
|
+
tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" },
|
|
342
|
+
pdfSignatures: { method: "POST", path: "/pdf/signatures" }
|
|
341
343
|
};
|
|
342
344
|
var RP_ROUTES_UNDISCOVERED = {
|
|
343
345
|
tokenAuthChallenge: { method: "POST", path: "/token/auth/challenge" },
|
|
344
|
-
tokenAuthVerify: { method: "POST", path: "/token/auth/verify" }
|
|
345
|
-
pdfTokenPrepare: { method: "POST", path: "/pdf/token/prepare" },
|
|
346
|
-
pdfTokenFinalize: { method: "POST", path: "/pdf/token/{signingID}/finalize" },
|
|
347
|
-
pdfTokenDocument: { method: "GET", path: "/pdf/token/{signingID}/document" }
|
|
346
|
+
tokenAuthVerify: { method: "POST", path: "/token/auth/verify" }
|
|
348
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
|
+
];
|
|
349
364
|
function routePath(route, ...args) {
|
|
350
365
|
let i = 0;
|
|
351
366
|
const out = route.path.replace(/\{[^}]*\}/g, () => {
|
|
@@ -395,6 +410,21 @@ function acspV2Digest(p, hashAlgorithm) {
|
|
|
395
410
|
return (0, import_node_crypto.createHash)(nodeHash(hashAlgorithm)).update(buildAcspV2Payload(p)).digest();
|
|
396
411
|
}
|
|
397
412
|
|
|
413
|
+
// src/types.ts
|
|
414
|
+
var DEFAULT_SIGN_SESSION_TTL_MS = 6e5;
|
|
415
|
+
function sessionExpiry(sentAtMs, ttlMs) {
|
|
416
|
+
return sentAtMs + (ttlMs && ttlMs > 0 ? ttlMs : DEFAULT_SIGN_SESSION_TTL_MS);
|
|
417
|
+
}
|
|
418
|
+
function parseExpiresAt(v) {
|
|
419
|
+
if (typeof v !== "string" || !/^\d{4}-\d{2}-\d{2}T/.test(v)) return void 0;
|
|
420
|
+
const t = Date.parse(v);
|
|
421
|
+
return Number.isFinite(t) ? t : void 0;
|
|
422
|
+
}
|
|
423
|
+
function startExpiry(raw, sentAtMs, ttlMs) {
|
|
424
|
+
const r = raw && typeof raw === "object" ? raw : {};
|
|
425
|
+
return parseExpiresAt(r.expiresAt) ?? sessionExpiry(sentAtMs, ttlMs);
|
|
426
|
+
}
|
|
427
|
+
|
|
398
428
|
// src/auth.ts
|
|
399
429
|
function rec(v) {
|
|
400
430
|
return v && typeof v === "object" ? v : {};
|
|
@@ -440,11 +470,19 @@ var AuthApi = class {
|
|
|
440
470
|
parseNotification(raw, acsp) {
|
|
441
471
|
const r = rec(raw);
|
|
442
472
|
const vc = rec(r.vc).value;
|
|
443
|
-
|
|
473
|
+
const expiresAt = parseExpiresAt(r.expiresAt);
|
|
474
|
+
return {
|
|
475
|
+
sessionId: String(r.sessionID),
|
|
476
|
+
vc: vc != null ? String(vc) : null,
|
|
477
|
+
rpChallenge: acsp.rpChallenge,
|
|
478
|
+
acsp,
|
|
479
|
+
...expiresAt !== void 0 ? { expiresAt } : {}
|
|
480
|
+
};
|
|
444
481
|
}
|
|
445
482
|
parseDeviceLink(raw, acsp) {
|
|
446
483
|
const r = rec(raw);
|
|
447
484
|
const vc = typeof r.vc === "object" && r.vc !== null ? rec(r.vc).value : r.vc;
|
|
485
|
+
const expiresAt = parseExpiresAt(r.expiresAt);
|
|
448
486
|
return {
|
|
449
487
|
sessionId: String(r.sessionID),
|
|
450
488
|
sessionToken: r.sessionToken != null ? String(r.sessionToken) : null,
|
|
@@ -454,7 +492,8 @@ var AuthApi = class {
|
|
|
454
492
|
rpChallenge: acsp.rpChallenge,
|
|
455
493
|
acsp,
|
|
456
494
|
receivedAt: Date.now(),
|
|
457
|
-
sessionType: "auth"
|
|
495
|
+
sessionType: "auth",
|
|
496
|
+
...expiresAt !== void 0 ? { expiresAt } : {}
|
|
458
497
|
};
|
|
459
498
|
}
|
|
460
499
|
/** Push — РД/иргэний дугаар/ETSI-ээр (сервер төрлийг таьна). Иргэний утас руу мэдэгдэл очно. */
|
|
@@ -483,13 +522,85 @@ var AuthApi = class {
|
|
|
483
522
|
}
|
|
484
523
|
};
|
|
485
524
|
|
|
486
|
-
// src/types.ts
|
|
487
|
-
var DEFAULT_SIGN_SESSION_TTL_MS = 6e5;
|
|
488
|
-
function sessionExpiry(sentAtMs, ttlMs) {
|
|
489
|
-
return sentAtMs + (ttlMs && ttlMs > 0 ? ttlMs : DEFAULT_SIGN_SESSION_TTL_MS);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
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
|
+
}
|
|
493
604
|
function rec2(v) {
|
|
494
605
|
return v && typeof v === "object" ? v : {};
|
|
495
606
|
}
|
|
@@ -540,7 +651,7 @@ var SignApi = class {
|
|
|
540
651
|
vc: vcOf(r),
|
|
541
652
|
rpChallenge: "",
|
|
542
653
|
...sign ? { sign } : {},
|
|
543
|
-
expiresAt:
|
|
654
|
+
expiresAt: startExpiry(r, sent, this.defaults.sessionTtlMs)
|
|
544
655
|
};
|
|
545
656
|
}
|
|
546
657
|
async deviceLink(path, body, extra) {
|
|
@@ -555,7 +666,7 @@ var SignApi = class {
|
|
|
555
666
|
rpChallenge: "",
|
|
556
667
|
receivedAt: Date.now(),
|
|
557
668
|
...extra,
|
|
558
|
-
expiresAt:
|
|
669
|
+
expiresAt: startExpiry(r, sent, this.defaults.sessionTtlMs)
|
|
559
670
|
};
|
|
560
671
|
}
|
|
561
672
|
/**
|
|
@@ -574,6 +685,77 @@ var SignApi = class {
|
|
|
574
685
|
const { body, sign } = this.body(digestB64, interactions, opts, "rsassa-pss");
|
|
575
686
|
return this.notification(routePath(RP_ROUTES.signNotificationDocument, documentNumber), body, sign);
|
|
576
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
|
+
}
|
|
577
759
|
/** Текстийн SHA-256 digest-ийг дотроо тооцоод push sign (богино текст баримтад тохиромжтой). */
|
|
578
760
|
async textByEtsi(id, text, interactions, opts) {
|
|
579
761
|
return this.digestByEtsi(id, sha256Base64(text), interactions, opts);
|
|
@@ -689,7 +871,6 @@ function parseSessionResult(raw) {
|
|
|
689
871
|
signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
|
|
690
872
|
interactionTypeUsed: str(r.interactionTypeUsed),
|
|
691
873
|
onBehalfOf: parseOrg(r.onBehalfOf),
|
|
692
|
-
pdf: parsePdf(r.pdf),
|
|
693
874
|
person: parsePerson(r.person),
|
|
694
875
|
tokenCertificate: parseTokenCert(r.tokenCertificate),
|
|
695
876
|
raw
|
|
@@ -727,28 +908,6 @@ function personMatchesCertificate(person, subject) {
|
|
|
727
908
|
const serial = typeof subject === "string" ? subject : subject?.serialNumber;
|
|
728
909
|
return !!person?.civilId && !!serial && serial === `PNOMN-${person.civilId}`;
|
|
729
910
|
}
|
|
730
|
-
function parsePdf(raw) {
|
|
731
|
-
const p = asRecord(raw);
|
|
732
|
-
const docID = str(p.docID);
|
|
733
|
-
if (!docID) return null;
|
|
734
|
-
const out = { docID, documentStatus: String(p.documentStatus ?? "") };
|
|
735
|
-
if (p.errorCode != null) out.errorCode = String(p.errorCode);
|
|
736
|
-
if (p.outSha256 != null) out.outSha256 = String(p.outSha256);
|
|
737
|
-
if (p.size != null) out.size = Number(p.size);
|
|
738
|
-
if (p.expiresAt != null) out.expiresAt = String(p.expiresAt);
|
|
739
|
-
if (p.signatureLevel != null) out.signatureLevel = String(p.signatureLevel);
|
|
740
|
-
const sg = asRecord(p.signer);
|
|
741
|
-
if (sg.etsi != null) {
|
|
742
|
-
out.signer = { etsi: String(sg.etsi), certSerial: String(sg.certSerial ?? "") };
|
|
743
|
-
if (sg.onBehalfOf) out.signer.onBehalfOf = String(sg.onBehalfOf);
|
|
744
|
-
if (sg.claimedRole) out.signer.claimedRole = String(sg.claimedRole);
|
|
745
|
-
}
|
|
746
|
-
const v = asRecord(p.validation);
|
|
747
|
-
if (v.indication != null) {
|
|
748
|
-
out.validation = { indication: String(v.indication), ltv: v.ltv === true, signatures: Number(v.signatures ?? 0) };
|
|
749
|
-
}
|
|
750
|
-
return out;
|
|
751
|
-
}
|
|
752
911
|
function parseSigParams(raw) {
|
|
753
912
|
const p = asRecord(raw);
|
|
754
913
|
const hash = str(p.hashAlgorithm);
|
|
@@ -807,160 +966,6 @@ var SessionApi = class {
|
|
|
807
966
|
}
|
|
808
967
|
};
|
|
809
968
|
|
|
810
|
-
// src/pdf.ts
|
|
811
|
-
var import_node_crypto2 = require("crypto");
|
|
812
|
-
var DEFAULT_PDF_MAX_BYTES = 64 << 20;
|
|
813
|
-
var DEFAULT_PDF_TIMEOUT_MS = 8e4;
|
|
814
|
-
function rec3(v) {
|
|
815
|
-
return v && typeof v === "object" ? v : {};
|
|
816
|
-
}
|
|
817
|
-
var PdfApi = class {
|
|
818
|
-
constructor(http, creds, defaults) {
|
|
819
|
-
this.http = http;
|
|
820
|
-
this.creds = creds;
|
|
821
|
-
this.defaults = defaults;
|
|
822
|
-
}
|
|
823
|
-
http;
|
|
824
|
-
creds;
|
|
825
|
-
defaults;
|
|
826
|
-
/**
|
|
827
|
-
* POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. Түр алдаа: `ApiError` 429
|
|
828
|
-
* PDF_PREPARE_BUSY, 503 LTV_UNAVAILABLE / TSA_UNAVAILABLE — `retryAfterMs`-ийн дараа дахин (`retryable`).
|
|
829
|
-
*/
|
|
830
|
-
async prepare(input) {
|
|
831
|
-
const request = {
|
|
832
|
-
relyingPartyUUID: this.creds.rpUUID,
|
|
833
|
-
relyingPartyName: this.creds.rpName,
|
|
834
|
-
certificateLevel: input.certificateLevel ?? this.defaults.certificateLevel,
|
|
835
|
-
signer: input.signer,
|
|
836
|
-
flow: input.flow ?? "notification",
|
|
837
|
-
fileName: input.fileName
|
|
838
|
-
};
|
|
839
|
-
if (input.initialCallbackUrl?.trim()) request.initialCallbackUrl = input.initialCallbackUrl.trim();
|
|
840
|
-
if (input.docID?.trim()) request.docID = input.docID.trim();
|
|
841
|
-
if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
|
|
842
|
-
const form = new FormData();
|
|
843
|
-
form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
|
|
844
|
-
form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
|
|
845
|
-
const r = rec3(await this.http.postMultipart(routePath(RP_ROUTES.pdfPrepare), form, this.timeout));
|
|
846
|
-
const receivedAt = Date.now();
|
|
847
|
-
const vc = rec3(r.vc).value;
|
|
848
|
-
const dl = rec3(r.deviceLink);
|
|
849
|
-
const digest = typeof r.digest === "string" && r.digest ? r.digest : typeof dl.digest === "string" && dl.digest ? dl.digest : void 0;
|
|
850
|
-
return {
|
|
851
|
-
sessionId: String(r.sessionID),
|
|
852
|
-
docId: String(r.docID),
|
|
853
|
-
vc: vc != null ? String(vc) : null,
|
|
854
|
-
...digest !== void 0 ? { digest } : {},
|
|
855
|
-
deviceLink: dl.sessionToken ? {
|
|
856
|
-
sessionToken: String(dl.sessionToken),
|
|
857
|
-
sessionSecret: String(dl.sessionSecret),
|
|
858
|
-
deviceLinkBase: String(dl.deviceLinkBase),
|
|
859
|
-
digest: String(dl.digest),
|
|
860
|
-
interactions: String(dl.interactions)
|
|
861
|
-
} : null,
|
|
862
|
-
receivedAt
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
/**
|
|
866
|
-
* GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
|
|
867
|
-
* session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`. Баримт
|
|
868
|
-
* бэлэн болоогүй бол `ApiError` 503 `.code` DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE — `retryAfterMs`-ийн
|
|
869
|
-
* дараа дахин татна (`retryable`).
|
|
870
|
-
*/
|
|
871
|
-
document(sessionId) {
|
|
872
|
-
return this.http.getBytes(routePath(RP_ROUTES.pdfDocument, sessionId), this.timeout, this.maxBytes);
|
|
873
|
-
}
|
|
874
|
-
get timeout() {
|
|
875
|
-
return this.defaults.timeoutMs ?? DEFAULT_PDF_TIMEOUT_MS;
|
|
876
|
-
}
|
|
877
|
-
get maxBytes() {
|
|
878
|
-
return this.defaults.maxBytes ?? DEFAULT_PDF_MAX_BYTES;
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* USB токены PAdES (CA docs/PADES_SIGNING.md §16.5, TOKEN_CERT.md) — session / утас оролцохгүй. POST /pdf/token/prepare
|
|
882
|
-
* (multipart: `request` JSON + `pdf`), SIGN эрх. Дараа нь токеноор `hashToSign`-ийг CKM_ECDSA (prehashed, P-256)-аар
|
|
883
|
-
* зурж {@link tokenFinalize}. Түр алдаа: `ApiError` 429 PDF_PREPARE_BUSY / RATE_LIMITED, 503 TSA_UNAVAILABLE /
|
|
884
|
-
* LTV_UNAVAILABLE / STATE_UNAVAILABLE (`retryable`). Гэрчилгээ токенд холбогдоогүй / цуцлагдсан → 409
|
|
885
|
-
* TOKEN_CERT_INVALID. Зам нь CA-д бий ч `/.well-known/eid`-д хараахан алга (гэрээний тест WARN).
|
|
886
|
-
*/
|
|
887
|
-
async tokenPrepare(input) {
|
|
888
|
-
if (!input.pdf?.length || !input.certificate?.length || !input.signerEtsi?.trim() || !input.fileName?.trim()) {
|
|
889
|
-
throw new Error("eid: pdf token: pdf, certificate, signerEtsi, fileName \u0448\u0430\u0430\u0440\u0434\u043B\u0430\u0433\u0430\u0442\u0430\u0439");
|
|
890
|
-
}
|
|
891
|
-
const request = {
|
|
892
|
-
relyingPartyUUID: this.creds.rpUUID,
|
|
893
|
-
relyingPartyName: this.creds.rpName,
|
|
894
|
-
signer: { etsi: input.signerEtsi.trim() },
|
|
895
|
-
certificate: Buffer.from(input.certificate).toString("base64"),
|
|
896
|
-
fileName: input.fileName
|
|
897
|
-
};
|
|
898
|
-
if (input.docID?.trim()) request.docID = input.docID.trim();
|
|
899
|
-
if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
|
|
900
|
-
const form = new FormData();
|
|
901
|
-
form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
|
|
902
|
-
form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
|
|
903
|
-
const raw = await this.http.postMultipart(routePath(RP_ROUTES_UNDISCOVERED.pdfTokenPrepare), form, this.timeout);
|
|
904
|
-
const r = rec3(raw);
|
|
905
|
-
const hashToSign = new Uint8Array(Buffer.from(String(r.hashToSign ?? ""), "base64"));
|
|
906
|
-
if (!r.signingID || hashToSign.length !== 32) {
|
|
907
|
-
throw new ValidationError("pdf token prepare: signingID / hashToSign (32 \u0431\u0430\u0439\u0442) \u0431\u0443\u0440\u0443\u0443");
|
|
908
|
-
}
|
|
909
|
-
return {
|
|
910
|
-
signingId: String(r.signingID),
|
|
911
|
-
docId: String(r.docID ?? ""),
|
|
912
|
-
hashToSign,
|
|
913
|
-
hashAlgorithm: String(r.hashAlgorithm ?? ""),
|
|
914
|
-
signatureAlgorithm: String(r.signatureAlgorithm ?? ""),
|
|
915
|
-
signatureFormat: String(r.signatureFormat ?? ""),
|
|
916
|
-
expiresAt: String(r.expiresAt ?? ""),
|
|
917
|
-
raw
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
/**
|
|
921
|
-
* POST /pdf/token/{signingID}/finalize `{signature: base64 түүхий r||s}` — `signature` 64 байт (P-256 r||s, DER БИШ).
|
|
922
|
-
* Амжвал READY баримтын блок (`outSha256` — {@link tokenDocument}-оор татсан файлтай тулга). READY-ээс хойш идэмпотент.
|
|
923
|
-
* Алдаа: 400 SIGNATURE_INVALID (3 удаа → FAILED), 409 DOCUMENT_FAILED, 410 DOCUMENT_EXPIRED, 503 DOCUMENT_PENDING /
|
|
924
|
-
* TSA_UNAVAILABLE / LTV_UNAVAILABLE — ижил гарын үсгийг `retryAfterMs`-ийн дараа дахин илгээнэ.
|
|
925
|
-
*/
|
|
926
|
-
async tokenFinalize(signingId, signature) {
|
|
927
|
-
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)");
|
|
928
|
-
const r = await this.http.post(
|
|
929
|
-
routePath(RP_ROUTES_UNDISCOVERED.pdfTokenFinalize, signingId),
|
|
930
|
-
{ signature: Buffer.from(signature).toString("base64") },
|
|
931
|
-
this.timeout
|
|
932
|
-
);
|
|
933
|
-
return r;
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* GET /pdf/token/{signingID}/document — гарын үсэгтэй PDF (зөвхөн үүсгэсэн RP, expiresAt хүртэл), `pdfMaxBytes`
|
|
937
|
-
* хүртэл. SHA-256-ийг {@link tokenFinalize}-ийн `outSha256`-тай заавал тулга.
|
|
938
|
-
*/
|
|
939
|
-
tokenDocument(signingId) {
|
|
940
|
-
return this.http.getBytes(routePath(RP_ROUTES_UNDISCOVERED.pdfTokenDocument, signingId), this.timeout, this.maxBytes);
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* {@link document} + тулгалт: татсан байтын SHA-256 нь session poll-ийн `pdf.outSha256`-тай тэнцүү байх ёстой.
|
|
944
|
-
* `expected` — poll-ийн `result.pdf` блок (`documentStatus` READY + `outSha256` заавал) эсвэл SHA-256 hex.
|
|
945
|
-
*
|
|
946
|
-
* @throws {ValidationError} блок READY биш / `outSha256` алга / hash таарахгүй (файлд бүү итгэ).
|
|
947
|
-
*/
|
|
948
|
-
async documentVerified(sessionId, expected) {
|
|
949
|
-
let want;
|
|
950
|
-
if (typeof expected === "string") {
|
|
951
|
-
want = expected;
|
|
952
|
-
} else {
|
|
953
|
-
if (expected.documentStatus !== "READY") throw new ValidationError(`pdf.documentStatus ${expected.documentStatus} \u2260 READY`);
|
|
954
|
-
want = expected.outSha256;
|
|
955
|
-
}
|
|
956
|
-
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");
|
|
957
|
-
const bytes = await this.document(sessionId);
|
|
958
|
-
const got = (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex");
|
|
959
|
-
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");
|
|
960
|
-
return bytes;
|
|
961
|
-
}
|
|
962
|
-
};
|
|
963
|
-
|
|
964
969
|
// src/organization.ts
|
|
965
970
|
var OrganizationApi = class {
|
|
966
971
|
constructor(http) {
|
|
@@ -976,7 +981,7 @@ var OrganizationApi = class {
|
|
|
976
981
|
};
|
|
977
982
|
|
|
978
983
|
// src/person.ts
|
|
979
|
-
function
|
|
984
|
+
function rec3(v) {
|
|
980
985
|
return v && typeof v === "object" ? v : {};
|
|
981
986
|
}
|
|
982
987
|
var PersonApi = class {
|
|
@@ -986,7 +991,7 @@ var PersonApi = class {
|
|
|
986
991
|
http;
|
|
987
992
|
async get(path) {
|
|
988
993
|
const raw = await this.http.get(path);
|
|
989
|
-
return { ...
|
|
994
|
+
return { ...rec3(raw), raw };
|
|
990
995
|
}
|
|
991
996
|
/** Иргэний бүх гэрчилгээ + статусын тоолол. */
|
|
992
997
|
certificates(personEtsi) {
|
|
@@ -1016,10 +1021,10 @@ var PersonApi = class {
|
|
|
1016
1021
|
};
|
|
1017
1022
|
|
|
1018
1023
|
// src/token.ts
|
|
1019
|
-
var
|
|
1024
|
+
var import_node_crypto3 = require("crypto");
|
|
1020
1025
|
|
|
1021
1026
|
// src/subject.ts
|
|
1022
|
-
var
|
|
1027
|
+
var import_node_crypto2 = require("crypto");
|
|
1023
1028
|
var OIDS = {
|
|
1024
1029
|
"2.5.4.42": "givenName",
|
|
1025
1030
|
"2.5.4.4": "surname",
|
|
@@ -1084,7 +1089,7 @@ function stringOf(buf, t) {
|
|
|
1084
1089
|
}
|
|
1085
1090
|
}
|
|
1086
1091
|
function parseSubject(cert) {
|
|
1087
|
-
const der = cert instanceof
|
|
1092
|
+
const der = cert instanceof import_node_crypto2.X509Certificate ? new Uint8Array(cert.raw) : typeof cert === "string" ? new Uint8Array(Buffer.from(cert, "base64")) : cert;
|
|
1088
1093
|
const top = tlv(der, 0, der.length);
|
|
1089
1094
|
if (top.tag !== 48) throw new ValidationError("subject: Certificate SEQUENCE \u0431\u0438\u0448");
|
|
1090
1095
|
const tbs = tlv(der, top.start, top.end);
|
|
@@ -1157,12 +1162,12 @@ function certExtensions(der) {
|
|
|
1157
1162
|
}
|
|
1158
1163
|
|
|
1159
1164
|
// src/token.ts
|
|
1160
|
-
function
|
|
1165
|
+
function rec4(v) {
|
|
1161
1166
|
return v && typeof v === "object" ? v : {};
|
|
1162
1167
|
}
|
|
1163
1168
|
function hexSha256Equal(hex, data) {
|
|
1164
1169
|
if (typeof hex !== "string" || !/^[0-9a-fA-F]{64}$/.test(hex)) return false;
|
|
1165
|
-
return (0,
|
|
1170
|
+
return (0, import_node_crypto3.timingSafeEqual)((0, import_node_crypto3.createHash)("sha256").update(data).digest(), Buffer.from(hex, "hex"));
|
|
1166
1171
|
}
|
|
1167
1172
|
var TOKEN_RESULT_TIMEOUT_MS = 3e4;
|
|
1168
1173
|
var TokenApi = class {
|
|
@@ -1191,11 +1196,11 @@ var TokenApi = class {
|
|
|
1191
1196
|
flow: "notification"
|
|
1192
1197
|
};
|
|
1193
1198
|
if (input.tokenLabel) body.tokenLabel = input.tokenLabel;
|
|
1194
|
-
const r =
|
|
1199
|
+
const r = rec4(await this.http.post(routePath(RP_ROUTES.tokenCertificate), body));
|
|
1195
1200
|
if (!hexSha256Equal(r.csrAuthSha256, input.csrAuth) || !hexSha256Equal(r.csrSignSha256, input.csrSign)) {
|
|
1196
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");
|
|
1197
1202
|
}
|
|
1198
|
-
const vc =
|
|
1203
|
+
const vc = rec4(r.vc).value ?? (typeof r.vc === "string" ? r.vc : void 0);
|
|
1199
1204
|
return {
|
|
1200
1205
|
sessionId: String(r.sessionID ?? ""),
|
|
1201
1206
|
vc: vc != null ? String(vc) : null,
|
|
@@ -1210,7 +1215,7 @@ var TokenApi = class {
|
|
|
1210
1215
|
*/
|
|
1211
1216
|
async certificateResult(sessionId) {
|
|
1212
1217
|
const raw = await this.http.get(routePath(RP_ROUTES.tokenCertificateResult, sessionId), TOKEN_RESULT_TIMEOUT_MS);
|
|
1213
|
-
const r =
|
|
1218
|
+
const r = rec4(raw);
|
|
1214
1219
|
const item = (v) => v && typeof v === "object" ? v : null;
|
|
1215
1220
|
return {
|
|
1216
1221
|
status: String(r.status ?? ""),
|
|
@@ -1226,7 +1231,7 @@ var TokenApi = class {
|
|
|
1226
1231
|
}
|
|
1227
1232
|
/** POST /token/auth/challenge — энэ RP-д холбогдсон нэг удаагийн challenge (2 мин). */
|
|
1228
1233
|
async authChallenge() {
|
|
1229
|
-
const r =
|
|
1234
|
+
const r = rec4(
|
|
1230
1235
|
await this.http.post(routePath(RP_ROUTES_UNDISCOVERED.tokenAuthChallenge), {
|
|
1231
1236
|
relyingPartyUUID: this.creds.rpUUID,
|
|
1232
1237
|
relyingPartyName: this.creds.rpName
|
|
@@ -1247,7 +1252,7 @@ var TokenApi = class {
|
|
|
1247
1252
|
async authVerify(challengeId, certDer, signature) {
|
|
1248
1253
|
let serial;
|
|
1249
1254
|
try {
|
|
1250
|
-
serial = parseSubject(new
|
|
1255
|
+
serial = parseSubject(new import_node_crypto3.X509Certificate(certDer)).serialNumber;
|
|
1251
1256
|
} catch {
|
|
1252
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");
|
|
1253
1258
|
}
|
|
@@ -1257,12 +1262,12 @@ var TokenApi = class {
|
|
|
1257
1262
|
certificate: Buffer.from(certDer).toString("base64"),
|
|
1258
1263
|
signature: Buffer.from(signature).toString("base64")
|
|
1259
1264
|
});
|
|
1260
|
-
const r =
|
|
1261
|
-
if (
|
|
1262
|
-
const id =
|
|
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);
|
|
1263
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");
|
|
1264
|
-
const org =
|
|
1265
|
-
const cert =
|
|
1269
|
+
const org = rec4(r.organization);
|
|
1270
|
+
const cert = rec4(r.cert);
|
|
1266
1271
|
return {
|
|
1267
1272
|
identity: { etsi: String(id.etsi), givenName: String(id.givenName ?? ""), surname: String(id.surname ?? "") },
|
|
1268
1273
|
serialHex: String(cert.serialHex ?? ""),
|
|
@@ -1278,7 +1283,7 @@ function csrSpki(csr) {
|
|
|
1278
1283
|
const version = tlv(csr, info.start, info.end);
|
|
1279
1284
|
const subject = tlv(csr, version.end, info.end);
|
|
1280
1285
|
const spki = tlv(csr, subject.end, info.end);
|
|
1281
|
-
return (0,
|
|
1286
|
+
return (0, import_node_crypto3.createPublicKey)({ key: Buffer.from(csr.subarray(subject.end, spki.end)), format: "der", type: "spki" }).export({
|
|
1282
1287
|
format: "der",
|
|
1283
1288
|
type: "spki"
|
|
1284
1289
|
});
|
|
@@ -1297,7 +1302,7 @@ function verifyTokenCertificates(result, csrAuth, csrSign) {
|
|
|
1297
1302
|
}
|
|
1298
1303
|
let got;
|
|
1299
1304
|
try {
|
|
1300
|
-
got = new
|
|
1305
|
+
got = new import_node_crypto3.X509Certificate(Buffer.from(item.certificate, "base64")).publicKey.export({ format: "der", type: "spki" });
|
|
1301
1306
|
} catch {
|
|
1302
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`);
|
|
1303
1308
|
}
|
|
@@ -1310,13 +1315,13 @@ function tokenAuthDigest(rpUUID, challengeB64) {
|
|
|
1310
1315
|
}
|
|
1311
1316
|
const ch = Buffer.from(challengeB64, "base64");
|
|
1312
1317
|
if (ch.length !== 32 || ch.toString("base64") !== challengeB64) throw new Error("token auth: challenge 32 \u0431\u0430\u0439\u0442 base64 \u0431\u0438\u0448");
|
|
1313
|
-
return (0,
|
|
1318
|
+
return (0, import_node_crypto3.createHash)("sha256").update("eID-TOKEN-AUTH", "ascii").update(rpUUID, "ascii").update(ch).digest();
|
|
1314
1319
|
}
|
|
1315
1320
|
function tokenLimitOf(err) {
|
|
1316
1321
|
if (!(err instanceof ApiError) || err.code !== "TOKEN_LIMIT_REACHED") return void 0;
|
|
1317
1322
|
let b;
|
|
1318
1323
|
try {
|
|
1319
|
-
b =
|
|
1324
|
+
b = rec4(JSON.parse(err.body));
|
|
1320
1325
|
} catch {
|
|
1321
1326
|
return void 0;
|
|
1322
1327
|
}
|
|
@@ -1329,7 +1334,7 @@ function tokenLimitOf(err) {
|
|
|
1329
1334
|
}
|
|
1330
1335
|
|
|
1331
1336
|
// src/validator.ts
|
|
1332
|
-
var
|
|
1337
|
+
var import_node_crypto4 = require("crypto");
|
|
1333
1338
|
var CITIZEN_ISSUING_CA_SKI = "b8a37f2c64ad677912db7d9e34f246358a2fb909";
|
|
1334
1339
|
var ResponseValidator = class {
|
|
1335
1340
|
anchors;
|
|
@@ -1343,8 +1348,8 @@ var ResponseValidator = class {
|
|
|
1343
1348
|
checkKeyUsage;
|
|
1344
1349
|
schemeName;
|
|
1345
1350
|
constructor(trust = {}) {
|
|
1346
|
-
this.anchors = (trust.trustAnchorsPem ?? []).map((p) => new
|
|
1347
|
-
this.intermediates = (trust.intermediatesPem ?? []).map((p) => new
|
|
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));
|
|
1348
1353
|
this.requiredLevel = trust.requiredLevel ?? "QUALIFIED";
|
|
1349
1354
|
this.skew = trust.clockSkewMs ?? 0;
|
|
1350
1355
|
this.allowUntrusted = trust.allowUntrusted ?? false;
|
|
@@ -1525,7 +1530,7 @@ var ResponseValidator = class {
|
|
|
1525
1530
|
leafOf(result, protocol) {
|
|
1526
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");
|
|
1527
1532
|
if (!result.documentNumber) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 documentNumber \u0430\u043B\u0433\u0430");
|
|
1528
|
-
const leaf = new
|
|
1533
|
+
const leaf = new import_node_crypto4.X509Certificate(Buffer.from(result.certificateDerB64, "base64"));
|
|
1529
1534
|
this.checkValidity(leaf);
|
|
1530
1535
|
this.checkLevel(String(result.certificateLevel ?? ""));
|
|
1531
1536
|
this.checkPurpose(leaf, protocol);
|
|
@@ -1659,16 +1664,16 @@ function signedCallbackUrl(fromResponse, rpCallback, flowType) {
|
|
|
1659
1664
|
return fromResponse;
|
|
1660
1665
|
}
|
|
1661
1666
|
function userChallengeOf(verifier) {
|
|
1662
|
-
return (0,
|
|
1667
|
+
return (0, import_node_crypto4.createHash)("sha256").update(verifier, "utf8").digest("base64url");
|
|
1663
1668
|
}
|
|
1664
1669
|
function verifyPayloadSignature(payload, sigB64, key, alg, hash) {
|
|
1665
1670
|
const sig = Buffer.from(sigB64, "base64");
|
|
1666
1671
|
const h = nodeHash(hash);
|
|
1667
1672
|
if (alg === "rsassa-pss") {
|
|
1668
|
-
return (0,
|
|
1673
|
+
return (0, import_node_crypto4.verify)(h, payload, { key, padding: import_node_crypto4.constants.RSA_PKCS1_PSS_PADDING, saltLength: hashLen(hash) }, sig);
|
|
1669
1674
|
}
|
|
1670
|
-
if (alg === "ecdsa-with-SHA256") return (0,
|
|
1671
|
-
return (0,
|
|
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);
|
|
1672
1677
|
}
|
|
1673
1678
|
function verifyPrehashedSignature(digest, sigB64, key, alg, hash) {
|
|
1674
1679
|
const sig = Buffer.from(sigB64, "base64");
|
|
@@ -1677,7 +1682,7 @@ function verifyPrehashedSignature(digest, sigB64, key, alg, hash) {
|
|
|
1677
1682
|
if (!modBits || sig.length !== Math.ceil(modBits / 8)) return false;
|
|
1678
1683
|
let em;
|
|
1679
1684
|
try {
|
|
1680
|
-
em = (0,
|
|
1685
|
+
em = (0, import_node_crypto4.publicDecrypt)({ key, padding: import_node_crypto4.constants.RSA_NO_PADDING }, sig);
|
|
1681
1686
|
} catch {
|
|
1682
1687
|
return false;
|
|
1683
1688
|
}
|
|
@@ -1761,7 +1766,7 @@ function mgf1(hash, seed, length) {
|
|
|
1761
1766
|
for (let i = 0, n = 0; n < length; i++, n += hashLen(hash)) {
|
|
1762
1767
|
const c = Buffer.alloc(4);
|
|
1763
1768
|
c.writeUInt32BE(i, 0);
|
|
1764
|
-
out.push((0,
|
|
1769
|
+
out.push((0, import_node_crypto4.createHash)(nodeHash(hash)).update(seed).update(c).digest());
|
|
1765
1770
|
}
|
|
1766
1771
|
return Buffer.concat(out).subarray(0, length);
|
|
1767
1772
|
}
|
|
@@ -1785,7 +1790,7 @@ function emsaPssVerify(mHash, em, emBits, hash) {
|
|
|
1785
1790
|
const psLen = emLen - hLen - sLen - 2;
|
|
1786
1791
|
if (!db.subarray(0, psLen).every((b) => b === 0) || db[psLen] !== 1) return false;
|
|
1787
1792
|
const salt = db.subarray(psLen + 1);
|
|
1788
|
-
const h2 = (0,
|
|
1793
|
+
const h2 = (0, import_node_crypto4.createHash)(nodeHash(hash)).update(Buffer.alloc(8)).update(mHash).update(salt).digest();
|
|
1789
1794
|
return h.equals(h2);
|
|
1790
1795
|
}
|
|
1791
1796
|
function keyUsageBits(cert) {
|
|
@@ -1919,7 +1924,6 @@ function resolveBaseUrl(baseUrl) {
|
|
|
1919
1924
|
var EidClient = class {
|
|
1920
1925
|
auth;
|
|
1921
1926
|
sign;
|
|
1922
|
-
pdf;
|
|
1923
1927
|
organization;
|
|
1924
1928
|
person;
|
|
1925
1929
|
token;
|
|
@@ -1941,11 +1945,6 @@ var EidClient = class {
|
|
|
1941
1945
|
const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
|
|
1942
1946
|
this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
|
|
1943
1947
|
this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level, sessionTtlMs: cfg.signSessionTtlMs });
|
|
1944
|
-
this.pdf = new PdfApi(http, cfg.credentials, {
|
|
1945
|
-
certificateLevel: level,
|
|
1946
|
-
timeoutMs: cfg.pdfTimeoutMs ?? DEFAULT_PDF_TIMEOUT_MS,
|
|
1947
|
-
maxBytes: cfg.pdfMaxBytes && cfg.pdfMaxBytes > 0 ? cfg.pdfMaxBytes : DEFAULT_PDF_MAX_BYTES
|
|
1948
|
-
});
|
|
1949
1948
|
this.organization = new OrganizationApi(http);
|
|
1950
1949
|
this.person = new PersonApi(http);
|
|
1951
1950
|
this.token = new TokenApi(http, cfg.credentials);
|
|
@@ -1953,6 +1952,14 @@ var EidClient = class {
|
|
|
1953
1952
|
this.validator = new ResponseValidator(cfg.trust ?? {});
|
|
1954
1953
|
this.rpName = cfg.credentials.rpName;
|
|
1955
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
|
+
}
|
|
1956
1963
|
};
|
|
1957
1964
|
|
|
1958
1965
|
// src/devicelink.ts
|
|
@@ -1998,7 +2005,7 @@ function openOrShowQR(session, opts) {
|
|
|
1998
2005
|
}
|
|
1999
2006
|
|
|
2000
2007
|
// src/devicelinkv3.ts
|
|
2001
|
-
var
|
|
2008
|
+
var import_node_crypto5 = require("crypto");
|
|
2002
2009
|
var TYPES = ["QR", "Web2App", "App2App"];
|
|
2003
2010
|
var PROTOCOL = { auth: "ACSP_V2", sign: "RAW_DIGEST_SIGNATURE", cert: "" };
|
|
2004
2011
|
var TOKEN_RE = /^[A-Za-z0-9_-]+$/;
|
|
@@ -2055,7 +2062,7 @@ function buildDeviceLink(p) {
|
|
|
2055
2062
|
callback,
|
|
2056
2063
|
unprotected
|
|
2057
2064
|
].join("|");
|
|
2058
|
-
const authCode = (0,
|
|
2065
|
+
const authCode = (0, import_node_crypto5.createHmac)("sha256", Buffer.from(p.sessionSecret, "base64")).update(payload, "utf8").digest("base64url");
|
|
2059
2066
|
return `${unprotected}&authCode=${authCode}`;
|
|
2060
2067
|
}
|
|
2061
2068
|
function deviceLinkInput(s, deviceLinkType, lang) {
|
|
@@ -2099,7 +2106,7 @@ function qrDeviceLinkTicker(input, onLink, opts) {
|
|
|
2099
2106
|
}
|
|
2100
2107
|
|
|
2101
2108
|
// src/login.ts
|
|
2102
|
-
var
|
|
2109
|
+
var import_node_crypto6 = require("crypto");
|
|
2103
2110
|
function memoryLoginStore() {
|
|
2104
2111
|
const m = /* @__PURE__ */ new Map();
|
|
2105
2112
|
return {
|
|
@@ -2136,7 +2143,7 @@ function forwardedClientIp(headers, trustProxy) {
|
|
|
2136
2143
|
return header(headers, "x-real-ip").trim() || void 0;
|
|
2137
2144
|
}
|
|
2138
2145
|
var ID_RE = /^(\p{L}{2}[0-9]{8}|[0-9]{8,14}|PNOMN-[0-9]{8,14})$/u;
|
|
2139
|
-
var
|
|
2146
|
+
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
2140
2147
|
var B64URL_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
2141
2148
|
var IOS_BROWSERS = [
|
|
2142
2149
|
[/CriOS/i, "googlechromes"],
|
|
@@ -2180,11 +2187,11 @@ function readCookie(h, name) {
|
|
|
2180
2187
|
}
|
|
2181
2188
|
return "";
|
|
2182
2189
|
}
|
|
2183
|
-
var sha256hex = (s) => (0,
|
|
2190
|
+
var sha256hex = (s) => (0, import_node_crypto6.createHash)("sha256").update(s).digest("hex");
|
|
2184
2191
|
function safeEqualHex(a, b) {
|
|
2185
2192
|
const x = Buffer.from(a, "hex");
|
|
2186
2193
|
const y = Buffer.from(b, "hex");
|
|
2187
|
-
return x.length === y.length && x.length > 0 && (0,
|
|
2194
|
+
return x.length === y.length && x.length > 0 && (0, import_node_crypto6.timingSafeEqual)(x, y);
|
|
2188
2195
|
}
|
|
2189
2196
|
function toIdentity(sessionId, r, v, flowType) {
|
|
2190
2197
|
const s = v.parsedSubject;
|
|
@@ -2267,7 +2274,7 @@ function createEidLogin(opts) {
|
|
|
2267
2274
|
const dot = raw.indexOf(".");
|
|
2268
2275
|
const sid = raw.slice(0, dot);
|
|
2269
2276
|
const binding = raw.slice(dot + 1);
|
|
2270
|
-
if (dot < 0 || !
|
|
2277
|
+
if (dot < 0 || !UUID_RE2.test(sid) || !B64URL_RE.test(binding)) return void 0;
|
|
2271
2278
|
const tx = await store.get(sid);
|
|
2272
2279
|
if (!tx || !safeEqualHex(tx.bindingHash, sha256hex(binding))) return void 0;
|
|
2273
2280
|
if (tx.expiresAt <= Date.now()) {
|
|
@@ -2324,7 +2331,7 @@ function createEidLogin(opts) {
|
|
|
2324
2331
|
});
|
|
2325
2332
|
}
|
|
2326
2333
|
}
|
|
2327
|
-
const binding = (0,
|
|
2334
|
+
const binding = (0, import_node_crypto6.randomBytes)(32).toString("base64url");
|
|
2328
2335
|
const full = { ...tx, bindingHash: sha256hex(binding), expiresAt: Date.now() + ttl };
|
|
2329
2336
|
await store.set(full.sessionId, full, ttl);
|
|
2330
2337
|
const setCookie = `${cookieName}=${full.sessionId}.${binding}; ${cookieAttrs(Math.ceil(ttl / 1e3))}`;
|
|
@@ -2491,9 +2498,8 @@ function createEidLogin(opts) {
|
|
|
2491
2498
|
AuthApi,
|
|
2492
2499
|
AuthenticationError,
|
|
2493
2500
|
CITIZEN_ISSUING_CA_SKI,
|
|
2501
|
+
CertChoiceInvalidError,
|
|
2494
2502
|
DEFAULT_BASE_URL,
|
|
2495
|
-
DEFAULT_PDF_MAX_BYTES,
|
|
2496
|
-
DEFAULT_PDF_TIMEOUT_MS,
|
|
2497
2503
|
DEFAULT_SIGN_SESSION_TTL_MS,
|
|
2498
2504
|
DeviceLinkError,
|
|
2499
2505
|
EidClient,
|
|
@@ -2501,11 +2507,13 @@ function createEidLogin(opts) {
|
|
|
2501
2507
|
ForbiddenError,
|
|
2502
2508
|
Http,
|
|
2503
2509
|
NetworkError,
|
|
2510
|
+
NoSigningCertificateError,
|
|
2504
2511
|
ON_BEHALF_ERROR_CODES,
|
|
2505
2512
|
OrganizationApi,
|
|
2506
|
-
|
|
2513
|
+
PDF_SIGNATURE_REGISTER_FIELDS,
|
|
2507
2514
|
PersonApi,
|
|
2508
2515
|
RETRYABLE_ERROR_CODES,
|
|
2516
|
+
RequestInvalidError,
|
|
2509
2517
|
ResponseTooLargeError,
|
|
2510
2518
|
ResponseValidator,
|
|
2511
2519
|
SessionApi,
|
|
@@ -2516,6 +2524,7 @@ function createEidLogin(opts) {
|
|
|
2516
2524
|
acspV2Digest,
|
|
2517
2525
|
buildAcspV2Payload,
|
|
2518
2526
|
buildDeviceLink,
|
|
2527
|
+
buildRegisterBody,
|
|
2519
2528
|
createEidLogin,
|
|
2520
2529
|
deviceLink,
|
|
2521
2530
|
deviceLinkInput,
|
|
@@ -2524,15 +2533,18 @@ function createEidLogin(opts) {
|
|
|
2524
2533
|
isMobileUserAgent,
|
|
2525
2534
|
memoryLoginStore,
|
|
2526
2535
|
openOrShowQR,
|
|
2536
|
+
parseExpiresAt,
|
|
2527
2537
|
parseRetryAfter,
|
|
2528
2538
|
parseSessionResult,
|
|
2529
2539
|
parseSubject,
|
|
2530
2540
|
personMatchesCertificate,
|
|
2531
2541
|
qrDeviceLinkTicker,
|
|
2532
2542
|
randomChallenge,
|
|
2543
|
+
registrationFromCompleted,
|
|
2533
2544
|
resolveBaseUrl,
|
|
2534
2545
|
sessionExpiry,
|
|
2535
2546
|
sha256Base64,
|
|
2547
|
+
startExpiry,
|
|
2536
2548
|
tokenAuthDigest,
|
|
2537
2549
|
tokenLimitOf,
|
|
2538
2550
|
userChallengeOf,
|