@e-sig/core 0.5.1 → 0.7.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/LICENSE +1 -1
- package/README.md +92 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/pem-signer.d.ts +26 -5
- package/dist/pem-signer.d.ts.map +1 -1
- package/dist/pem-signer.js +84 -13
- package/dist/pem-signer.js.map +1 -1
- package/dist/pq-cert.d.ts +76 -0
- package/dist/pq-cert.d.ts.map +1 -0
- package/dist/pq-cert.js +295 -0
- package/dist/pq-cert.js.map +1 -0
- package/dist/pq-embed.d.ts +22 -0
- package/dist/pq-embed.d.ts.map +1 -0
- package/dist/pq-embed.js +119 -0
- package/dist/pq-embed.js.map +1 -0
- package/dist/pq-lifecycle.d.ts +59 -0
- package/dist/pq-lifecycle.d.ts.map +1 -0
- package/dist/pq-lifecycle.js +55 -0
- package/dist/pq-lifecycle.js.map +1 -0
- package/dist/pq-seal.d.ts +183 -0
- package/dist/pq-seal.d.ts.map +1 -0
- package/dist/pq-seal.js +345 -0
- package/dist/pq-seal.js.map +1 -0
- package/dist/pq-verify.d.ts +99 -0
- package/dist/pq-verify.d.ts.map +1 -0
- package/dist/pq-verify.js +135 -0
- package/dist/pq-verify.js.map +1 -0
- package/dist/render-pdf.d.ts +5 -5
- package/dist/render-pdf.d.ts.map +1 -1
- package/dist/render-pdf.js +51 -17
- package/dist/render-pdf.js.map +1 -1
- package/dist/sign-document.d.ts +16 -0
- package/dist/sign-document.d.ts.map +1 -1
- package/dist/sign-document.js +10 -0
- package/dist/sign-document.js.map +1 -1
- package/dist/sign-pdf.d.ts +66 -4
- package/dist/sign-pdf.d.ts.map +1 -1
- package/dist/sign-pdf.js +51 -2
- package/dist/sign-pdf.js.map +1 -1
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vendor/placeholder-plain/LICENSE-signpdf.md +47 -0
- package/dist/vendor/placeholder-plain/createBufferPageWithAnnotation.d.ts +6 -0
- package/dist/vendor/placeholder-plain/createBufferPageWithAnnotation.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/createBufferPageWithAnnotation.js +38 -0
- package/dist/vendor/placeholder-plain/createBufferPageWithAnnotation.js.map +1 -0
- package/dist/vendor/placeholder-plain/createBufferRootWithAcroform.d.ts +6 -0
- package/dist/vendor/placeholder-plain/createBufferRootWithAcroform.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/createBufferRootWithAcroform.js +15 -0
- package/dist/vendor/placeholder-plain/createBufferRootWithAcroform.js.map +1 -0
- package/dist/vendor/placeholder-plain/createBufferTrailer.d.ts +4 -0
- package/dist/vendor/placeholder-plain/createBufferTrailer.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/createBufferTrailer.js +26 -0
- package/dist/vendor/placeholder-plain/createBufferTrailer.js.map +1 -0
- package/dist/vendor/placeholder-plain/findObject.d.ts +4 -0
- package/dist/vendor/placeholder-plain/findObject.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/findObject.js +18 -0
- package/dist/vendor/placeholder-plain/findObject.js.map +1 -0
- package/dist/vendor/placeholder-plain/getIndexFromRef.d.ts +4 -0
- package/dist/vendor/placeholder-plain/getIndexFromRef.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/getIndexFromRef.js +13 -0
- package/dist/vendor/placeholder-plain/getIndexFromRef.js.map +1 -0
- package/dist/vendor/placeholder-plain/getPageRef.d.ts +6 -0
- package/dist/vendor/placeholder-plain/getPageRef.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/getPageRef.js +18 -0
- package/dist/vendor/placeholder-plain/getPageRef.js.map +1 -0
- package/dist/vendor/placeholder-plain/getPagesDictionaryRef.d.ts +3 -0
- package/dist/vendor/placeholder-plain/getPagesDictionaryRef.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/getPagesDictionaryRef.js +12 -0
- package/dist/vendor/placeholder-plain/getPagesDictionaryRef.js.map +1 -0
- package/dist/vendor/placeholder-plain/index.d.ts +2 -0
- package/dist/vendor/placeholder-plain/index.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/index.js +4 -0
- package/dist/vendor/placeholder-plain/index.js.map +1 -0
- package/dist/vendor/placeholder-plain/pdfObject.d.ts +13 -0
- package/dist/vendor/placeholder-plain/pdfObject.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/pdfObject.js +142 -0
- package/dist/vendor/placeholder-plain/pdfObject.js.map +1 -0
- package/dist/vendor/placeholder-plain/pdfkitAddPlaceholder.d.ts +49 -0
- package/dist/vendor/placeholder-plain/pdfkitAddPlaceholder.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/pdfkitAddPlaceholder.js +115 -0
- package/dist/vendor/placeholder-plain/pdfkitAddPlaceholder.js.map +1 -0
- package/dist/vendor/placeholder-plain/plainAddPlaceholder.d.ts +41 -0
- package/dist/vendor/placeholder-plain/plainAddPlaceholder.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/plainAddPlaceholder.js +95 -0
- package/dist/vendor/placeholder-plain/plainAddPlaceholder.js.map +1 -0
- package/dist/vendor/placeholder-plain/readPdf.d.ts +20 -0
- package/dist/vendor/placeholder-plain/readPdf.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/readPdf.js +47 -0
- package/dist/vendor/placeholder-plain/readPdf.js.map +1 -0
- package/dist/vendor/placeholder-plain/readRefTable.d.ts +17 -0
- package/dist/vendor/placeholder-plain/readRefTable.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/readRefTable.js +84 -0
- package/dist/vendor/placeholder-plain/readRefTable.js.map +1 -0
- package/dist/vendor/placeholder-plain/xrefToRefMap.d.ts +3 -0
- package/dist/vendor/placeholder-plain/xrefToRefMap.d.ts.map +1 -0
- package/dist/vendor/placeholder-plain/xrefToRefMap.js +38 -0
- package/dist/vendor/placeholder-plain/xrefToRefMap.js.map +1 -0
- package/package.json +10 -8
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// pq-verify.ts
|
|
2
|
+
//
|
|
3
|
+
// Verify the post-quantum hybrid seal embedded in a signed PDF, and the combined
|
|
4
|
+
// classical + post-quantum trust result — the "two-line" answer the product
|
|
5
|
+
// pitch leans on:
|
|
6
|
+
//
|
|
7
|
+
// classical (PAdES / PKCS#7 RSA): VALID — every PDF reader, incl. Acrobat
|
|
8
|
+
// post-quantum (ML-DSA-65, FIPS 204): VALID — quantum-resistant
|
|
9
|
+
//
|
|
10
|
+
// The seal binds to the document by covering SHA-256 of the first `coveredBytes`
|
|
11
|
+
// bytes (P0, the pre-seal PDF). Because the seal is embedded append-only and the
|
|
12
|
+
// RSA /ByteRange signature is applied on top, tampering with the document breaks
|
|
13
|
+
// BOTH the classical signature AND the post-quantum digest.
|
|
14
|
+
import crypto from "node:crypto";
|
|
15
|
+
import { verifyPdfSignature } from "./verify-pdf.js";
|
|
16
|
+
import { verifyPqSealSignatures } from "./pq-seal.js";
|
|
17
|
+
import { extractPqSeal } from "./pq-embed.js";
|
|
18
|
+
import { certMatchesPqSeal } from "./pq-cert.js";
|
|
19
|
+
/**
|
|
20
|
+
* Verify the embedded post-quantum seal against the document bytes. Never throws;
|
|
21
|
+
* a missing or malformed seal returns `{ present:false, ok:false }`. Pass
|
|
22
|
+
* `expectedMldsa65Fpr` to also pin the signer identity.
|
|
23
|
+
*/
|
|
24
|
+
export function verifyPqSeal(pdf, opts = {}) {
|
|
25
|
+
const failures = [];
|
|
26
|
+
const seal = extractPqSeal(pdf);
|
|
27
|
+
if (!seal) {
|
|
28
|
+
return { present: false, ok: false, failures: ["no post-quantum seal found"] };
|
|
29
|
+
}
|
|
30
|
+
const verdict = {
|
|
31
|
+
present: true,
|
|
32
|
+
ok: false,
|
|
33
|
+
alg: seal.alg,
|
|
34
|
+
keyId: seal.keyId,
|
|
35
|
+
mldsa65Fpr: seal.keys?.mldsa65Fpr,
|
|
36
|
+
signedAt: seal.signedAt,
|
|
37
|
+
coveredBytes: seal.coveredBytes,
|
|
38
|
+
uuaid: seal.uuaid,
|
|
39
|
+
failures,
|
|
40
|
+
};
|
|
41
|
+
// (1) Document binding: the seal must cover a real, in-bounds prefix.
|
|
42
|
+
const covered = seal.coveredBytes;
|
|
43
|
+
let digestBinds = false;
|
|
44
|
+
if (!Number.isInteger(covered) || covered <= 0 || covered > pdf.length) {
|
|
45
|
+
failures.push(`seal coveredBytes (${covered}) is out of range for a ${pdf.length}-byte file`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const recomputed = crypto.createHash("sha256").update(pdf.subarray(0, covered)).digest("hex");
|
|
49
|
+
digestBinds = recomputed === seal.digest;
|
|
50
|
+
if (!digestBinds) {
|
|
51
|
+
failures.push("post-quantum digest does not match the document — content altered");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
verdict.digestBinds = digestBinds;
|
|
55
|
+
// (2) Signatures: both classical Ed25519 and post-quantum ML-DSA-65 must verify.
|
|
56
|
+
const sigs = verifyPqSealSignatures(seal);
|
|
57
|
+
verdict.ed25519 = sigs.ed25519;
|
|
58
|
+
verdict.mldsa65 = sigs.mldsa65;
|
|
59
|
+
verdict.fingerprintOk = sigs.fingerprintOk;
|
|
60
|
+
if (!sigs.ed25519)
|
|
61
|
+
failures.push("Ed25519 seal signature invalid");
|
|
62
|
+
if (!sigs.mldsa65)
|
|
63
|
+
failures.push("ML-DSA-65 seal signature invalid");
|
|
64
|
+
if (!sigs.fingerprintOk)
|
|
65
|
+
failures.push("ML-DSA-65 fingerprint inconsistent with its public key");
|
|
66
|
+
if (!sigs.keyIdOk)
|
|
67
|
+
failures.push("seal keyId inconsistent with its public keys");
|
|
68
|
+
if (!sigs.uuaidOk)
|
|
69
|
+
failures.push("seal carries a malformed UUAID assertion");
|
|
70
|
+
// (3) Optional identity pinning (in-band TOFU assertion).
|
|
71
|
+
let identityOk = true;
|
|
72
|
+
if (opts.expectedMldsa65Fpr) {
|
|
73
|
+
identityOk = (verdict.mldsa65Fpr ?? "").toLowerCase() === opts.expectedMldsa65Fpr.toLowerCase();
|
|
74
|
+
if (!identityOk) {
|
|
75
|
+
failures.push("post-quantum signer fingerprint does not match the expected (pinned) identity");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// (3b) Optional UUAID assertion pinning (IAASO-0004). Pins what the seal
|
|
79
|
+
// CLAIMS; the uuaid -> key direction is out of band, see PqSealVerdict.uuaid.
|
|
80
|
+
if (opts.expectedUuaid) {
|
|
81
|
+
const uuaidMatches = verdict.uuaid === opts.expectedUuaid;
|
|
82
|
+
verdict.uuaidMatches = uuaidMatches;
|
|
83
|
+
if (!uuaidMatches) {
|
|
84
|
+
identityOk = false;
|
|
85
|
+
failures.push(verdict.uuaid === undefined
|
|
86
|
+
? "seal asserts no UUAID but one was expected"
|
|
87
|
+
: "seal's asserted UUAID does not match the expected (pinned) identity");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// (4) Optional X.509 identity: a self-signed ML-DSA-65 cert bound to this key.
|
|
91
|
+
if (opts.signerCert) {
|
|
92
|
+
const certOk = certMatchesPqSeal(opts.signerCert, seal);
|
|
93
|
+
verdict.certIdentityOk = certOk;
|
|
94
|
+
if (!certOk) {
|
|
95
|
+
identityOk = false;
|
|
96
|
+
failures.push("signer certificate is invalid or does not match the seal's post-quantum key");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
verdict.ok = digestBinds && sigs.ok && identityOk;
|
|
100
|
+
return verdict;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Full document verification: classical PAdES signature + post-quantum hybrid
|
|
104
|
+
* seal. Also asserts that the seal-covered region falls within the RSA-signed
|
|
105
|
+
* prefix, so the classical signature genuinely protects the seal. Pass
|
|
106
|
+
* `requirePq` to reject unsealed documents and `expectedMldsa65Fpr` to pin the
|
|
107
|
+
* post-quantum signer.
|
|
108
|
+
*/
|
|
109
|
+
export function verifyDocument(pdf, opts = {}) {
|
|
110
|
+
const classical = verifyPdfSignature(pdf);
|
|
111
|
+
const postQuantum = verifyPqSeal(pdf, {
|
|
112
|
+
expectedMldsa65Fpr: opts.expectedMldsa65Fpr,
|
|
113
|
+
signerCert: opts.signerCert,
|
|
114
|
+
expectedUuaid: opts.expectedUuaid,
|
|
115
|
+
});
|
|
116
|
+
let sealWithinSignedRegion = true;
|
|
117
|
+
if (postQuantum.present && classical.byteRange && postQuantum.coveredBytes != null) {
|
|
118
|
+
const [a, b] = classical.byteRange;
|
|
119
|
+
const contentsStart = a + b; // end of the first signed chunk = start of /Contents
|
|
120
|
+
if (postQuantum.coveredBytes > contentsStart) {
|
|
121
|
+
sealWithinSignedRegion = false;
|
|
122
|
+
postQuantum.failures.push("seal covers bytes beyond the classically-signed region (not RSA-protected)");
|
|
123
|
+
postQuantum.ok = false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (opts.requirePq && !postQuantum.present) {
|
|
127
|
+
postQuantum.failures.push("post-quantum seal required but none present");
|
|
128
|
+
}
|
|
129
|
+
const pqSatisfied = postQuantum.present
|
|
130
|
+
? postQuantum.ok && sealWithinSignedRegion
|
|
131
|
+
: !opts.requirePq;
|
|
132
|
+
const ok = classical.ok && pqSatisfied;
|
|
133
|
+
return { ok, classical, postQuantum };
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=pq-verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pq-verify.js","sourceRoot":"","sources":["../src/pq-verify.ts"],"names":[],"mappings":"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"}
|
package/dist/render-pdf.d.ts
CHANGED
|
@@ -10,11 +10,11 @@ export interface RenderHtmlToPdfOptions {
|
|
|
10
10
|
};
|
|
11
11
|
printBackground?: boolean;
|
|
12
12
|
/**
|
|
13
|
-
* Override the chromium executable path. If omitted:
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Set this when porting to an environment outside that matrix.
|
|
13
|
+
* Override the chromium executable path. If omitted, resolution order is:
|
|
14
|
+
* 1. ESIG_CHROME_PATH / PUPPETEER_EXECUTABLE_PATH / CHROME_PATH env vars
|
|
15
|
+
* 2. on Vercel / AWS Lambda: load @sparticuz/chromium
|
|
16
|
+
* 3. platform scan: common Chrome/Chromium/Edge/Brave install locations
|
|
17
|
+
* Set this (or an env var) when porting to an environment outside that matrix.
|
|
18
18
|
*/
|
|
19
19
|
executablePath?: string;
|
|
20
20
|
/** Override puppeteer launch args. Default: --no-sandbox --disable-setuid-sandbox. */
|
package/dist/render-pdf.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-pdf.d.ts","sourceRoot":"","sources":["../src/render-pdf.ts"],"names":[],"mappings":"AAMA,OAAkB,EAAgB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE3E,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"render-pdf.d.ts","sourceRoot":"","sources":["../src/render-pdf.ts"],"names":[],"mappings":"AAMA,OAAkB,EAAgB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE3E,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyED,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwCnF"}
|
package/dist/render-pdf.js
CHANGED
|
@@ -4,32 +4,66 @@
|
|
|
4
4
|
// Lambda environment to use @sparticuz/chromium, falls back to system Chrome
|
|
5
5
|
// for local dev.
|
|
6
6
|
import puppeteer from "puppeteer-core";
|
|
7
|
+
const CHROME_ENV_VARS = ["ESIG_CHROME_PATH", "PUPPETEER_EXECUTABLE_PATH", "CHROME_PATH"];
|
|
8
|
+
const CHROME_CANDIDATES = {
|
|
9
|
+
darwin: [
|
|
10
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
11
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
12
|
+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
13
|
+
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
|
14
|
+
],
|
|
15
|
+
linux: [
|
|
16
|
+
"/usr/bin/google-chrome",
|
|
17
|
+
"/usr/bin/google-chrome-stable",
|
|
18
|
+
"/usr/bin/chromium",
|
|
19
|
+
"/usr/bin/chromium-browser",
|
|
20
|
+
"/snap/bin/chromium",
|
|
21
|
+
"/usr/bin/microsoft-edge",
|
|
22
|
+
"/usr/bin/brave-browser",
|
|
23
|
+
],
|
|
24
|
+
win32: [
|
|
25
|
+
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
|
26
|
+
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
|
|
27
|
+
"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
async function isExecutable(p) {
|
|
31
|
+
try {
|
|
32
|
+
const fs = await import("node:fs");
|
|
33
|
+
// X_OK is meaningless on Windows; existence is the useful check there.
|
|
34
|
+
await fs.promises.access(p, process.platform === "win32" ? fs.constants.F_OK : fs.constants.X_OK);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
7
41
|
async function resolveExecutablePath(opts) {
|
|
8
42
|
if (opts.executablePath)
|
|
9
43
|
return opts.executablePath;
|
|
44
|
+
for (const envVar of CHROME_ENV_VARS) {
|
|
45
|
+
const p = process.env[envVar];
|
|
46
|
+
if (!p)
|
|
47
|
+
continue;
|
|
48
|
+
if (await isExecutable(p))
|
|
49
|
+
return p;
|
|
50
|
+
// An explicitly set env var pointing nowhere is a config error — fail loud
|
|
51
|
+
// rather than silently falling through to a different browser.
|
|
52
|
+
throw new Error(`renderHtmlToPdf: ${envVar}="${p}" is not an executable file.`);
|
|
53
|
+
}
|
|
10
54
|
if (process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.VERCEL_ENV) {
|
|
11
55
|
const chromium = (await import("@sparticuz/chromium")).default;
|
|
12
56
|
return await chromium.executablePath();
|
|
13
57
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
for (const p of [
|
|
18
|
-
"/usr/bin/google-chrome",
|
|
19
|
-
"/usr/bin/chromium",
|
|
20
|
-
"/usr/bin/chromium-browser",
|
|
21
|
-
"/snap/bin/chromium",
|
|
22
|
-
]) {
|
|
23
|
-
try {
|
|
24
|
-
const fs = await import("node:fs");
|
|
25
|
-
await fs.promises.access(p, fs.constants.X_OK);
|
|
58
|
+
const candidates = CHROME_CANDIDATES[process.platform] ?? [];
|
|
59
|
+
for (const p of candidates) {
|
|
60
|
+
if (await isExecutable(p))
|
|
26
61
|
return p;
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
// try next
|
|
30
|
-
}
|
|
31
62
|
}
|
|
32
|
-
throw new Error("renderHtmlToPdf: no Chrome/Chromium executable found.
|
|
63
|
+
throw new Error("renderHtmlToPdf: no Chrome/Chromium executable found. Tried:\n" +
|
|
64
|
+
candidates.map((p) => ` - ${p}`).join("\n") +
|
|
65
|
+
"\nInstall Chrome/Chromium, or point to a browser with options.executablePath " +
|
|
66
|
+
"or the ESIG_CHROME_PATH / PUPPETEER_EXECUTABLE_PATH / CHROME_PATH env var.");
|
|
33
67
|
}
|
|
34
68
|
async function resolveLaunchArgs(opts) {
|
|
35
69
|
if (opts.launchArgs)
|
package/dist/render-pdf.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-pdf.js","sourceRoot":"","sources":["../src/render-pdf.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,iBAAiB;AAEjB,OAAO,SAA6C,MAAM,gBAAgB,CAAC;AA4B3E,
|
|
1
|
+
{"version":3,"file":"render-pdf.js","sourceRoot":"","sources":["../src/render-pdf.ts"],"names":[],"mappings":"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"}
|
package/dist/sign-document.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CertStore, AuditLogStore, PdfStorageStore, EsigAuditAction } from "./adapters.js";
|
|
2
2
|
import type { TsaTransport } from "./types.js";
|
|
3
|
+
import type { PqSigningKeys } from "./pq-seal.js";
|
|
3
4
|
export interface SignDocumentInput {
|
|
4
5
|
/** Fully-rendered, signature-embedded HTML (the consumer composes this). */
|
|
5
6
|
html: string;
|
|
@@ -30,6 +31,15 @@ export interface SignDocumentInput {
|
|
|
30
31
|
signingTime?: Date;
|
|
31
32
|
/** Optional RFC-3161 timestamp transport. Provide one to upgrade to CAdES-T. */
|
|
32
33
|
tsa?: TsaTransport;
|
|
34
|
+
/**
|
|
35
|
+
* Optional post-quantum hybrid seal (Ed25519 + ML-DSA-65). Provide signing
|
|
36
|
+
* keys (e.g. from `ensureActivePqKeys` or `loadPqSigningKeys`) to embed a
|
|
37
|
+
* quantum-resistant seal covered by the classical PAdES signature.
|
|
38
|
+
*/
|
|
39
|
+
pq?: {
|
|
40
|
+
keys: PqSigningKeys;
|
|
41
|
+
signedAt?: Date;
|
|
42
|
+
};
|
|
33
43
|
/** Audit row fields. */
|
|
34
44
|
action?: EsigAuditAction | string;
|
|
35
45
|
targetTable?: string;
|
|
@@ -49,6 +59,12 @@ export interface SignDocumentResult {
|
|
|
49
59
|
/** True when an RFC-3161 token was embedded (CAdES-T); false = CAdES-B. */
|
|
50
60
|
timestamped: boolean;
|
|
51
61
|
tsaError?: string;
|
|
62
|
+
/** True when a post-quantum hybrid seal (ML-DSA-65) was embedded. */
|
|
63
|
+
pqSealed: boolean;
|
|
64
|
+
/** Seal keyId, when post-quantum sealed. */
|
|
65
|
+
pqKeyId?: string;
|
|
66
|
+
/** ML-DSA-65 public-key fingerprint, when post-quantum sealed. */
|
|
67
|
+
pqMldsa65Fpr?: string;
|
|
52
68
|
}
|
|
53
69
|
export declare function signDocument(input: SignDocumentInput): Promise<SignDocumentResult>;
|
|
54
70
|
//# sourceMappingURL=sign-document.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-document.d.ts","sourceRoot":"","sources":["../src/sign-document.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EACb,eAAe,EACf,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"sign-document.d.ts","sourceRoot":"","sources":["../src/sign-document.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EACb,eAAe,EACf,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,cAAc,CAAC,EAAE;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAE5D,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IAEnB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,aAAa,CAAC;IAC1B,OAAO,EAAE,eAAe,CAAC;IAEzB,iGAAiG;IACjG,UAAU,EAAE,MAAM,CAAC;IAEnB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,gFAAgF;IAChF,GAAG,CAAC,EAAE,YAAY,CAAC;IACnB;;;;OAIG;IACH,EAAE,CAAC,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;KAAE,CAAC;IAE9C,wBAAwB;IACxB,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC;IAClB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,kBAAkB,CAAC,CA6F7B"}
|
package/dist/sign-document.js
CHANGED
|
@@ -35,6 +35,7 @@ export async function signDocument(input) {
|
|
|
35
35
|
name: input.signer.name,
|
|
36
36
|
signingTime: signedAt,
|
|
37
37
|
tsa: input.tsa,
|
|
38
|
+
pqSeal: input.pq ? { keys: input.pq.keys, signedAt: input.pq.signedAt ?? signedAt } : undefined,
|
|
38
39
|
});
|
|
39
40
|
// 4. Persist the signed PDF (+ optional signature image).
|
|
40
41
|
const tsKey = signedAt.toISOString().replace(/[:.]/g, "-");
|
|
@@ -78,6 +79,12 @@ export async function signDocument(input) {
|
|
|
78
79
|
degraded: !!input.tsa && !r.timestamped,
|
|
79
80
|
error: r.tsaError ?? null,
|
|
80
81
|
},
|
|
82
|
+
post_quantum: {
|
|
83
|
+
sealed: r.pqSealed,
|
|
84
|
+
...(r.pqSealed
|
|
85
|
+
? { alg: "hybrid-ed25519-ml-dsa-65", key_id: r.pqKeyId, mldsa65_fpr: r.pqMldsa65Fpr }
|
|
86
|
+
: {}),
|
|
87
|
+
},
|
|
81
88
|
},
|
|
82
89
|
});
|
|
83
90
|
return {
|
|
@@ -88,6 +95,9 @@ export async function signDocument(input) {
|
|
|
88
95
|
certFingerprint: cert.cert.certFingerprint,
|
|
89
96
|
timestamped: r.timestamped,
|
|
90
97
|
tsaError: r.tsaError,
|
|
98
|
+
pqSealed: r.pqSealed,
|
|
99
|
+
pqKeyId: r.pqKeyId,
|
|
100
|
+
pqMldsa65Fpr: r.pqMldsa65Fpr,
|
|
91
101
|
};
|
|
92
102
|
}
|
|
93
103
|
//# sourceMappingURL=sign-document.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-document.js","sourceRoot":"","sources":["../src/sign-document.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,EAAE;AACF,qEAAqE;AACrE,wEAAwE;AACxE,EAAE;AACF,2EAA2E;AAC3E,oDAAoD;AACpD,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,6EAA6E;AAC7E,4EAA4E;AAE5E,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"sign-document.js","sourceRoot":"","sources":["../src/sign-document.ts"],"names":[],"mappings":"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"}
|
package/dist/sign-pdf.d.ts
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
|
-
import type { TsaTransport } from "./types.js";
|
|
1
|
+
import type { ExternalSigner, TsaTransport } from "./types.js";
|
|
2
|
+
import { type PqSigningKeys } from "./pq-seal.js";
|
|
3
|
+
/**
|
|
4
|
+
* The /SubFilter values `signPdf` can emit. Both denote a detached PKCS#7,
|
|
5
|
+
* which is what `PemSigner` produces; the legacy `adbe.pkcs7.sha1` /
|
|
6
|
+
* `adbe.x509.rsa.sha1` forms are deliberately absent because this signer
|
|
7
|
+
* cannot produce them.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SUPPORTED_SUBFILTERS: readonly ["ETSI.CAdES.detached", "adbe.pkcs7.detached"];
|
|
10
|
+
export type SubFilter = (typeof SUPPORTED_SUBFILTERS)[number];
|
|
2
11
|
export interface SignPdfInput {
|
|
3
12
|
pdf: Buffer;
|
|
4
|
-
|
|
5
|
-
|
|
13
|
+
/**
|
|
14
|
+
* PEM-encoded RSA private key (in-memory signing). Required unless
|
|
15
|
+
* `externalSigner` is provided — the two are mutually exclusive.
|
|
16
|
+
*/
|
|
17
|
+
keyPem?: string;
|
|
18
|
+
/**
|
|
19
|
+
* PEM-encoded X.509 certificate. Required with `keyPem`; with
|
|
20
|
+
* `externalSigner` it defaults to `externalSigner.certificatePem`.
|
|
21
|
+
*/
|
|
22
|
+
certPem?: string;
|
|
23
|
+
/**
|
|
24
|
+
* External signing seam (HSM / KMS): the RSA private key stays outside this
|
|
25
|
+
* process and the PKCS#7 signature is produced by
|
|
26
|
+
* `externalSigner.signRsaSha256` (sync or async — RSASSA-PKCS1-v1_5/SHA-256
|
|
27
|
+
* over raw bytes, e.g. PKCS#11 CKM_SHA256_RSA_PKCS). Mutually exclusive with
|
|
28
|
+
* `keyPem`. Output is byte-identical to the `keyPem` path for the same key.
|
|
29
|
+
* See `ExternalSigner` in types.ts; a PKCS#11 adapter ships as
|
|
30
|
+
* `@e-sig/hsm-pkcs11`.
|
|
31
|
+
*/
|
|
32
|
+
externalSigner?: ExternalSigner;
|
|
6
33
|
reason: string;
|
|
7
34
|
location: string;
|
|
8
35
|
contactInfo: string;
|
|
@@ -17,8 +44,14 @@ export interface SignPdfInput {
|
|
|
17
44
|
/**
|
|
18
45
|
* Subfilter. Default ETSI.CAdES.detached (PAdES B-B baseline). Override
|
|
19
46
|
* to adbe.pkcs7.detached for legacy Adobe Reader 9+ compatibility.
|
|
47
|
+
*
|
|
48
|
+
* Restricted to the two values this signer can actually produce — a
|
|
49
|
+
* detached PKCS#7. Anything else is rejected: /SubFilter is written into
|
|
50
|
+
* the signature dictionary as a PDF name, so an unvalidated value is a
|
|
51
|
+
* dictionary-injection vector for any caller that feeds this from user or
|
|
52
|
+
* tenant input. (Defence in depth — the serializer now escapes names too.)
|
|
20
53
|
*/
|
|
21
|
-
subFilter?:
|
|
54
|
+
subFilter?: SubFilter;
|
|
22
55
|
/**
|
|
23
56
|
* Optional RFC 3161 timestamp transport. When provided, the signature is
|
|
24
57
|
* upgraded to CAdES-T and the default `signatureLength` rises to 30720. The
|
|
@@ -33,6 +66,27 @@ export interface SignPdfInput {
|
|
|
33
66
|
* (keeps `signing-time` for backward compatibility).
|
|
34
67
|
*/
|
|
35
68
|
padesStrict?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Optional post-quantum hybrid seal (Ed25519 + ML-DSA-65, FIPS 204). When
|
|
71
|
+
* provided, a seal over SHA-256 of the pre-signature PDF is embedded FIRST (an
|
|
72
|
+
* append-only incremental update), so the classical RSA /ByteRange signature
|
|
73
|
+
* applied on top cryptographically covers it. The classical signature remains
|
|
74
|
+
* valid in every PDF reader; the seal is the quantum-resistant layer, verified
|
|
75
|
+
* by `verifyPqSeal` / `verifyDocument`.
|
|
76
|
+
*/
|
|
77
|
+
pqSeal?: {
|
|
78
|
+
/** Hybrid signing keys (see generatePqKeyBundle / loadPqSigningKeys). */
|
|
79
|
+
keys: PqSigningKeys;
|
|
80
|
+
/** Seal timestamp. Defaults to `signingTime` or now. */
|
|
81
|
+
signedAt?: Date;
|
|
82
|
+
/**
|
|
83
|
+
* Optional UUAID this signer asserts as its identity (IAASO-0004
|
|
84
|
+
* attribution). Signed into the seal, so it is immutable once embedded —
|
|
85
|
+
* but it is a claim by the seal key, not proof of identity. Omit to produce
|
|
86
|
+
* a seal byte-identical to pre-`uuaid` output.
|
|
87
|
+
*/
|
|
88
|
+
uuaid?: string;
|
|
89
|
+
};
|
|
36
90
|
}
|
|
37
91
|
export interface SignPdfResult {
|
|
38
92
|
signedPdf: Buffer;
|
|
@@ -41,6 +95,14 @@ export interface SignPdfResult {
|
|
|
41
95
|
timestamped: boolean;
|
|
42
96
|
/** Present when a non-required TSA request failed (signature is CAdES-B). */
|
|
43
97
|
tsaError?: string;
|
|
98
|
+
/** True when a post-quantum hybrid seal was embedded (and is RSA-covered). */
|
|
99
|
+
pqSealed: boolean;
|
|
100
|
+
/** Seal keyId (128-bit hex over both public keys), when sealed. */
|
|
101
|
+
pqKeyId?: string;
|
|
102
|
+
/** ML-DSA-65 public-key fingerprint (SHA-256 hex), when sealed. */
|
|
103
|
+
pqMldsa65Fpr?: string;
|
|
104
|
+
/** The UUAID asserted in the seal, when one was supplied. */
|
|
105
|
+
pqUuaid?: string;
|
|
44
106
|
}
|
|
45
107
|
export declare function signPdf(input: SignPdfInput): Promise<SignPdfResult>;
|
|
46
108
|
//# sourceMappingURL=sign-pdf.d.ts.map
|
package/dist/sign-pdf.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-pdf.d.ts","sourceRoot":"","sources":["../src/sign-pdf.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sign-pdf.d.ts","sourceRoot":"","sources":["../src/sign-pdf.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAsC,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAYtF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,yDAA0D,CAAC;AAE5F,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,YAAY,CAAC;IACnB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE;QACP,yEAAyE;QACzE,IAAI,EAAE,aAAa,CAAC;QACpB,wDAAwD;QACxD,QAAQ,CAAC,EAAE,IAAI,CAAC;QAChB;;;;;WAKG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,WAAW,EAAE,OAAO,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,QAAQ,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAsFzE"}
|
package/dist/sign-pdf.js
CHANGED
|
@@ -7,10 +7,13 @@
|
|
|
7
7
|
// TSA is supplied the default /Contents budget is raised to fit the TST + TSA
|
|
8
8
|
// cert chain, and an overflow guard rejects a result whose PKCS#7 exceeded the
|
|
9
9
|
// placeholder (which @signpdf would otherwise silently truncate).
|
|
10
|
+
import crypto from "node:crypto";
|
|
10
11
|
import { SignPdf } from "@signpdf/signpdf";
|
|
11
|
-
import { plainAddPlaceholder } from "
|
|
12
|
+
import { plainAddPlaceholder } from "./vendor/placeholder-plain/index.js";
|
|
12
13
|
import { PemSigner } from "./pem-signer.js";
|
|
13
14
|
import { verifyPdfStructure } from "./verify-pdf.js";
|
|
15
|
+
import { buildPqSeal, publicMaterialForKeys } from "./pq-seal.js";
|
|
16
|
+
import { embedPqSeal } from "./pq-embed.js";
|
|
14
17
|
/** Default /Contents budget (bytes) when no timestamp is embedded. */
|
|
15
18
|
const DEFAULT_SIGNATURE_LENGTH = 8192;
|
|
16
19
|
/**
|
|
@@ -19,12 +22,53 @@ const DEFAULT_SIGNATURE_LENGTH = 8192;
|
|
|
19
22
|
* bytes ≈ 15 KB of DER headroom.
|
|
20
23
|
*/
|
|
21
24
|
const TIMESTAMPED_SIGNATURE_LENGTH = 30720;
|
|
25
|
+
/**
|
|
26
|
+
* The /SubFilter values `signPdf` can emit. Both denote a detached PKCS#7,
|
|
27
|
+
* which is what `PemSigner` produces; the legacy `adbe.pkcs7.sha1` /
|
|
28
|
+
* `adbe.x509.rsa.sha1` forms are deliberately absent because this signer
|
|
29
|
+
* cannot produce them.
|
|
30
|
+
*/
|
|
31
|
+
export const SUPPORTED_SUBFILTERS = ["ETSI.CAdES.detached", "adbe.pkcs7.detached"];
|
|
22
32
|
const signpdfInstance = new SignPdf();
|
|
23
33
|
export async function signPdf(input) {
|
|
34
|
+
if (input.externalSigner && input.keyPem) {
|
|
35
|
+
throw new Error("signPdf: pass either keyPem or externalSigner, not both");
|
|
36
|
+
}
|
|
37
|
+
if (!input.externalSigner && !input.keyPem) {
|
|
38
|
+
throw new Error("signPdf: keyPem or externalSigner is required");
|
|
39
|
+
}
|
|
40
|
+
// Runtime check, not just a type: the attack surface is JS callers passing
|
|
41
|
+
// an unvalidated string straight through from user/tenant input.
|
|
42
|
+
if (input.subFilter !== undefined &&
|
|
43
|
+
!SUPPORTED_SUBFILTERS.includes(input.subFilter)) {
|
|
44
|
+
throw new Error(`signPdf: unsupported subFilter ${JSON.stringify(input.subFilter)} — ` +
|
|
45
|
+
`expected one of ${SUPPORTED_SUBFILTERS.join(", ")}`);
|
|
46
|
+
}
|
|
24
47
|
const budget = input.signatureLength ??
|
|
25
48
|
(input.tsa ? TIMESTAMPED_SIGNATURE_LENGTH : DEFAULT_SIGNATURE_LENGTH);
|
|
49
|
+
// Post-quantum seal FIRST (append-only), so the classical /ByteRange signature
|
|
50
|
+
// applied below cryptographically covers it. The seal signs SHA-256 of the
|
|
51
|
+
// pre-seal PDF (P0); coveredBytes = P0.length locates that region in the final
|
|
52
|
+
// file (incremental updates never rewrite prior bytes).
|
|
53
|
+
let basePdf = input.pdf;
|
|
54
|
+
let pqKeyId;
|
|
55
|
+
let pqMldsa65Fpr;
|
|
56
|
+
if (input.pqSeal) {
|
|
57
|
+
const digestHex = crypto.createHash("sha256").update(input.pdf).digest("hex");
|
|
58
|
+
const seal = buildPqSeal({
|
|
59
|
+
digestHex,
|
|
60
|
+
coveredBytes: input.pdf.length,
|
|
61
|
+
keys: input.pqSeal.keys,
|
|
62
|
+
signedAt: input.pqSeal.signedAt ?? input.signingTime,
|
|
63
|
+
uuaid: input.pqSeal.uuaid,
|
|
64
|
+
});
|
|
65
|
+
basePdf = embedPqSeal(input.pdf, seal);
|
|
66
|
+
const pub = publicMaterialForKeys(input.pqSeal.keys);
|
|
67
|
+
pqKeyId = pub.keyId;
|
|
68
|
+
pqMldsa65Fpr = pub.mldsa65Fpr;
|
|
69
|
+
}
|
|
26
70
|
const withPlaceholder = plainAddPlaceholder({
|
|
27
|
-
pdfBuffer:
|
|
71
|
+
pdfBuffer: basePdf,
|
|
28
72
|
reason: input.reason,
|
|
29
73
|
contactInfo: input.contactInfo,
|
|
30
74
|
name: input.name,
|
|
@@ -35,6 +79,7 @@ export async function signPdf(input) {
|
|
|
35
79
|
const signer = new PemSigner({
|
|
36
80
|
keyPem: input.keyPem,
|
|
37
81
|
certPem: input.certPem,
|
|
82
|
+
externalSigner: input.externalSigner,
|
|
38
83
|
tsa: input.tsa,
|
|
39
84
|
padesStrict: input.padesStrict,
|
|
40
85
|
});
|
|
@@ -55,6 +100,10 @@ export async function signPdf(input) {
|
|
|
55
100
|
placeholderBudget: budget,
|
|
56
101
|
timestamped: signer.lastTimestamped,
|
|
57
102
|
tsaError: signer.lastTsaError,
|
|
103
|
+
pqSealed: !!input.pqSeal,
|
|
104
|
+
pqKeyId,
|
|
105
|
+
pqMldsa65Fpr,
|
|
106
|
+
pqUuaid: input.pqSeal?.uuaid,
|
|
58
107
|
};
|
|
59
108
|
}
|
|
60
109
|
//# sourceMappingURL=sign-pdf.js.map
|
package/dist/sign-pdf.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-pdf.js","sourceRoot":"","sources":["../src/sign-pdf.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,uEAAuE;AACvE,uEAAuE;AACvE,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,+EAA+E;AAC/E,kEAAkE;AAElE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"sign-pdf.js","sourceRoot":"","sources":["../src/sign-pdf.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -24,6 +24,42 @@ export interface SignedPdfMetadata {
|
|
|
24
24
|
/** When the signature was applied. Defaults to now. */
|
|
25
25
|
signingTime?: Date;
|
|
26
26
|
}
|
|
27
|
+
/** RSA key sizes supported by the {@link ExternalSigner} seam. */
|
|
28
|
+
export type ExternalSignerKeyType = "rsa-2048" | "rsa-3072" | "rsa-4096";
|
|
29
|
+
/**
|
|
30
|
+
* Caller-injected signing seam — lets the RSA private key live OUTSIDE this
|
|
31
|
+
* process (HSM via PKCS#11, KMS, remote signing service…). The key material
|
|
32
|
+
* never touches @e-sig/core; only the certificate (public) and a sign callback
|
|
33
|
+
* are supplied. Pass it to `signPdf({ externalSigner })` or
|
|
34
|
+
* `new PemSigner({ externalSigner })` in place of `keyPem`.
|
|
35
|
+
*
|
|
36
|
+
* Design note (why a single raw-bytes callback is sufficient): the PKCS#7
|
|
37
|
+
* signature that ends up in the PDF is ALWAYS recomputed by `PemSigner` over
|
|
38
|
+
* the final signed-attributes SET (after the ESS signing-certificate-v2 splice)
|
|
39
|
+
* — node-forge's internal sync signing pass is discarded. That recomputation
|
|
40
|
+
* happens in our own async code with the exact to-be-signed bytes in hand, so
|
|
41
|
+
* the external signer is invoked exactly once, may be async (network HSMs),
|
|
42
|
+
* and needs no node-forge shim.
|
|
43
|
+
*
|
|
44
|
+
* A PKCS#11 reference adapter ships as `@e-sig/hsm-pkcs11`.
|
|
45
|
+
*/
|
|
46
|
+
export interface ExternalSigner {
|
|
47
|
+
/** RSA modulus size of the external key. Validated against `certificatePem`. */
|
|
48
|
+
keyType: ExternalSignerKeyType;
|
|
49
|
+
/**
|
|
50
|
+
* PEM-encoded X.509 certificate whose public key matches the external
|
|
51
|
+
* private key. Embedded in the PKCS#7 and used for structural validation.
|
|
52
|
+
*/
|
|
53
|
+
certificatePem: string;
|
|
54
|
+
/**
|
|
55
|
+
* Sign `data` with RSASSA-PKCS1-v1_5 / SHA-256 — the signer hashes the raw
|
|
56
|
+
* bytes itself (PKCS#11 mechanism CKM_SHA256_RSA_PKCS does exactly this).
|
|
57
|
+
* This is byte-identical to what node-forge produces with `key.sign(md)` for
|
|
58
|
+
* the same key. Must return the raw signature, exactly modulus-sized
|
|
59
|
+
* (256/384/512 bytes for rsa-2048/3072/4096). Sync or async.
|
|
60
|
+
*/
|
|
61
|
+
signRsaSha256(data: Uint8Array): Uint8Array | Promise<Uint8Array>;
|
|
62
|
+
}
|
|
27
63
|
/**
|
|
28
64
|
* Caller-injected RFC 3161 timestamp transport.
|
|
29
65
|
*
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,MAAM;IACrB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,CAAC,WAAW,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACxD;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,MAAM;IACrB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB;AAED,kEAAkE;AAClE,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;AAEzE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,cAAc;IAC7B,gFAAgF;IAChF,OAAO,EAAE,qBAAqB,CAAC;IAC/B;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACnE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,CAAC,WAAW,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACxD;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
|