@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.
Files changed (102) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +92 -0
  3. package/dist/index.d.ts +7 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +7 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/pem-signer.d.ts +26 -5
  8. package/dist/pem-signer.d.ts.map +1 -1
  9. package/dist/pem-signer.js +84 -13
  10. package/dist/pem-signer.js.map +1 -1
  11. package/dist/pq-cert.d.ts +76 -0
  12. package/dist/pq-cert.d.ts.map +1 -0
  13. package/dist/pq-cert.js +295 -0
  14. package/dist/pq-cert.js.map +1 -0
  15. package/dist/pq-embed.d.ts +22 -0
  16. package/dist/pq-embed.d.ts.map +1 -0
  17. package/dist/pq-embed.js +119 -0
  18. package/dist/pq-embed.js.map +1 -0
  19. package/dist/pq-lifecycle.d.ts +59 -0
  20. package/dist/pq-lifecycle.d.ts.map +1 -0
  21. package/dist/pq-lifecycle.js +55 -0
  22. package/dist/pq-lifecycle.js.map +1 -0
  23. package/dist/pq-seal.d.ts +183 -0
  24. package/dist/pq-seal.d.ts.map +1 -0
  25. package/dist/pq-seal.js +345 -0
  26. package/dist/pq-seal.js.map +1 -0
  27. package/dist/pq-verify.d.ts +99 -0
  28. package/dist/pq-verify.d.ts.map +1 -0
  29. package/dist/pq-verify.js +135 -0
  30. package/dist/pq-verify.js.map +1 -0
  31. package/dist/render-pdf.d.ts +5 -5
  32. package/dist/render-pdf.d.ts.map +1 -1
  33. package/dist/render-pdf.js +51 -17
  34. package/dist/render-pdf.js.map +1 -1
  35. package/dist/sign-document.d.ts +16 -0
  36. package/dist/sign-document.d.ts.map +1 -1
  37. package/dist/sign-document.js +10 -0
  38. package/dist/sign-document.js.map +1 -1
  39. package/dist/sign-pdf.d.ts +66 -4
  40. package/dist/sign-pdf.d.ts.map +1 -1
  41. package/dist/sign-pdf.js +51 -2
  42. package/dist/sign-pdf.js.map +1 -1
  43. package/dist/types.d.ts +36 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/vendor/placeholder-plain/LICENSE-signpdf.md +47 -0
  46. package/dist/vendor/placeholder-plain/createBufferPageWithAnnotation.d.ts +6 -0
  47. package/dist/vendor/placeholder-plain/createBufferPageWithAnnotation.d.ts.map +1 -0
  48. package/dist/vendor/placeholder-plain/createBufferPageWithAnnotation.js +38 -0
  49. package/dist/vendor/placeholder-plain/createBufferPageWithAnnotation.js.map +1 -0
  50. package/dist/vendor/placeholder-plain/createBufferRootWithAcroform.d.ts +6 -0
  51. package/dist/vendor/placeholder-plain/createBufferRootWithAcroform.d.ts.map +1 -0
  52. package/dist/vendor/placeholder-plain/createBufferRootWithAcroform.js +15 -0
  53. package/dist/vendor/placeholder-plain/createBufferRootWithAcroform.js.map +1 -0
  54. package/dist/vendor/placeholder-plain/createBufferTrailer.d.ts +4 -0
  55. package/dist/vendor/placeholder-plain/createBufferTrailer.d.ts.map +1 -0
  56. package/dist/vendor/placeholder-plain/createBufferTrailer.js +26 -0
  57. package/dist/vendor/placeholder-plain/createBufferTrailer.js.map +1 -0
  58. package/dist/vendor/placeholder-plain/findObject.d.ts +4 -0
  59. package/dist/vendor/placeholder-plain/findObject.d.ts.map +1 -0
  60. package/dist/vendor/placeholder-plain/findObject.js +18 -0
  61. package/dist/vendor/placeholder-plain/findObject.js.map +1 -0
  62. package/dist/vendor/placeholder-plain/getIndexFromRef.d.ts +4 -0
  63. package/dist/vendor/placeholder-plain/getIndexFromRef.d.ts.map +1 -0
  64. package/dist/vendor/placeholder-plain/getIndexFromRef.js +13 -0
  65. package/dist/vendor/placeholder-plain/getIndexFromRef.js.map +1 -0
  66. package/dist/vendor/placeholder-plain/getPageRef.d.ts +6 -0
  67. package/dist/vendor/placeholder-plain/getPageRef.d.ts.map +1 -0
  68. package/dist/vendor/placeholder-plain/getPageRef.js +18 -0
  69. package/dist/vendor/placeholder-plain/getPageRef.js.map +1 -0
  70. package/dist/vendor/placeholder-plain/getPagesDictionaryRef.d.ts +3 -0
  71. package/dist/vendor/placeholder-plain/getPagesDictionaryRef.d.ts.map +1 -0
  72. package/dist/vendor/placeholder-plain/getPagesDictionaryRef.js +12 -0
  73. package/dist/vendor/placeholder-plain/getPagesDictionaryRef.js.map +1 -0
  74. package/dist/vendor/placeholder-plain/index.d.ts +2 -0
  75. package/dist/vendor/placeholder-plain/index.d.ts.map +1 -0
  76. package/dist/vendor/placeholder-plain/index.js +4 -0
  77. package/dist/vendor/placeholder-plain/index.js.map +1 -0
  78. package/dist/vendor/placeholder-plain/pdfObject.d.ts +13 -0
  79. package/dist/vendor/placeholder-plain/pdfObject.d.ts.map +1 -0
  80. package/dist/vendor/placeholder-plain/pdfObject.js +142 -0
  81. package/dist/vendor/placeholder-plain/pdfObject.js.map +1 -0
  82. package/dist/vendor/placeholder-plain/pdfkitAddPlaceholder.d.ts +49 -0
  83. package/dist/vendor/placeholder-plain/pdfkitAddPlaceholder.d.ts.map +1 -0
  84. package/dist/vendor/placeholder-plain/pdfkitAddPlaceholder.js +115 -0
  85. package/dist/vendor/placeholder-plain/pdfkitAddPlaceholder.js.map +1 -0
  86. package/dist/vendor/placeholder-plain/plainAddPlaceholder.d.ts +41 -0
  87. package/dist/vendor/placeholder-plain/plainAddPlaceholder.d.ts.map +1 -0
  88. package/dist/vendor/placeholder-plain/plainAddPlaceholder.js +95 -0
  89. package/dist/vendor/placeholder-plain/plainAddPlaceholder.js.map +1 -0
  90. package/dist/vendor/placeholder-plain/readPdf.d.ts +20 -0
  91. package/dist/vendor/placeholder-plain/readPdf.d.ts.map +1 -0
  92. package/dist/vendor/placeholder-plain/readPdf.js +47 -0
  93. package/dist/vendor/placeholder-plain/readPdf.js.map +1 -0
  94. package/dist/vendor/placeholder-plain/readRefTable.d.ts +17 -0
  95. package/dist/vendor/placeholder-plain/readRefTable.d.ts.map +1 -0
  96. package/dist/vendor/placeholder-plain/readRefTable.js +84 -0
  97. package/dist/vendor/placeholder-plain/readRefTable.js.map +1 -0
  98. package/dist/vendor/placeholder-plain/xrefToRefMap.d.ts +3 -0
  99. package/dist/vendor/placeholder-plain/xrefToRefMap.d.ts.map +1 -0
  100. package/dist/vendor/placeholder-plain/xrefToRefMap.js +38 -0
  101. package/dist/vendor/placeholder-plain/xrefToRefMap.js.map +1 -0
  102. package/package.json +10 -8
