@e-sig/core 0.4.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/CONSUMING.md +97 -0
- package/LICENSE +21 -0
- package/README.md +298 -0
- package/dist/adapters.d.ts +79 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +10 -0
- package/dist/adapters.js.map +1 -0
- package/dist/cert-issuer.d.ts +47 -0
- package/dist/cert-issuer.d.ts.map +1 -0
- package/dist/cert-issuer.js +132 -0
- package/dist/cert-issuer.js.map +1 -0
- package/dist/cert-lifecycle.d.ts +16 -0
- package/dist/cert-lifecycle.d.ts.map +1 -0
- package/dist/cert-lifecycle.js +30 -0
- package/dist/cert-lifecycle.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/pem-signer.d.ts +42 -0
- package/dist/pem-signer.d.ts.map +1 -0
- package/dist/pem-signer.js +276 -0
- package/dist/pem-signer.js.map +1 -0
- package/dist/render-pdf.d.ts +33 -0
- package/dist/render-pdf.d.ts.map +1 -0
- package/dist/render-pdf.js +86 -0
- package/dist/render-pdf.js.map +1 -0
- package/dist/sign-document.d.ts +54 -0
- package/dist/sign-document.d.ts.map +1 -0
- package/dist/sign-document.js +93 -0
- package/dist/sign-document.js.map +1 -0
- package/dist/sign-pdf.d.ts +46 -0
- package/dist/sign-pdf.d.ts.map +1 -0
- package/dist/sign-pdf.js +60 -0
- package/dist/sign-pdf.js.map +1 -0
- package/dist/signature-block.d.ts +23 -0
- package/dist/signature-block.d.ts.map +1 -0
- package/dist/signature-block.js +63 -0
- package/dist/signature-block.js.map +1 -0
- package/dist/timestamp.d.ts +59 -0
- package/dist/timestamp.d.ts.map +1 -0
- package/dist/timestamp.js +278 -0
- package/dist/timestamp.js.map +1 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/verify-pdf.d.ts +33 -0
- package/dist/verify-pdf.d.ts.map +1 -0
- package/dist/verify-pdf.js +338 -0
- package/dist/verify-pdf.js.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/lib/integrations/esig/core/cert-issuer.ts
|
|
2
|
+
//
|
|
3
|
+
// Portable self-signed cert issuance. Project-agnostic — given a subject name
|
|
4
|
+
// (e.g. organization name), produces an RSA-2048 X.509 + PEM bundle ready
|
|
5
|
+
// to feed into PemSigner.
|
|
6
|
+
//
|
|
7
|
+
// Includes app-side AES-256-GCM key wrapping so callers can persist keys at
|
|
8
|
+
// rest without leaning on pgsodium / KMS / Vault. The wrapped blob is opaque;
|
|
9
|
+
// only callers that know the passphrase can unwrap.
|
|
10
|
+
import forge from "node-forge";
|
|
11
|
+
import crypto from "node:crypto";
|
|
12
|
+
const ENC_VERSION = "v1"; // scheme version prefix for future rotation
|
|
13
|
+
const DEFAULT_CERT_VALIDITY_DAYS = 365;
|
|
14
|
+
// PBKDF: scrypt N=2^15 ≈ 32MB / 100ms on modern CPU. Adequate at-rest
|
|
15
|
+
// derivation when passphrase is a high-entropy 24+ char env var.
|
|
16
|
+
const SCRYPT_N = 1 << 15;
|
|
17
|
+
const SCRYPT_R = 8;
|
|
18
|
+
const SCRYPT_P = 1;
|
|
19
|
+
const KEY_LEN = 32;
|
|
20
|
+
const IV_LEN = 12;
|
|
21
|
+
const SALT_LEN = 16;
|
|
22
|
+
const MIN_PASSPHRASE_LEN = 24;
|
|
23
|
+
/**
|
|
24
|
+
* Generate a self-signed RSA-2048 X.509 certificate suitable for PDF e-signing.
|
|
25
|
+
*
|
|
26
|
+
* ASCII-only subject — node-forge's `certificateFromPem` mis-counts byte length
|
|
27
|
+
* for non-ASCII values when round-tripping (concrete bug: em-dash in OU breaks
|
|
28
|
+
* parsing with "Too few bytes to parse DER"). The guard here prevents that.
|
|
29
|
+
*/
|
|
30
|
+
export function generateSelfSignedCert(opts) {
|
|
31
|
+
const { subjectName, validityDays = DEFAULT_CERT_VALIDITY_DAYS, commonNamePrefix = "E-sig" } = opts;
|
|
32
|
+
if (!/^[\x20-\x7e]+$/.test(subjectName)) {
|
|
33
|
+
throw new Error(`subjectName "${subjectName}" contains non-ASCII characters — would break node-forge cert round-trip`);
|
|
34
|
+
}
|
|
35
|
+
const keys = forge.pki.rsa.generateKeyPair(2048);
|
|
36
|
+
const cert = forge.pki.createCertificate();
|
|
37
|
+
cert.publicKey = keys.publicKey;
|
|
38
|
+
// Serial: 128 bits of CSPRNG entropy (RFC 5280 §4.1.2.2 — unique, unpredictable,
|
|
39
|
+
// ≤20 octets). Clear the top bit so the DER INTEGER is positive without a
|
|
40
|
+
// leading zero; force a nonzero leading byte so it isn't short-encoded.
|
|
41
|
+
const serialBytes = crypto.randomBytes(16);
|
|
42
|
+
serialBytes[0] = (serialBytes[0] & 0x7f) | 0x40;
|
|
43
|
+
cert.serialNumber = serialBytes.toString("hex");
|
|
44
|
+
const now = new Date();
|
|
45
|
+
cert.validity.notBefore = now;
|
|
46
|
+
cert.validity.notAfter = new Date(now.getTime() + validityDays * 86400_000);
|
|
47
|
+
const subject = [
|
|
48
|
+
{ name: "commonName", value: `${commonNamePrefix} (${subjectName})` },
|
|
49
|
+
{ name: "organizationName", value: subjectName },
|
|
50
|
+
{ name: "organizationalUnitName", value: "E-signature" },
|
|
51
|
+
{ name: "countryName", value: "US" },
|
|
52
|
+
];
|
|
53
|
+
cert.setSubject(subject);
|
|
54
|
+
cert.setIssuer(subject);
|
|
55
|
+
cert.setExtensions(opts.extensions ?? [
|
|
56
|
+
{ name: "basicConstraints", cA: false },
|
|
57
|
+
// digitalSignature + nonRepudiation (contentCommitment) — the key usages
|
|
58
|
+
// for a document-signing end-entity cert.
|
|
59
|
+
{ name: "keyUsage", digitalSignature: true, nonRepudiation: true },
|
|
60
|
+
// Document signing → emailProtection (S/MIME/document). clientAuth (TLS
|
|
61
|
+
// client) was semantically wrong for a signing cert and is removed.
|
|
62
|
+
{ name: "extKeyUsage", emailProtection: true },
|
|
63
|
+
// Subject Key Identifier improves cert hygiene / chain building.
|
|
64
|
+
{ name: "subjectKeyIdentifier" },
|
|
65
|
+
]);
|
|
66
|
+
cert.sign(keys.privateKey, forge.md.sha256.create());
|
|
67
|
+
const certPem = forge.pki.certificateToPem(cert);
|
|
68
|
+
const keyPem = forge.pki.privateKeyToPem(keys.privateKey);
|
|
69
|
+
const der = forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes();
|
|
70
|
+
const fingerprint = crypto.createHash("sha256").update(Buffer.from(der, "binary")).digest("hex");
|
|
71
|
+
return {
|
|
72
|
+
keyPem,
|
|
73
|
+
certPem,
|
|
74
|
+
fingerprint,
|
|
75
|
+
notBefore: cert.validity.notBefore,
|
|
76
|
+
notAfter: cert.validity.notAfter,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// ---------- App-side key wrapping ----------
|
|
80
|
+
function deriveKey(passphrase, salt) {
|
|
81
|
+
return crypto.scryptSync(passphrase, salt, KEY_LEN, {
|
|
82
|
+
N: SCRYPT_N,
|
|
83
|
+
r: SCRYPT_R,
|
|
84
|
+
p: SCRYPT_P,
|
|
85
|
+
maxmem: 64 * 1024 * 1024,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* AES-256-GCM-encrypt a PEM-encoded private key for at-rest persistence.
|
|
90
|
+
* Layout: version(2) | salt(16) | iv(12) | authTag(16) | ciphertext.
|
|
91
|
+
*
|
|
92
|
+
* @param keyPem PEM-encoded private key string.
|
|
93
|
+
* @param passphrase ≥24 char high-entropy secret. Derived via scrypt with
|
|
94
|
+
* per-call random salt.
|
|
95
|
+
*/
|
|
96
|
+
export function encryptKeyPem(keyPem, passphrase) {
|
|
97
|
+
if (!passphrase || passphrase.length < MIN_PASSPHRASE_LEN) {
|
|
98
|
+
throw new Error(`encryptKeyPem: passphrase must be ≥${MIN_PASSPHRASE_LEN} chars`);
|
|
99
|
+
}
|
|
100
|
+
const salt = crypto.randomBytes(SALT_LEN);
|
|
101
|
+
const iv = crypto.randomBytes(IV_LEN);
|
|
102
|
+
const key = deriveKey(passphrase, salt);
|
|
103
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
104
|
+
const ciphertext = Buffer.concat([cipher.update(keyPem, "utf8"), cipher.final()]);
|
|
105
|
+
const authTag = cipher.getAuthTag();
|
|
106
|
+
return Buffer.concat([Buffer.from(ENC_VERSION, "utf8"), salt, iv, authTag, ciphertext]);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Inverse of encryptKeyPem. Throws on:
|
|
110
|
+
* - unknown version prefix (future-compat detection)
|
|
111
|
+
* - wrong passphrase (AES-GCM auth-tag mismatch surfaces as error)
|
|
112
|
+
* - tampered ciphertext (same as above)
|
|
113
|
+
*/
|
|
114
|
+
export function decryptKeyPem(blob, passphrase) {
|
|
115
|
+
if (!passphrase || passphrase.length < MIN_PASSPHRASE_LEN) {
|
|
116
|
+
throw new Error(`decryptKeyPem: passphrase must be ≥${MIN_PASSPHRASE_LEN} chars`);
|
|
117
|
+
}
|
|
118
|
+
const buf = Buffer.from(blob);
|
|
119
|
+
const version = buf.slice(0, 2).toString("utf8");
|
|
120
|
+
if (version !== ENC_VERSION) {
|
|
121
|
+
throw new Error(`Unknown key encryption version: ${version}`);
|
|
122
|
+
}
|
|
123
|
+
const salt = buf.slice(2, 2 + SALT_LEN);
|
|
124
|
+
const iv = buf.slice(2 + SALT_LEN, 2 + SALT_LEN + IV_LEN);
|
|
125
|
+
const authTag = buf.slice(2 + SALT_LEN + IV_LEN, 2 + SALT_LEN + IV_LEN + 16);
|
|
126
|
+
const ciphertext = buf.slice(2 + SALT_LEN + IV_LEN + 16);
|
|
127
|
+
const key = deriveKey(passphrase, salt);
|
|
128
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
129
|
+
decipher.setAuthTag(authTag);
|
|
130
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=cert-issuer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cert-issuer.js","sourceRoot":"","sources":["../src/cert-issuer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CertStore, StoredCert } from "./adapters.js";
|
|
2
|
+
export interface EnsureCertResult {
|
|
3
|
+
cert: StoredCert;
|
|
4
|
+
certPem: string;
|
|
5
|
+
/** Decrypted PEM private key — keep in memory only; never persist plaintext. */
|
|
6
|
+
keyPem: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function ensureActiveCert(opts: {
|
|
9
|
+
store: CertStore;
|
|
10
|
+
tenantId: string;
|
|
11
|
+
/** Cert subject CN (e.g. the tenant/org display name). ASCII-clean it upstream. */
|
|
12
|
+
subjectName: string;
|
|
13
|
+
/** Passphrase used to encrypt/decrypt the key at rest in the CertStore. */
|
|
14
|
+
passphrase: string;
|
|
15
|
+
}): Promise<EnsureCertResult>;
|
|
16
|
+
//# sourceMappingURL=cert-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cert-lifecycle.d.ts","sourceRoot":"","sources":["../src/cert-lifecycle.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuB5B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// cert-lifecycle.ts
|
|
2
|
+
//
|
|
3
|
+
// Stack-agnostic "ensure an active signing cert for this tenant" helper. Depends
|
|
4
|
+
// only on the CertStore interface + the crypto in cert-issuer — no DB, no stack.
|
|
5
|
+
// Creates a cert on first call; on expiry, deactivates the old one and issues a
|
|
6
|
+
// fresh cert with `rotatedFromId` pointing at the predecessor.
|
|
7
|
+
import { generateSelfSignedCert, encryptKeyPem, decryptKeyPem, } from "./cert-issuer.js";
|
|
8
|
+
export async function ensureActiveCert(opts) {
|
|
9
|
+
const existing = await opts.store.findActive(opts.tenantId);
|
|
10
|
+
if (existing && existing.notAfter > new Date()) {
|
|
11
|
+
return {
|
|
12
|
+
cert: existing,
|
|
13
|
+
certPem: existing.certPem,
|
|
14
|
+
keyPem: decryptKeyPem(existing.keyPemEncrypted, opts.passphrase),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (existing) {
|
|
18
|
+
await opts.store.deactivate(existing.id);
|
|
19
|
+
}
|
|
20
|
+
const generated = generateSelfSignedCert({ subjectName: opts.subjectName });
|
|
21
|
+
const keyPemEncrypted = encryptKeyPem(generated.keyPem, opts.passphrase);
|
|
22
|
+
const cert = await opts.store.insert({
|
|
23
|
+
tenantId: opts.tenantId,
|
|
24
|
+
generated,
|
|
25
|
+
keyPemEncrypted,
|
|
26
|
+
rotatedFromId: existing?.id ?? null,
|
|
27
|
+
});
|
|
28
|
+
return { cert, certPem: generated.certPem, keyPem: generated.keyPem };
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=cert-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cert-lifecycle.js","sourceRoot":"","sources":["../src/cert-lifecycle.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,EAAE;AACF,iFAAiF;AACjF,iFAAiF;AACjF,gFAAgF;AAChF,+DAA+D;AAE/D,OAAO,EACL,sBAAsB,EACtB,aAAa,EACb,aAAa,GACd,MAAM,kBAAkB,CAAC;AAU1B,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAOtC;IACC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,QAAQ,IAAI,QAAQ,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;QAC/C,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,eAAe,EAAE,IAAI,CAAC,UAAU,CAAC;SACjE,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,SAAS,GAAG,sBAAsB,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5E,MAAM,eAAe,GAAG,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QACnC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS;QACT,eAAe;QACf,aAAa,EAAE,QAAQ,EAAE,EAAE,IAAI,IAAI;KACpC,CAAC,CAAC;IACH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;AACxE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { PemSigner, type PemSignerInput } from "./pem-signer.js";
|
|
2
|
+
export { generateSelfSignedCert, encryptKeyPem, decryptKeyPem, type GenerateCertOptions, type GeneratedCert, } from "./cert-issuer.js";
|
|
3
|
+
export { renderHtmlToPdf, type RenderHtmlToPdfOptions } from "./render-pdf.js";
|
|
4
|
+
export { signPdf, type SignPdfInput, type SignPdfResult } from "./sign-pdf.js";
|
|
5
|
+
export { verifyPdfStructure, verifyPdfSignature, type VerifyResult } from "./verify-pdf.js";
|
|
6
|
+
export { buildTimeStampReq, parseTimeStampResp, parseTstInfo, OID_TIMESTAMP_TOKEN, } from "./timestamp.js";
|
|
7
|
+
export type { Signer, SigningCertPem, SignedPdfMetadata, TsaTransport, } from "./types.js";
|
|
8
|
+
export type { StoredCert, CertStore, AuditLogEntry, AuditLogRow, AuditLogStore, EsigAuditAction, PdfStorageStore, } from "./adapters.js";
|
|
9
|
+
export { ensureActiveCert, type EnsureCertResult } from "./cert-lifecycle.js";
|
|
10
|
+
export { signDocument, type SignDocumentInput, type SignDocumentResult, } from "./sign-document.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EACL,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,MAAM,EACN,cAAc,EACd,iBAAiB,EACjB,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,UAAU,EACV,SAAS,EACT,aAAa,EACb,WAAW,EACX,aAAa,EACb,eAAe,EACf,eAAe,GAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG9E,OAAO,EACL,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @e-sig/core
|
|
2
|
+
//
|
|
3
|
+
// Portable, self-contained PDF e-signature engine. Zero database / storage /
|
|
4
|
+
// auth / framework assumptions: render HTML→PDF, issue a self-signed org cert,
|
|
5
|
+
// PKCS#7-detached sign (optional RFC-3161 TSA → CAdES-T), verify.
|
|
6
|
+
//
|
|
7
|
+
// - Bring-your-own persistence via the CertStore / AuditLogStore / PdfStorageStore
|
|
8
|
+
// interfaces (./adapters). A Supabase reference impl ships as @e-sig/supabase.
|
|
9
|
+
// - `signDocument()` is the optional end-to-end orchestrator over those stores.
|
|
10
|
+
// ---- Crypto primitives ----
|
|
11
|
+
export { PemSigner } from "./pem-signer.js";
|
|
12
|
+
export { generateSelfSignedCert, encryptKeyPem, decryptKeyPem, } from "./cert-issuer.js";
|
|
13
|
+
export { renderHtmlToPdf } from "./render-pdf.js";
|
|
14
|
+
export { signPdf } from "./sign-pdf.js";
|
|
15
|
+
export { verifyPdfStructure, verifyPdfSignature } from "./verify-pdf.js";
|
|
16
|
+
export { buildTimeStampReq, parseTimeStampResp, parseTstInfo, OID_TIMESTAMP_TOKEN, } from "./timestamp.js";
|
|
17
|
+
// ---- Cert lifecycle (stack-agnostic; interface-only) ----
|
|
18
|
+
export { ensureActiveCert } from "./cert-lifecycle.js";
|
|
19
|
+
// ---- End-to-end orchestrator ----
|
|
20
|
+
export { signDocument, } from "./sign-document.js";
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,kEAAkE;AAClE,EAAE;AACF,mFAAmF;AACnF,iFAAiF;AACjF,gFAAgF;AAEhF,8BAA8B;AAC9B,OAAO,EAAE,SAAS,EAAuB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EACL,sBAAsB,EACtB,aAAa,EACb,aAAa,GAGd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAA+B,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAyC,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAqB,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AAmBxB,4DAA4D;AAC5D,OAAO,EAAE,gBAAgB,EAAyB,MAAM,qBAAqB,CAAC;AAE9E,oCAAoC;AACpC,OAAO,EACL,YAAY,GAGb,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Signer } from "@signpdf/utils";
|
|
2
|
+
import type { TsaTransport } from "./types.js";
|
|
3
|
+
export interface PemSignerInput {
|
|
4
|
+
keyPem: string;
|
|
5
|
+
certPem: string;
|
|
6
|
+
/**
|
|
7
|
+
* Optional RFC 3161 timestamp transport. When provided, each produced
|
|
8
|
+
* signature is upgraded from CAdES-B to CAdES-T by embedding a
|
|
9
|
+
* TimeStampToken. The caller injects the network POST so this package never
|
|
10
|
+
* performs egress (the TSA only ever receives a SHA-256 hash, never PHI).
|
|
11
|
+
*/
|
|
12
|
+
tsa?: TsaTransport;
|
|
13
|
+
/**
|
|
14
|
+
* Strict PAdES baseline (ETSI EN 319 142-1) mode. When true, the CMS
|
|
15
|
+
* `signing-time` signed attribute is removed (PAdES requires the claimed time
|
|
16
|
+
* to live in the signature dictionary /M entry, not the CMS). Default false
|
|
17
|
+
* keeps `signing-time` for backward compatibility with existing deployments;
|
|
18
|
+
* the ESS `signing-certificate-v2` attribute is added in BOTH modes.
|
|
19
|
+
*/
|
|
20
|
+
padesStrict?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare class PemSigner extends Signer {
|
|
23
|
+
private privateKey;
|
|
24
|
+
private certificate;
|
|
25
|
+
private tsa?;
|
|
26
|
+
private padesStrict;
|
|
27
|
+
/** True after the most recent sign() embedded a TimeStampToken (CAdES-T). */
|
|
28
|
+
lastTimestamped: boolean;
|
|
29
|
+
/** Set when a non-required TSA call failed (signature falls back to CAdES-B). */
|
|
30
|
+
lastTsaError?: string;
|
|
31
|
+
constructor({ keyPem, certPem, tsa, padesStrict }: PemSignerInput);
|
|
32
|
+
sign(pdfBuffer: Buffer, signingTime?: Date): Promise<Buffer>;
|
|
33
|
+
/**
|
|
34
|
+
* Insert the ESS signing-certificate-v2 signed attribute into the first
|
|
35
|
+
* SignerInfo's signed-attributes set and recompute the RSA signature over the
|
|
36
|
+
* modified set. In `padesStrict` mode the PAdES-forbidden signing-time
|
|
37
|
+
* attribute is also removed. Returns the new signatureValue (forge binary
|
|
38
|
+
* string) so the caller can timestamp the FINAL signature.
|
|
39
|
+
*/
|
|
40
|
+
private addSigningCertV2AndResign;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=pem-signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pem-signer.d.ts","sourceRoot":"","sources":["../src/pem-signer.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,MAAM,EAAgB,MAAM,gBAAgB,CAAC;AAStD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAS/C,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,YAAY,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qBAAa,SAAU,SAAQ,MAAM;IACnC,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAAwB;IAC3C,OAAO,CAAC,GAAG,CAAC,CAAe;IAC3B,OAAO,CAAC,WAAW,CAAU;IAE7B,6EAA6E;IACtE,eAAe,UAAS;IAC/B,iFAAiF;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;gBAEjB,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,cAAc;IAc3D,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IA2ElE;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;CAwClC"}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// src/lib/integrations/esig/core/pem-signer.ts
|
|
2
|
+
//
|
|
3
|
+
// Portable PKCS#7 signer for @signpdf/signpdf, driven by raw PEM input.
|
|
4
|
+
//
|
|
5
|
+
// Drops the PKCS#12 round-trip (broken in node-forge — see Phase 19 spike
|
|
6
|
+
// bug notes) by taking PEM key + cert and using forge.pkcs7.createSignedData
|
|
7
|
+
// directly. Project-agnostic: no DB, no storage, no project-specific assumptions.
|
|
8
|
+
//
|
|
9
|
+
// RFC 3161 (CAdES-T): when a TsaTransport is supplied, after signing we request
|
|
10
|
+
// a TimeStampToken over the SignerInfo signatureValue (encryptedDigest) and
|
|
11
|
+
// splice it into the SignerInfo as the id-aa-timeStampToken UNSIGNED attribute.
|
|
12
|
+
// The token is added by walking the DER and appending a hand-built [1] node to
|
|
13
|
+
// the SignerInfo SEQUENCE — we do NOT use forge's unauthenticatedAttributes
|
|
14
|
+
// auto-serialization, which is broken in 1.4.0 (a `.values` vs `.value` bug in
|
|
15
|
+
// the per-attribute path emits a malformed [1]).
|
|
16
|
+
//
|
|
17
|
+
// @signpdf/signpdf calls `signer.sign(pdfBuffer, signingTime)` (two args, and
|
|
18
|
+
// it awaits the result), so the TSA transport is supplied via the constructor,
|
|
19
|
+
// not per-call.
|
|
20
|
+
import forge from "node-forge";
|
|
21
|
+
import { Signer, SignPdfError } from "@signpdf/utils";
|
|
22
|
+
import { buildTimeStampReq, parseTimeStampResp, derStringToUint8, uint8ToDerString, OID_TIMESTAMP_TOKEN, } from "./timestamp.js";
|
|
23
|
+
const asn1 = forge.asn1;
|
|
24
|
+
/** id-aa-signingCertificateV2 (ESS, RFC 5035) — binds the signer cert into the signed data. */
|
|
25
|
+
const OID_SIGNING_CERT_V2 = "1.2.840.113549.1.9.16.2.47";
|
|
26
|
+
/** id-signingTime (PKCS#9) — PAdES forbids this signed attribute (time belongs in /M). */
|
|
27
|
+
const OID_SIGNING_TIME = "1.2.840.113549.1.9.5";
|
|
28
|
+
export class PemSigner extends Signer {
|
|
29
|
+
privateKey;
|
|
30
|
+
certificate;
|
|
31
|
+
tsa;
|
|
32
|
+
padesStrict;
|
|
33
|
+
/** True after the most recent sign() embedded a TimeStampToken (CAdES-T). */
|
|
34
|
+
lastTimestamped = false;
|
|
35
|
+
/** Set when a non-required TSA call failed (signature falls back to CAdES-B). */
|
|
36
|
+
lastTsaError;
|
|
37
|
+
constructor({ keyPem, certPem, tsa, padesStrict }) {
|
|
38
|
+
super();
|
|
39
|
+
if (!keyPem.includes("-----BEGIN")) {
|
|
40
|
+
throw new Error("PemSigner: keyPem must be PEM-encoded");
|
|
41
|
+
}
|
|
42
|
+
if (!certPem.includes("-----BEGIN CERTIFICATE-----")) {
|
|
43
|
+
throw new Error("PemSigner: certPem must be a PEM-encoded X.509 certificate");
|
|
44
|
+
}
|
|
45
|
+
this.privateKey = forge.pki.privateKeyFromPem(keyPem);
|
|
46
|
+
this.certificate = forge.pki.certificateFromPem(certPem);
|
|
47
|
+
this.tsa = tsa;
|
|
48
|
+
this.padesStrict = padesStrict ?? false;
|
|
49
|
+
}
|
|
50
|
+
async sign(pdfBuffer, signingTime) {
|
|
51
|
+
if (!Buffer.isBuffer(pdfBuffer)) {
|
|
52
|
+
throw new SignPdfError("PDF expected as Buffer.", SignPdfError.TYPE_INPUT);
|
|
53
|
+
}
|
|
54
|
+
// Reset per-call timestamp state (signpdf calls sign() once per signature).
|
|
55
|
+
this.lastTimestamped = false;
|
|
56
|
+
this.lastTsaError = undefined;
|
|
57
|
+
const p7 = forge.pkcs7.createSignedData();
|
|
58
|
+
p7.content = forge.util.createBuffer(pdfBuffer.toString("binary"));
|
|
59
|
+
p7.addCertificate(this.certificate);
|
|
60
|
+
p7.addSigner({
|
|
61
|
+
key: this.privateKey,
|
|
62
|
+
certificate: this.certificate,
|
|
63
|
+
digestAlgorithm: forge.pki.oids.sha256,
|
|
64
|
+
authenticatedAttributes: [
|
|
65
|
+
{ type: forge.pki.oids.contentType, value: forge.pki.oids.data },
|
|
66
|
+
// @types/node-forge declares value as string but the runtime accepts
|
|
67
|
+
// and prefers a Date — matches the official forge.pkcs7 docs.
|
|
68
|
+
{ type: forge.pki.oids.signingTime, value: (signingTime ?? new Date()) },
|
|
69
|
+
{ type: forge.pki.oids.messageDigest },
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
p7.sign({ detached: true });
|
|
73
|
+
// Add the ESS signing-certificate-v2 signed attribute (binds THIS cert into
|
|
74
|
+
// the signed data — required for PAdES/CAdES baseline) and re-sign the
|
|
75
|
+
// modified signed-attributes set. forge cannot encode this attribute in its
|
|
76
|
+
// authenticatedAttributes list, so we splice it in and recompute the RSA
|
|
77
|
+
// signature over the exact bytes. Returns the NEW signatureValue.
|
|
78
|
+
const p7Asn1 = p7.toAsn1();
|
|
79
|
+
const sigValue = this.addSigningCertV2AndResign(p7Asn1);
|
|
80
|
+
// No timestamp requested → CAdES-B (now cert-bound).
|
|
81
|
+
if (!this.tsa) {
|
|
82
|
+
return Buffer.from(forge.asn1.toDer(p7Asn1).getBytes(), "binary");
|
|
83
|
+
}
|
|
84
|
+
// CAdES-T: request a TimeStampToken over the (final) SignerInfo signatureValue.
|
|
85
|
+
try {
|
|
86
|
+
const reqDer = buildTimeStampReq(sigValue);
|
|
87
|
+
const respBytes = await this.tsa.fetch(derStringToUint8(reqDer));
|
|
88
|
+
const { token } = parseTimeStampResp(uint8ToDerString(respBytes));
|
|
89
|
+
// Attribute ::= SEQUENCE { attrType OID, attrValues SET OF AttributeValue }
|
|
90
|
+
const attrSeq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
91
|
+
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(OID_TIMESTAMP_TOKEN).getBytes()),
|
|
92
|
+
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [token]),
|
|
93
|
+
]);
|
|
94
|
+
// unsignedAttrs ::= [1] IMPLICIT SET OF Attribute
|
|
95
|
+
const unsignedAttrs = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [attrSeq]);
|
|
96
|
+
// Splice via DER-walk (NOT forge unauthenticatedAttributes, which is
|
|
97
|
+
// broken in 1.4.0). Append the [1] node to the first SignerInfo SEQUENCE.
|
|
98
|
+
spliceUnsignedAttrs(p7Asn1, unsignedAttrs);
|
|
99
|
+
this.lastTimestamped = true;
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
if (this.tsa.required) {
|
|
103
|
+
throw e;
|
|
104
|
+
}
|
|
105
|
+
// Non-required: degrade gracefully to CAdES-B.
|
|
106
|
+
this.lastTimestamped = false;
|
|
107
|
+
this.lastTsaError = String(e);
|
|
108
|
+
}
|
|
109
|
+
return Buffer.from(forge.asn1.toDer(p7Asn1).getBytes(), "binary");
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Insert the ESS signing-certificate-v2 signed attribute into the first
|
|
113
|
+
* SignerInfo's signed-attributes set and recompute the RSA signature over the
|
|
114
|
+
* modified set. In `padesStrict` mode the PAdES-forbidden signing-time
|
|
115
|
+
* attribute is also removed. Returns the new signatureValue (forge binary
|
|
116
|
+
* string) so the caller can timestamp the FINAL signature.
|
|
117
|
+
*/
|
|
118
|
+
addSigningCertV2AndResign(p7Asn1) {
|
|
119
|
+
const signerInfo = firstSignerInfoSeq(p7Asn1);
|
|
120
|
+
const siChildren = signerInfo.value;
|
|
121
|
+
// signedAttrs = the [0] IMPLICIT context node; encryptedDigest = the
|
|
122
|
+
// UNIVERSAL OCTET STRING (the signature to overwrite).
|
|
123
|
+
let signedAttrs;
|
|
124
|
+
let sigOctet;
|
|
125
|
+
for (const child of siChildren) {
|
|
126
|
+
if (child.tagClass === asn1.Class.CONTEXT_SPECIFIC && child.type === 0) {
|
|
127
|
+
signedAttrs = child;
|
|
128
|
+
}
|
|
129
|
+
if (child.tagClass === asn1.Class.UNIVERSAL && child.type === asn1.Type.OCTETSTRING) {
|
|
130
|
+
sigOctet = child;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!signedAttrs || !Array.isArray(signedAttrs.value) || !sigOctet) {
|
|
134
|
+
throw new Error("PemSigner: could not locate signed attributes to add signing-certificate-v2");
|
|
135
|
+
}
|
|
136
|
+
let attrs = signedAttrs.value;
|
|
137
|
+
if (this.padesStrict) {
|
|
138
|
+
attrs = attrs.filter((a) => {
|
|
139
|
+
const oidNode = Array.isArray(a.value) ? a.value[0] : undefined;
|
|
140
|
+
return !(oidNode && oidNode.type === asn1.Type.OID && safeOid(oidNode.value) === OID_SIGNING_TIME);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
attrs.push(buildSigningCertV2Attr(this.certificate));
|
|
144
|
+
signedAttrs.value = attrs;
|
|
145
|
+
// Re-sign: RSA over DER(signedAttrs re-tagged as SET OF), SHA-256.
|
|
146
|
+
const setDer = asn1.toDer(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs)).getBytes();
|
|
147
|
+
const md = forge.md.sha256.create();
|
|
148
|
+
md.update(setDer);
|
|
149
|
+
const newSig = this.privateKey.sign(md); // RSASSA-PKCS1-V1_5 (forge default)
|
|
150
|
+
sigOctet.value = newSig;
|
|
151
|
+
return newSig;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Build the ESS signing-certificate-v2 signed attribute (RFC 5035) binding the
|
|
156
|
+
* signer certificate into the signed data:
|
|
157
|
+
* Attribute { OID id-aa-signingCertificateV2, SET { SigningCertificateV2 } }
|
|
158
|
+
* SigningCertificateV2 ::= SEQUENCE { certs SEQUENCE OF ESSCertIDv2 }
|
|
159
|
+
* ESSCertIDv2 ::= SEQUENCE { certHash OCTET STRING, issuerSerial IssuerSerial }
|
|
160
|
+
* hashAlgorithm defaults to SHA-256 and is omitted (DEFAULT).
|
|
161
|
+
*/
|
|
162
|
+
function buildSigningCertV2Attr(cert) {
|
|
163
|
+
const certDer = asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes();
|
|
164
|
+
const md = forge.md.sha256.create();
|
|
165
|
+
md.update(certDer);
|
|
166
|
+
const certHash = md.digest().getBytes();
|
|
167
|
+
// IssuerSerial ::= SEQUENCE { issuer GeneralNames, serialNumber INTEGER }
|
|
168
|
+
const issuerName = forge.pki.distinguishedNameToAsn1(cert.issuer);
|
|
169
|
+
const generalName = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 4, true, [issuerName]); // directoryName [4]
|
|
170
|
+
const generalNames = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [generalName]);
|
|
171
|
+
let serialBytes = forge.util.hexToBytes(cert.serialNumber);
|
|
172
|
+
if (serialBytes.length > 0 && (serialBytes.charCodeAt(0) & 0x80) !== 0) {
|
|
173
|
+
serialBytes = "\x00" + serialBytes; // keep the INTEGER positive
|
|
174
|
+
}
|
|
175
|
+
const serial = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, serialBytes);
|
|
176
|
+
const issuerSerial = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [generalNames, serial]);
|
|
177
|
+
const essCertId = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
178
|
+
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, certHash),
|
|
179
|
+
issuerSerial,
|
|
180
|
+
]);
|
|
181
|
+
const certsSeq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [essCertId]);
|
|
182
|
+
const signingCertV2 = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [certsSeq]);
|
|
183
|
+
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
184
|
+
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(OID_SIGNING_CERT_V2).getBytes()),
|
|
185
|
+
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [signingCertV2]),
|
|
186
|
+
]);
|
|
187
|
+
}
|
|
188
|
+
/** OID decode that never throws (returns "" on malformed input). */
|
|
189
|
+
function safeOid(der) {
|
|
190
|
+
try {
|
|
191
|
+
return asn1.derToOid(der);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return "";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** Locate the first SignerInfo SEQUENCE inside a PKCS#7 SignedData ASN.1 tree. */
|
|
198
|
+
function firstSignerInfoSeq(contentInfo) {
|
|
199
|
+
if (!Array.isArray(contentInfo.value)) {
|
|
200
|
+
throw new Error("PemSigner: ContentInfo has no children");
|
|
201
|
+
}
|
|
202
|
+
let signedData;
|
|
203
|
+
for (const child of contentInfo.value) {
|
|
204
|
+
if (child.tagClass === asn1.Class.CONTEXT_SPECIFIC && Array.isArray(child.value)) {
|
|
205
|
+
signedData = child.value[0];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (!signedData || !Array.isArray(signedData.value)) {
|
|
209
|
+
throw new Error("PemSigner: SignedData not found");
|
|
210
|
+
}
|
|
211
|
+
let signerInfos;
|
|
212
|
+
for (const child of signedData.value) {
|
|
213
|
+
if (child.tagClass === asn1.Class.UNIVERSAL && child.type === asn1.Type.SET && Array.isArray(child.value)) {
|
|
214
|
+
signerInfos = child; // last UNIVERSAL SET = signerInfos
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (!signerInfos || !Array.isArray(signerInfos.value) || signerInfos.value.length === 0) {
|
|
218
|
+
throw new Error("PemSigner: signerInfos SET not found");
|
|
219
|
+
}
|
|
220
|
+
const si = signerInfos.value[0];
|
|
221
|
+
if (!Array.isArray(si.value)) {
|
|
222
|
+
throw new Error("PemSigner: SignerInfo SEQUENCE malformed");
|
|
223
|
+
}
|
|
224
|
+
return si;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Locate the (first) SignerInfo SEQUENCE inside a PKCS#7 SignedData ASN.1 tree
|
|
228
|
+
* and append the supplied unsigned-attributes [1] node to it.
|
|
229
|
+
*
|
|
230
|
+
* ContentInfo ::= SEQUENCE { contentType OID, content [0] EXPLICIT SignedData }
|
|
231
|
+
* SignedData ::= SEQUENCE { version, digestAlgorithms SET, encapContentInfo,
|
|
232
|
+
* certificates [0] IMPLICIT OPTIONAL,
|
|
233
|
+
* crls [1] IMPLICIT OPTIONAL,
|
|
234
|
+
* signerInfos SET OF SignerInfo }
|
|
235
|
+
* SignerInfo ::= SEQUENCE { version, sid, digestAlgorithm,
|
|
236
|
+
* signedAttrs [0] IMPLICIT OPTIONAL,
|
|
237
|
+
* signatureAlgorithm, signature,
|
|
238
|
+
* unsignedAttrs [1] IMPLICIT OPTIONAL }
|
|
239
|
+
*
|
|
240
|
+
* signerInfos is the last UNIVERSAL SET among SignedData's children (the first
|
|
241
|
+
* SET is digestAlgorithms). We append the unsigned attrs as the final child of
|
|
242
|
+
* the first SignerInfo SEQUENCE.
|
|
243
|
+
*/
|
|
244
|
+
function spliceUnsignedAttrs(contentInfo, unsignedAttrs) {
|
|
245
|
+
if (!Array.isArray(contentInfo.value)) {
|
|
246
|
+
throw new Error("RFC3161 splice: ContentInfo has no children");
|
|
247
|
+
}
|
|
248
|
+
// content [0] EXPLICIT → SignedData SEQUENCE
|
|
249
|
+
let signedData;
|
|
250
|
+
for (const child of contentInfo.value) {
|
|
251
|
+
if (child.tagClass === asn1.Class.CONTEXT_SPECIFIC && Array.isArray(child.value)) {
|
|
252
|
+
signedData = child.value[0];
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (!signedData || !Array.isArray(signedData.value)) {
|
|
256
|
+
throw new Error("RFC3161 splice: SignedData not found");
|
|
257
|
+
}
|
|
258
|
+
// signerInfos = the last UNIVERSAL SET among SignedData children.
|
|
259
|
+
let signerInfos;
|
|
260
|
+
for (const child of signedData.value) {
|
|
261
|
+
if (child.tagClass === asn1.Class.UNIVERSAL &&
|
|
262
|
+
child.type === asn1.Type.SET &&
|
|
263
|
+
Array.isArray(child.value)) {
|
|
264
|
+
signerInfos = child; // keep the last one encountered
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (!signerInfos || !Array.isArray(signerInfos.value) || signerInfos.value.length === 0) {
|
|
268
|
+
throw new Error("RFC3161 splice: signerInfos SET not found");
|
|
269
|
+
}
|
|
270
|
+
const signerInfo = signerInfos.value[0];
|
|
271
|
+
if (!Array.isArray(signerInfo.value)) {
|
|
272
|
+
throw new Error("RFC3161 splice: SignerInfo SEQUENCE malformed");
|
|
273
|
+
}
|
|
274
|
+
signerInfo.value.push(unsignedAttrs);
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=pem-signer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pem-signer.js","sourceRoot":"","sources":["../src/pem-signer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type PaperFormat } from "puppeteer-core";
|
|
2
|
+
export interface RenderHtmlToPdfOptions {
|
|
3
|
+
html: string;
|
|
4
|
+
format?: PaperFormat;
|
|
5
|
+
margin?: {
|
|
6
|
+
top?: string;
|
|
7
|
+
bottom?: string;
|
|
8
|
+
left?: string;
|
|
9
|
+
right?: string;
|
|
10
|
+
};
|
|
11
|
+
printBackground?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Override the chromium executable path. If omitted:
|
|
14
|
+
* - on Vercel / AWS Lambda: load @sparticuz/chromium
|
|
15
|
+
* - on macOS: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
|
|
16
|
+
* - on Linux: try common google-chrome / chromium paths
|
|
17
|
+
* Set this when porting to an environment outside that matrix.
|
|
18
|
+
*/
|
|
19
|
+
executablePath?: string;
|
|
20
|
+
/** Override puppeteer launch args. Default: --no-sandbox --disable-setuid-sandbox. */
|
|
21
|
+
launchArgs?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Enable JavaScript execution in the rendered document. Default FALSE:
|
|
24
|
+
* document templates are static HTML, and executing untrusted/interpolated
|
|
25
|
+
* HTML with JS enabled is an SSRF / data-exfiltration surface. Set true only
|
|
26
|
+
* if your templates genuinely need in-page scripting.
|
|
27
|
+
*/
|
|
28
|
+
javascriptEnabled?: boolean;
|
|
29
|
+
/** Max ms to wait for content + subresources to load. Default 30000. */
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare function renderHtmlToPdf(opts: RenderHtmlToPdfOptions): Promise<Buffer>;
|
|
33
|
+
//# sourceMappingURL=render-pdf.d.ts.map
|
|
@@ -0,0 +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;AAuCD,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwCnF"}
|