@@ -0,0 +1,295 @@
1
+ // src/pq-cert.ts
2
+ //
3
+ // Self-signed ML-DSA-65 X.509 certificate (RFC 9881).
4
+ //
5
+ // The raw-key TOFU identity (pq-seal) is enough to pin a signer, but enterprise
6
+ // relying parties expect an X.509 container: a human-readable subject bound to a
7
+ // public key by a signature. This issues a self-signed certificate whose
8
+ // SubjectPublicKeyInfo AND signatureAlgorithm are id-ml-dsa-65
9
+ // (2.16.840.1.101.3.4.3.18), per RFC 9881 — parseable by OpenSSL 3.5+ and
10
+ // self-verifying with the same ML-DSA-65 primitive the seal uses.
11
+ //
12
+ // The cert does NOT add a root of trust (it is self-signed — still TOFU on the
13
+ // key), but it upgrades the identity from an opaque fingerprint to a portable,
14
+ // standards-shaped assertion "CN=<subject> controls this ML-DSA-65 key", which
15
+ // `certMatchesPqSeal` ties back to a seal by public-key fingerprint.
16
+ import forge from "node-forge";
17
+ import crypto from "node:crypto";
18
+ import { ml_dsa65 } from "@noble/post-quantum/ml-dsa.js";
19
+ /** RFC 9881 / NIST: id-ml-dsa-65. Used for BOTH the SPKI alg and the cert signature alg. */
20
+ export const ID_ML_DSA_65 = "2.16.840.1.101.3.4.3.18";
21
+ const MLDSA65_PUBLIC_LEN = 1952;
22
+ const MLDSA65_SIGNATURE_LEN = 3309;
23
+ const OID_COMMON_NAME = "2.5.4.3";
24
+ const OID_ORG_NAME = "2.5.4.10";
25
+ const OID_BASIC_CONSTRAINTS = "2.5.29.19";
26
+ const OID_KEY_USAGE = "2.5.29.15";
27
+ const DEFAULT_VALIDITY_DAYS = 365;
28
+ const asn1 = forge.asn1;
29
+ const { Class, Type } = asn1;
30
+ // ---------- binary <-> Uint8Array (node-forge speaks binary strings) ----------
31
+ function u8ToBin(u8) {
32
+ let s = "";
33
+ for (let i = 0; i < u8.length; i++)
34
+ s += String.fromCharCode(u8[i]);
35
+ return s;
36
+ }
37
+ function binToU8(bin) {
38
+ const u8 = new Uint8Array(bin.length);
39
+ for (let i = 0; i < bin.length; i++)
40
+ u8[i] = bin.charCodeAt(i) & 0xff;
41
+ return u8;
42
+ }
43
+ function sha256Hex(bytes) {
44
+ return crypto.createHash("sha256").update(bytes).digest("hex");
45
+ }
46
+ // ---------- ASN.1 builders ----------
47
+ function oidNode(oid) {
48
+ return asn1.create(Class.UNIVERSAL, Type.OID, false, asn1.oidToDer(oid).getBytes());
49
+ }
50
+ /** AlgorithmIdentifier for ML-DSA: SEQUENCE { OID } — parameters field ABSENT (RFC 9881 §4). */
51
+ function mlDsaAlgId() {
52
+ return asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, [oidNode(ID_ML_DSA_65)]);
53
+ }
54
+ /** DER BIT STRING with 0 unused bits wrapping raw bytes (keys, signatures). */
55
+ function bitStringNode(bytes) {
56
+ return asn1.create(Class.UNIVERSAL, Type.BITSTRING, false, String.fromCharCode(0x00) + u8ToBin(bytes));
57
+ }
58
+ function utf8Node(value) {
59
+ return asn1.create(Class.UNIVERSAL, Type.UTF8, false, forge.util.encodeUtf8(value));
60
+ }
61
+ /** RDNSequence for a CN + O subject. */
62
+ function nameNode(commonName, org) {
63
+ const rdn = (oid, value) => asn1.create(Class.UNIVERSAL, Type.SET, true, [
64
+ asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, [oidNode(oid), utf8Node(value)]),
65
+ ]);
66
+ return asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, [
67
+ rdn(OID_COMMON_NAME, commonName),
68
+ rdn(OID_ORG_NAME, org),
69
+ ]);
70
+ }
71
+ /** SubjectPublicKeyInfo: SEQUENCE { AlgorithmIdentifier, BIT STRING(raw pubkey) } (RFC 9881). */
72
+ function spkiNode(pub) {
73
+ return asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, [mlDsaAlgId(), bitStringNode(pub)]);
74
+ }
75
+ function timeNode(d) {
76
+ // UTCTime for years < 2050 (RFC 5280 §4.1.2.5), GeneralizedTime beyond.
77
+ if (d.getUTCFullYear() < 2050) {
78
+ return asn1.create(Class.UNIVERSAL, Type.UTCTIME, false, asn1.dateToUtcTime(d));
79
+ }
80
+ return asn1.create(Class.UNIVERSAL, Type.GENERALIZEDTIME, false, asn1.dateToGeneralizedTime(d));
81
+ }
82
+ /** Extension: SEQUENCE { OID, [critical BOOLEAN], OCTET STRING(DER of value) }. */
83
+ function extensionNode(oid, critical, value) {
84
+ const children = [oidNode(oid)];
85
+ if (critical)
86
+ children.push(asn1.create(Class.UNIVERSAL, Type.BOOLEAN, false, String.fromCharCode(0xff)));
87
+ children.push(asn1.create(Class.UNIVERSAL, Type.OCTETSTRING, false, asn1.toDer(value).getBytes()));
88
+ return asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, children);
89
+ }
90
+ /**
91
+ * Issue a self-signed ML-DSA-65 X.509 certificate for the given signing keys.
92
+ * The certificate's public key is the raw 1952-byte ML-DSA-65 key, and its own
93
+ * signature is ML-DSA-65 over the TBSCertificate.
94
+ */
95
+ export function issueMlDsaCertificate(opts) {
96
+ const { keys, subjectName, validityDays = DEFAULT_VALIDITY_DAYS } = opts;
97
+ if (!/^[\x20-\x7e]+$/.test(subjectName)) {
98
+ throw new Error(`subjectName "${subjectName}" contains non-ASCII characters`);
99
+ }
100
+ const pub = keys.mldsa65PublicKey;
101
+ if (pub.length !== MLDSA65_PUBLIC_LEN) {
102
+ throw new Error(`ML-DSA-65 public key must be ${MLDSA65_PUBLIC_LEN} bytes, got ${pub.length}`);
103
+ }
104
+ // Serial: 128 bits CSPRNG, top bit cleared (positive DER INTEGER), nonzero lead.
105
+ let serial;
106
+ if (opts.serial) {
107
+ serial = opts.serial;
108
+ }
109
+ else {
110
+ const s = crypto.randomBytes(16);
111
+ s[0] = (s[0] & 0x7f) | 0x40;
112
+ serial = s;
113
+ }
114
+ const notBefore = opts.notBefore ?? new Date();
115
+ const notAfter = new Date(notBefore.getTime() + validityDays * 86_400_000);
116
+ const cn = `E-sig PQ (${subjectName})`;
117
+ const name = () => nameNode(cn, subjectName);
118
+ const version = asn1.create(Class.CONTEXT_SPECIFIC, 0, true, [
119
+ asn1.create(Class.UNIVERSAL, Type.INTEGER, false, String.fromCharCode(0x02)), // v3
120
+ ]);
121
+ const serialNode = asn1.create(Class.UNIVERSAL, Type.INTEGER, false, u8ToBin(serial));
122
+ const validity = asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, [timeNode(notBefore), timeNode(notAfter)]);
123
+ // basicConstraints (CA:FALSE = empty SEQUENCE) + keyUsage (digitalSignature + nonRepudiation).
124
+ const basicConstraints = extensionNode(OID_BASIC_CONSTRAINTS, true, asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, []));
125
+ const keyUsage = extensionNode(OID_KEY_USAGE, true,
126
+ // bits 0 (digitalSignature) + 1 (nonRepudiation) set → 0xC0, 6 unused bits.
127
+ asn1.create(Class.UNIVERSAL, Type.BITSTRING, false, String.fromCharCode(0x06) + String.fromCharCode(0xc0)));
128
+ const extensions = asn1.create(Class.CONTEXT_SPECIFIC, 3, true, [
129
+ asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, [basicConstraints, keyUsage]),
130
+ ]);
131
+ const tbs = asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, [
132
+ version,
133
+ serialNode,
134
+ mlDsaAlgId(), // signature algorithm
135
+ name(), // issuer
136
+ validity,
137
+ name(), // subject (self-signed → issuer == subject)
138
+ spkiNode(pub),
139
+ extensions,
140
+ ]);
141
+ const tbsDer = binToU8(asn1.toDer(tbs).getBytes());
142
+ const signature = ml_dsa65.sign(tbsDer, keys.mldsa65SecretKey);
143
+ const certAsn1 = asn1.create(Class.UNIVERSAL, Type.SEQUENCE, true, [
144
+ tbs,
145
+ mlDsaAlgId(),
146
+ bitStringNode(signature),
147
+ ]);
148
+ const certDer = binToU8(asn1.toDer(certAsn1).getBytes());
149
+ const certPem = derToPem(certDer);
150
+ return {
151
+ certPem,
152
+ certDer,
153
+ fingerprint: sha256Hex(certDer),
154
+ publicKeyFingerprint: sha256Hex(pub),
155
+ notBefore,
156
+ notAfter,
157
+ };
158
+ }
159
+ function derToPem(der) {
160
+ const b64 = Buffer.from(der).toString("base64");
161
+ const lines = b64.match(/.{1,64}/g) ?? [];
162
+ return `-----BEGIN CERTIFICATE-----\n${lines.join("\n")}\n-----END CERTIFICATE-----\n`;
163
+ }
164
+ function pemToDer(pem) {
165
+ const b64 = pem.replace(/-----(BEGIN|END) CERTIFICATE-----/g, "").replace(/\s+/g, "");
166
+ return new Uint8Array(Buffer.from(b64, "base64"));
167
+ }
168
+ /** Read one definite-length DER TLV starting at `offset`. */
169
+ function readTlv(der, offset) {
170
+ if (offset + 1 >= der.length)
171
+ throw new Error("DER: truncated TLV");
172
+ const tag = der[offset];
173
+ let i = offset + 1;
174
+ let len = der[i++];
175
+ if (len & 0x80) {
176
+ const n = len & 0x7f;
177
+ if (n === 0 || n > 4)
178
+ throw new Error("DER: unsupported length encoding");
179
+ len = 0;
180
+ for (let k = 0; k < n; k++)
181
+ len = (len << 8) | der[i++];
182
+ }
183
+ const contentStart = i;
184
+ const end = contentStart + len;
185
+ if (end > der.length)
186
+ throw new Error("DER: length exceeds buffer");
187
+ return { tag, start: offset, contentStart, end };
188
+ }
189
+ /**
190
+ * Parse a self-signed ML-DSA-65 certificate. The TBS bytes are recovered by a
191
+ * raw TLV slice of the original DER (never re-encoded), so signature
192
+ * verification is over the exact signed bytes.
193
+ */
194
+ export function parseMlDsaCertificate(cert) {
195
+ const der = typeof cert === "string" ? pemToDer(cert) : cert;
196
+ // Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue }
197
+ const outer = readTlv(der, 0);
198
+ const tbs = readTlv(der, outer.contentStart);
199
+ const sigAlg = readTlv(der, tbs.end);
200
+ const sigVal = readTlv(der, sigAlg.end);
201
+ const tbsDer = der.slice(tbs.start, tbs.end);
202
+ // signatureValue is a BIT STRING: first content byte = unused-bits count (0).
203
+ const signature = der.slice(sigVal.contentStart + 1, sigVal.end);
204
+ // Human-readable + key fields via node-forge (structure is standard X.509).
205
+ const parsed = asn1.fromDer(forge.util.createBuffer(u8ToBin(der)));
206
+ const tbsNode = parsed.value[0];
207
+ const kids = tbsNode.value;
208
+ // With explicit version [0], fields are: [0]=version [1]=serial [2]=sigAlg
209
+ // [3]=issuer [4]=validity [5]=subject [6]=spki [7]=extensions.
210
+ const validityNode = kids[4];
211
+ const subjectNode = kids[5];
212
+ const spkiNode = kids[6];
213
+ const notBefore = asn1.utcTimeToDate(validityNode.value[0].value);
214
+ const notAfter = asn1.utcTimeToDate(validityNode.value[1].value);
215
+ const subjectCommonName = extractCommonName(subjectNode);
216
+ const spkiKids = spkiNode.value;
217
+ const algOid = asn1.derToOid(spkiKids[0].value[0].value);
218
+ const bitStr = spkiKids[1].value; // leading unused-bits byte
219
+ const publicKey = binToU8(bitStr.slice(1));
220
+ return {
221
+ subjectCommonName,
222
+ notBefore,
223
+ notAfter,
224
+ publicKey,
225
+ publicKeyFingerprint: sha256Hex(publicKey),
226
+ signatureAlgOid: algOid,
227
+ tbsDer,
228
+ signature,
229
+ };
230
+ }
231
+ function extractCommonName(nameNode) {
232
+ for (const rdn of nameNode.value) {
233
+ for (const atv of rdn.value) {
234
+ const [oidNode, valNode] = atv.value;
235
+ if (asn1.derToOid(oidNode.value) === OID_COMMON_NAME) {
236
+ return forge.util.decodeUtf8(valNode.value);
237
+ }
238
+ }
239
+ }
240
+ return "";
241
+ }
242
+ /**
243
+ * Verify a self-signed ML-DSA-65 certificate: correct algorithm, the self
244
+ * signature validates against the embedded public key, and it is within its
245
+ * validity window. Never throws.
246
+ */
247
+ export function verifyMlDsaCertificate(cert, now = new Date()) {
248
+ const failures = [];
249
+ try {
250
+ const p = parseMlDsaCertificate(cert);
251
+ const algOk = p.signatureAlgOid === ID_ML_DSA_65;
252
+ if (!algOk)
253
+ failures.push(`unexpected signature algorithm ${p.signatureAlgOid}`);
254
+ let selfSignatureOk = false;
255
+ if (p.publicKey.length === MLDSA65_PUBLIC_LEN &&
256
+ p.signature.length === MLDSA65_SIGNATURE_LEN) {
257
+ try {
258
+ selfSignatureOk = ml_dsa65.verify(p.signature, p.tbsDer, p.publicKey);
259
+ }
260
+ catch {
261
+ selfSignatureOk = false;
262
+ }
263
+ }
264
+ if (!selfSignatureOk)
265
+ failures.push("ML-DSA-65 self-signature invalid");
266
+ const t = now.getTime();
267
+ const timeValid = t >= p.notBefore.getTime() && t <= p.notAfter.getTime();
268
+ if (!timeValid)
269
+ failures.push("certificate is outside its validity window");
270
+ return { ok: algOk && selfSignatureOk && timeValid, algOk, selfSignatureOk, timeValid, failures };
271
+ }
272
+ catch (e) {
273
+ return {
274
+ ok: false,
275
+ algOk: false,
276
+ selfSignatureOk: false,
277
+ timeValid: false,
278
+ failures: [`certificate parse error: ${e.message}`],
279
+ };
280
+ }
281
+ }
282
+ /**
283
+ * Bind a certificate to a post-quantum seal: the cert must be valid AND its
284
+ * public key must be the one that produced the seal (fingerprint match). This
285
+ * is how a relying party upgrades from raw-fingerprint pinning to an X.509
286
+ * identity without changing the seal format.
287
+ */
288
+ export function certMatchesPqSeal(cert, seal, now = new Date()) {
289
+ const verdict = verifyMlDsaCertificate(cert, now);
290
+ if (!verdict.ok)
291
+ return false;
292
+ const p = parseMlDsaCertificate(cert);
293
+ return p.publicKeyFingerprint === seal.keys.mldsa65Fpr;
294
+ }
295
+ //# sourceMappingURL=pq-cert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pq-cert.js","sourceRoot":"","sources":["../src/pq-cert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { PqSeal } from "./pq-seal.js";
2
+ /**
3
+ * Append `seal` to `pdf` as an incremental update. Returns the new PDF; the first
4
+ * `pdf.length` bytes are preserved verbatim, so `seal.coveredBytes` (set by the
5
+ * caller to `pdf.length`) still points at the original document in the final file.
6
+ */
7
+ export declare function embedPqSeal(pdf: Buffer, seal: PqSeal): Buffer;
8
+ /**
9
+ * Extract the embedded seal from a (possibly later-signed) PDF, or null if none
10
+ * is present / it is not valid JSON. Returns the FIRST seal if several exist.
11
+ *
12
+ * FIRST, deliberately: the genuine seal is embedded BEFORE the RSA PAdES
13
+ * signature, so it sits at the lowest file offset and is covered by the classical
14
+ * /ByteRange. A seal an attacker appends AFTER signing lands at a higher offset
15
+ * and is NOT RSA-covered — taking the first match ignores it, so even a standalone
16
+ * `verifyPqSeal` reports the authentic signer rather than the appended identity.
17
+ * Never throws — a malformed seal is treated as absent (fail-closed at verify).
18
+ */
19
+ export declare function extractPqSeal(pdf: Buffer): PqSeal | null;
20
+ /** True if the PDF carries an embedded post-quantum seal. */
21
+ export declare function hasPqSeal(pdf: Buffer): boolean;
22
+ //# sourceMappingURL=pq-embed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pq-embed.d.ts","sourceRoot":"","sources":["../src/pq-embed.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AA+C3C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAiC7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASxD;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAG9C"}
@@ -0,0 +1,119 @@
1
+ // pq-embed.ts
2
+ //
3
+ // Embed a post-quantum seal (see pq-seal.ts) into a rendered PDF as an
4
+ // append-only incremental update, and extract it back out.
5
+ //
6
+ // WHY append-only: the seal signs SHA-256 of the document bytes it covers
7
+ // (`coveredBytes` = length of the pre-seal PDF, P0). An incremental update never
8
+ // rewrites prior bytes, so in the final signed file P2, the prefix P2[0:coveredBytes]
9
+ // is byte-identical to P0 — the verifier recovers exactly what the seal signed.
10
+ // This ordering (embed seal → THEN apply the RSA PAdES signature) also lets the
11
+ // classical /ByteRange signature cryptographically cover the seal.
12
+ //
13
+ // The incremental update we append is deliberately minimal and mirrors the exact
14
+ // xref/trailer byte layout @signpdf/placeholder-plain expects (it later parses
15
+ // this file to add its own signature layer): one new indirect object holding the
16
+ // base64 seal, one single-subsection classic xref, a trailer that copies the
17
+ // document's /Root (+ /Info) and points /Prev at the previous xref, and a clean
18
+ // `startxref … %%EOF` tail.
19
+ //
20
+ // Retrieval scans for the object's `/Seal(<base64>)` literal. Base64's alphabet
21
+ // (A–Z a–z 0–9 + / =) contains no PDF-string metacharacters, so no escaping is
22
+ // needed and a simple regex is unambiguous. @signpdf never rewrites these bytes,
23
+ // so the seal survives the subsequent signing pass verbatim.
24
+ /** Marker object type; also the retrieval anchor. */
25
+ const SEAL_OBJ_TYPE = "ESigPQSeal";
26
+ const SEAL_EXTRACT_RE = new RegExp(`/Type\\s*/${SEAL_OBJ_TYPE}/V\\s+1/Seal\\(([A-Za-z0-9+/=]+)\\)`, "g");
27
+ /**
28
+ * Parse the most recent classic trailer to recover the fields our incremental
29
+ * update must carry forward: /Root, optional /Info, /Size (→ next free object
30
+ * number), and the last xref offset (→ our /Prev). Throws with a clear message
31
+ * on xref-stream PDFs, which the whole signing pipeline (@signpdf included)
32
+ * doesn't support anyway.
33
+ */
34
+ function readLastTrailer(pdf) {
35
+ const text = pdf.toString("latin1"); // 1 byte ↔ 1 char, so string index === byte offset
36
+ const sxIdx = text.lastIndexOf("startxref");
37
+ if (sxIdx < 0)
38
+ throw new Error("pq-embed: no startxref found (not a classic PDF)");
39
+ const prevMatch = text.slice(sxIdx + "startxref".length).match(/\d+/);
40
+ if (!prevMatch)
41
+ throw new Error("pq-embed: could not read previous startxref offset");
42
+ const prevStartxref = parseInt(prevMatch[0], 10);
43
+ const trIdx = text.lastIndexOf("trailer");
44
+ if (trIdx < 0 || trIdx > sxIdx) {
45
+ throw new Error("pq-embed: classic trailer not found (xref-stream PDFs are unsupported)");
46
+ }
47
+ const dict = text.slice(trIdx, sxIdx);
48
+ const root = dict.match(/\/Root\s+(\d+\s+\d+\s+R)/);
49
+ const info = dict.match(/\/Info\s+(\d+\s+\d+\s+R)/);
50
+ const size = dict.match(/\/Size\s+(\d+)/);
51
+ if (!root)
52
+ throw new Error("pq-embed: trailer has no /Root");
53
+ if (!size)
54
+ throw new Error("pq-embed: trailer has no /Size");
55
+ return {
56
+ rootRef: root[1].replace(/\s+/g, " "),
57
+ infoRef: info?.[1].replace(/\s+/g, " "),
58
+ size: parseInt(size[1], 10),
59
+ prevStartxref,
60
+ };
61
+ }
62
+ /**
63
+ * Append `seal` to `pdf` as an incremental update. Returns the new PDF; the first
64
+ * `pdf.length` bytes are preserved verbatim, so `seal.coveredBytes` (set by the
65
+ * caller to `pdf.length`) still points at the original document in the final file.
66
+ */
67
+ export function embedPqSeal(pdf, seal) {
68
+ const trailer = readLastTrailer(pdf);
69
+ const sealB64 = Buffer.from(JSON.stringify(seal), "utf8").toString("base64");
70
+ const objNum = trailer.size; // next free object number
71
+ // Separate our update from the prior %%EOF with a newline (harmless if the
72
+ // file already ended in one).
73
+ const head = Buffer.concat([pdf, Buffer.from("\n", "latin1")]);
74
+ const objOffset = head.length;
75
+ const objBuf = Buffer.from(`${objNum} 0 obj\n<</Type/${SEAL_OBJ_TYPE}/V 1/Seal(${sealB64})>>\nendobj\n`, "latin1");
76
+ const withObj = Buffer.concat([head, objBuf]);
77
+ const xrefOffset = withObj.length;
78
+ const paddedOffset = String(objOffset).padStart(10, "0");
79
+ const xref = Buffer.from("xref\n" +
80
+ `${objNum} 1\n` +
81
+ `${paddedOffset} 00000 n \n` + // 20-byte entry: 10 offset + gen 00000 + 'n' + trailing space + LF
82
+ "trailer\n<<\n" +
83
+ `/Size ${objNum + 1}\n` +
84
+ `/Root ${trailer.rootRef}\n` +
85
+ (trailer.infoRef ? `/Info ${trailer.infoRef}\n` : "") +
86
+ `/Prev ${trailer.prevStartxref}\n` +
87
+ ">>\nstartxref\n" +
88
+ `${xrefOffset}\n%%EOF\n`, "latin1");
89
+ return Buffer.concat([withObj, xref]);
90
+ }
91
+ /**
92
+ * Extract the embedded seal from a (possibly later-signed) PDF, or null if none
93
+ * is present / it is not valid JSON. Returns the FIRST seal if several exist.
94
+ *
95
+ * FIRST, deliberately: the genuine seal is embedded BEFORE the RSA PAdES
96
+ * signature, so it sits at the lowest file offset and is covered by the classical
97
+ * /ByteRange. A seal an attacker appends AFTER signing lands at a higher offset
98
+ * and is NOT RSA-covered — taking the first match ignores it, so even a standalone
99
+ * `verifyPqSeal` reports the authentic signer rather than the appended identity.
100
+ * Never throws — a malformed seal is treated as absent (fail-closed at verify).
101
+ */
102
+ export function extractPqSeal(pdf) {
103
+ SEAL_EXTRACT_RE.lastIndex = 0;
104
+ const match = SEAL_EXTRACT_RE.exec(pdf.toString("latin1"));
105
+ if (!match)
106
+ return null;
107
+ try {
108
+ return JSON.parse(Buffer.from(match[1], "base64").toString("utf8"));
109
+ }
110
+ catch {
111
+ return null;
112
+ }
113
+ }
114
+ /** True if the PDF carries an embedded post-quantum seal. */
115
+ export function hasPqSeal(pdf) {
116
+ SEAL_EXTRACT_RE.lastIndex = 0;
117
+ return SEAL_EXTRACT_RE.test(pdf.toString("latin1"));
118
+ }
119
+ //# sourceMappingURL=pq-embed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pq-embed.js","sourceRoot":"","sources":["../src/pq-embed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { type PqSigningKeys, type PqPublicMaterial } from "./pq-seal.js";
2
+ /** A persisted, wrapped post-quantum key bundle + its public identity material. */
3
+ export interface StoredPqKeys {
4
+ id: string;
5
+ tenantId: string;
6
+ /** `wrapPqKeyBundle` output (AES-256-GCM). Never store the unwrapped bundle. */
7
+ keyBundleEncrypted: Uint8Array;
8
+ /** base64 raw Ed25519 public key. */
9
+ ed25519Public: string;
10
+ /** base64 raw ML-DSA-65 public key. */
11
+ mldsa65Public: string;
12
+ /** SHA-256 hex of the ML-DSA-65 public key — the identity to publish/pin. */
13
+ mldsa65Fpr: string;
14
+ /** 128-bit hex id over both public keys. */
15
+ keyId: string;
16
+ active: boolean;
17
+ rotatedFromId?: string | null;
18
+ createdAt: Date;
19
+ }
20
+ /** Bring-your-own persistence for post-quantum key bundles (mirrors CertStore). */
21
+ export interface PqKeyStore {
22
+ /** The active bundle for a tenant, or null if none exists yet. */
23
+ findActive(tenantId: string): Promise<StoredPqKeys | null>;
24
+ /** Persist a new bundle. Ensure at most one `active=true` per tenant. */
25
+ insert(input: {
26
+ tenantId: string;
27
+ keyBundleEncrypted: Uint8Array;
28
+ public: PqPublicMaterial;
29
+ rotatedFromId?: string | null;
30
+ }): Promise<StoredPqKeys>;
31
+ /** Mark a bundle inactive (used during rotation). */
32
+ deactivate(id: string): Promise<void>;
33
+ }
34
+ export interface EnsurePqKeysResult {
35
+ record: StoredPqKeys;
36
+ /** In-memory signing keys ready for `signPdf({ pqSeal: { keys } })`. */
37
+ keys: PqSigningKeys;
38
+ public: PqPublicMaterial;
39
+ }
40
+ /**
41
+ * Ensure an active post-quantum key bundle for the tenant, generating + wrapping
42
+ * one on first use. `passphrase` (≥24 chars) wraps the bundle at rest.
43
+ */
44
+ export declare function ensureActivePqKeys(opts: {
45
+ store: PqKeyStore;
46
+ tenantId: string;
47
+ passphrase: string;
48
+ }): Promise<EnsurePqKeysResult>;
49
+ /**
50
+ * Force a rotation: deactivate the current bundle (if any) and mint a fresh one
51
+ * with `rotatedFromId` set. Documents already sealed with the old key keep
52
+ * verifying — verification uses the public key embedded in each seal.
53
+ */
54
+ export declare function rotatePqKeys(opts: {
55
+ store: PqKeyStore;
56
+ tenantId: string;
57
+ passphrase: string;
58
+ }): Promise<EnsurePqKeysResult>;
59
+ //# sourceMappingURL=pq-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pq-lifecycle.d.ts","sourceRoot":"","sources":["../src/pq-lifecycle.ts"],"names":[],"mappings":"AAeA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AAEtB,mFAAmF;AACnF,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,kBAAkB,EAAE,UAAU,CAAC;IAC/B,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,mFAAmF;AACnF,MAAM,WAAW,UAAU;IACzB,kEAAkE;IAClE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC3D,yEAAyE;IACzE,MAAM,CAAC,KAAK,EAAE;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,kBAAkB,EAAE,UAAU,CAAC;QAC/B,MAAM,EAAE,gBAAgB,CAAC;QACzB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1B,qDAAqD;IACrD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,YAAY,CAAC;IACrB,wEAAwE;IACxE,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAiB9B;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAI9B"}
@@ -0,0 +1,55 @@
1
+ // pq-lifecycle.ts
2
+ //
3
+ // Stack-agnostic "ensure an active post-quantum key bundle for this tenant"
4
+ // helper — the ML-DSA-65 / Ed25519 analogue of ensureActiveCert. Depends only on
5
+ // the bring-your-own `PqKeyStore` interface plus the crypto in pq-seal; no DB, no
6
+ // stack assumptions.
7
+ //
8
+ // Unlike signing certificates, hybrid key bundles do not expire on a clock —
9
+ // rotation is an explicit, deployment-driven decision (e.g. suspected key
10
+ // compromise, or a policy roll). `ensureActivePqKeys` therefore generates on
11
+ // first use and reuses thereafter; `rotatePqKeys` mints a fresh bundle and links
12
+ // the predecessor. Consumers that don't want a managed store can skip this module
13
+ // entirely and use generatePqKeyBundle / wrapPqKeyBundle / loadPqSigningKeys
14
+ // directly, persisting the wrapped blob wherever they like.
15
+ import { generatePqKeyBundle, loadPqSigningKeys, wrapPqKeyBundle, unwrapPqKeyBundle, } from "./pq-seal.js";
16
+ /**
17
+ * Ensure an active post-quantum key bundle for the tenant, generating + wrapping
18
+ * one on first use. `passphrase` (≥24 chars) wraps the bundle at rest.
19
+ */
20
+ export async function ensureActivePqKeys(opts) {
21
+ const existing = await opts.store.findActive(opts.tenantId);
22
+ if (existing) {
23
+ const bundle = unwrapPqKeyBundle(existing.keyBundleEncrypted, opts.passphrase);
24
+ const keys = loadPqSigningKeys(bundle);
25
+ return {
26
+ record: existing,
27
+ keys,
28
+ public: {
29
+ ed25519: existing.ed25519Public,
30
+ mldsa65: existing.mldsa65Public,
31
+ mldsa65Fpr: existing.mldsa65Fpr,
32
+ keyId: existing.keyId,
33
+ },
34
+ };
35
+ }
36
+ return mint(opts.store, opts.tenantId, opts.passphrase, null);
37
+ }
38
+ /**
39
+ * Force a rotation: deactivate the current bundle (if any) and mint a fresh one
40
+ * with `rotatedFromId` set. Documents already sealed with the old key keep
41
+ * verifying — verification uses the public key embedded in each seal.
42
+ */
43
+ export async function rotatePqKeys(opts) {
44
+ const existing = await opts.store.findActive(opts.tenantId);
45
+ if (existing)
46
+ await opts.store.deactivate(existing.id);
47
+ return mint(opts.store, opts.tenantId, opts.passphrase, existing?.id ?? null);
48
+ }
49
+ async function mint(store, tenantId, passphrase, rotatedFromId) {
50
+ const { bundle, public: pub } = generatePqKeyBundle();
51
+ const keyBundleEncrypted = wrapPqKeyBundle(bundle, passphrase);
52
+ const record = await store.insert({ tenantId, keyBundleEncrypted, public: pub, rotatedFromId });
53
+ return { record, keys: loadPqSigningKeys(bundle), public: pub };
54
+ }
55
+ //# sourceMappingURL=pq-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pq-lifecycle.js","sourceRoot":"","sources":["../src/pq-lifecycle.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAClB,EAAE;AACF,4EAA4E;AAC5E,iFAAiF;AACjF,kFAAkF;AAClF,qBAAqB;AACrB,EAAE;AACF,6EAA6E;AAC7E,0EAA0E;AAC1E,6EAA6E;AAC7E,iFAAiF;AACjF,kFAAkF;AAClF,6EAA6E;AAC7E,4DAA4D;AAE5D,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,GAGlB,MAAM,cAAc,CAAC;AA2CtB;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAIxC;IACC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,kBAAkB,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/E,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACvC,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,IAAI;YACJ,MAAM,EAAE;gBACN,OAAO,EAAE,QAAQ,CAAC,aAAa;gBAC/B,OAAO,EAAE,QAAQ,CAAC,aAAa;gBAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,KAAK,EAAE,QAAQ,CAAC,KAAK;aACtB;SACF,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AAChE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAIlC;IACC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,QAAQ;QAAE,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvD,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,EAAE,IAAI,IAAI,CAAC,CAAC;AAChF,CAAC;AAED,KAAK,UAAU,IAAI,CACjB,KAAiB,EACjB,QAAgB,EAChB,UAAkB,EAClB,aAA4B;IAE5B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,mBAAmB,EAAE,CAAC;IACtD,MAAM,kBAAkB,GAAG,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC;IAChG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAClE,CAAC"}