@embedpdf/core-signature 3.0.0-next.14 → 3.0.0-next.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,115 +1,11 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
2
+ const require_engine = require("./engine-BPmX1KyF.cjs");
3
+ const require_oids = require("./oids-BUfXTCCm.cjs");
24
4
  let asn1js = require("asn1js");
25
- asn1js = __toESM(asn1js, 1);
5
+ asn1js = require_engine.__toESM(asn1js, 1);
26
6
  let pkijs = require("pkijs");
27
- pkijs = __toESM(pkijs, 1);
28
- //#region src/cms/engine.ts
29
- let ensured = false;
30
- /**
31
- * PKI.js needs a WebCrypto engine. Browsers and Node ≥ 19 expose
32
- * `globalThis.crypto`; register it once, lazily, so importing this package
33
- * has no side effect.
34
- */
35
- function ensureEngine() {
36
- if (!ensured) {
37
- ensured = true;
38
- if (!pkijs.getCrypto()) {
39
- const webcrypto = globalThis.crypto;
40
- if (!webcrypto?.subtle) throw new Error("@embedpdf/core-signature needs WebCrypto (globalThis.crypto.subtle)");
41
- pkijs.setEngine("embedpdf", new pkijs.CryptoEngine({
42
- name: "embedpdf",
43
- crypto: webcrypto
44
- }));
45
- }
46
- }
47
- return pkijs.getCrypto(true);
48
- }
49
- function toArrayBuffer(view) {
50
- const copy = new Uint8Array(view.byteLength);
51
- copy.set(view);
52
- return copy.buffer;
53
- }
54
- function bytesEqual(a, b) {
55
- if (a.byteLength !== b.byteLength) return false;
56
- for (let i = 0; i < a.byteLength; i++) if (a[i] !== b[i]) return false;
57
- return true;
58
- }
59
- //#endregion
60
- //#region src/cms/oids.ts
61
- const OID = {
62
- data: "1.2.840.113549.1.7.1",
63
- signedData: "1.2.840.113549.1.7.2",
64
- contentType: "1.2.840.113549.1.9.3",
65
- messageDigest: "1.2.840.113549.1.9.4",
66
- signingTime: "1.2.840.113549.1.9.5",
67
- signingCertificateV2: "1.2.840.113549.1.9.16.2.47",
68
- timestampToken: "1.2.840.113549.1.9.16.2.14",
69
- sha1: "1.3.14.3.2.26",
70
- sha256: "2.16.840.1.101.3.4.2.1",
71
- sha384: "2.16.840.1.101.3.4.2.2",
72
- sha512: "2.16.840.1.101.3.4.2.3",
73
- rsaEncryption: "1.2.840.113549.1.1.1",
74
- sha256WithRsa: "1.2.840.113549.1.1.11",
75
- sha384WithRsa: "1.2.840.113549.1.1.12",
76
- sha512WithRsa: "1.2.840.113549.1.1.13",
77
- rsaPss: "1.2.840.113549.1.1.10",
78
- mgf1: "1.2.840.113549.1.1.8",
79
- ecdsaSha256: "1.2.840.10045.4.3.2",
80
- ecdsaSha384: "1.2.840.10045.4.3.3",
81
- ecdsaSha512: "1.2.840.10045.4.3.4",
82
- subjectKeyIdentifier: "2.5.29.14",
83
- keyUsage: "2.5.29.15",
84
- basicConstraints: "2.5.29.19",
85
- commonName: "2.5.4.3"
86
- };
87
- const DIGEST_BY_OID = {
88
- [OID.sha1]: "sha1",
89
- [OID.sha256]: "sha256",
90
- [OID.sha384]: "sha384",
91
- [OID.sha512]: "sha512"
92
- };
93
- const OID_BY_DIGEST = {
94
- sha1: OID.sha1,
95
- sha256: OID.sha256,
96
- sha384: OID.sha384,
97
- sha512: OID.sha512
98
- };
99
- /** WebCrypto hash names. */
100
- const WEBCRYPTO_HASH = {
101
- sha1: "SHA-1",
102
- sha256: "SHA-256",
103
- sha384: "SHA-384",
104
- sha512: "SHA-512"
105
- };
106
- const DIGEST_LENGTH = {
107
- sha1: 20,
108
- sha256: 32,
109
- sha384: 48,
110
- sha512: 64
111
- };
112
- //#endregion
7
+ pkijs = require_engine.__toESM(pkijs, 1);
8
+ let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
113
9
  //#region src/cms/parse.ts
114
10
  var CmsError = class extends Error {
115
11
  constructor(reason, message) {
@@ -122,50 +18,58 @@ var CmsError = class extends Error {
122
18
  function parseDetachedCms(cms) {
123
19
  return parseCmsInternal(cms).parsed;
124
20
  }
125
- function parseCmsInternal(cms) {
21
+ function parseCmsInternal(cms, content = "detached") {
126
22
  if (cms.byteLength === 0) throw new CmsError("malformed", "empty CMS");
127
- const asn1 = asn1js.fromBER(toArrayBuffer(cms));
23
+ const asn1 = asn1js.fromBER(require_engine.toArrayBuffer(cms));
128
24
  if (asn1.offset === -1) throw new CmsError("malformed", "CMS is not valid BER");
129
25
  let contentInfo;
130
26
  let signedData;
131
27
  try {
132
28
  contentInfo = new pkijs.ContentInfo({ schema: asn1.result });
133
- if (contentInfo.contentType !== OID.signedData) throw new CmsError("unsupported", `CMS content type ${contentInfo.contentType} is not SignedData`);
29
+ if (contentInfo.contentType !== require_oids.OID.signedData) throw new CmsError("unsupported", `CMS content type ${contentInfo.contentType} is not SignedData`);
134
30
  signedData = new pkijs.SignedData({ schema: contentInfo.content });
135
- } catch (err) {
136
- if (err instanceof CmsError) throw err;
137
- throw new CmsError("malformed", `CMS does not parse as SignedData: ${err.message}`);
31
+ } catch (error) {
32
+ if (error instanceof CmsError) throw error;
33
+ throw new CmsError("malformed", `CMS does not parse as SignedData: ${error.message}`);
34
+ }
35
+ const encapsulated = signedData.encapContentInfo;
36
+ const expectedContentType = content === "tst-info" ? require_oids.OID.tstInfo : require_oids.OID.data;
37
+ let eContent = null;
38
+ if (content === "detached") {
39
+ if (encapsulated.eContent) throw new CmsError("unsupported", "CMS carries encapsulated content; a PDF signature is detached");
40
+ } else {
41
+ if (encapsulated.eContentType !== require_oids.OID.tstInfo || !encapsulated.eContent) throw new CmsError("malformed", "a timestamp token must carry a TSTInfo");
42
+ eContent = new Uint8Array(encapsulated.eContent.getValue());
138
43
  }
139
- if (signedData.encapContentInfo.eContent) throw new CmsError("unsupported", "CMS carries encapsulated content; a PDF signature is detached");
140
44
  if (signedData.signerInfos.length !== 1) throw new CmsError("unsupported", `CMS has ${signedData.signerInfos.length} signers; exactly one is expected`);
141
45
  const signerInfo = signedData.signerInfos[0];
142
- const digestAlgorithm = DIGEST_BY_OID[signerInfo.digestAlgorithm.algorithmId];
46
+ const digestAlgorithm = require_oids.DIGEST_BY_OID[signerInfo.digestAlgorithm.algorithmId];
143
47
  if (!digestAlgorithm) throw new CmsError("unsupported", `digest algorithm ${signerInfo.digestAlgorithm.algorithmId}`);
144
- const certificates = (signedData.certificates ?? []).filter((c) => c instanceof pkijs.Certificate);
48
+ const certificates = (signedData.certificates ?? []).filter((certificate) => certificate instanceof pkijs.Certificate);
145
49
  const signerCertificate = findSignerCertificate(signerInfo, certificates);
146
50
  if (!signerCertificate) throw new CmsError("malformed", "the signer certificate is not carried by the CMS");
147
51
  const signedAttrs = signerInfo.signedAttrs?.attributes ?? [];
148
52
  if (signedAttrs.length === 0) throw new CmsError("unsupported", "CMS has no signed attributes; a PDF signature needs message-digest");
149
- const attr = (type) => signedAttrs.find((a) => a.type === type);
150
- const contentType = attr(OID.contentType);
151
- if (contentType && contentType.values[0]?.valueBlock?.toString?.() !== OID.data) {
53
+ const attr = (type) => signedAttrs.find((attribute) => attribute.type === type);
54
+ const contentType = attr(require_oids.OID.contentType);
55
+ if (contentType) {
152
56
  const oid = contentType.values[0];
153
- if (!oid || oid.valueBlock.toString() !== OID.data) throw new CmsError("unsupported", "CMS content-type attribute is not id-data");
57
+ if (!oid || oid.valueBlock.toString() !== expectedContentType) throw new CmsError("unsupported", `CMS content-type attribute is not ${content === "tst-info" ? "id-ct-TSTInfo" : "id-data"}`);
154
58
  }
155
- const messageDigestValue = attr(OID.messageDigest)?.values[0];
59
+ const messageDigestValue = attr(require_oids.OID.messageDigest)?.values[0];
156
60
  if (!messageDigestValue || !(messageDigestValue instanceof asn1js.OctetString)) throw new CmsError("malformed", "CMS has no message-digest attribute");
157
61
  const messageDigest = new Uint8Array(messageDigestValue.valueBlock.valueHexView);
158
62
  let signingTime = null;
159
- const timeValue = attr(OID.signingTime)?.values[0];
63
+ const timeValue = attr(require_oids.OID.signingTime)?.values[0];
160
64
  if (timeValue instanceof asn1js.UTCTime || timeValue instanceof asn1js.GeneralizedTime) signingTime = timeValue.toDate();
161
- const essAttr = attr(OID.signingCertificateV2);
65
+ const essAttr = attr(require_oids.OID.signingCertificateV2);
162
66
  let essCertHash = null;
163
67
  if (essAttr) {
164
68
  const members = ((essAttr.values[0]?.valueBlock?.value?.[0])?.valueBlock?.value?.[0])?.valueBlock?.value ?? [];
165
69
  let cursor = 0;
166
70
  const head = members[0];
167
71
  if (head instanceof asn1js.Sequence && head.valueBlock.value[0] instanceof asn1js.ObjectIdentifier) {
168
- if (head.valueBlock.value[0].valueBlock.toString() !== OID.sha256) throw new CmsError("unsupported", "signing-certificate-v2 uses a hash other than SHA-256");
72
+ if (head.valueBlock.value[0].valueBlock.toString() !== require_oids.OID.sha256) throw new CmsError("unsupported", "signing-certificate-v2 uses a hash other than SHA-256");
169
73
  cursor = 1;
170
74
  }
171
75
  const hash = members[cursor];
@@ -173,9 +77,9 @@ function parseCmsInternal(cms) {
173
77
  essCertHash = new Uint8Array(hash.valueBlock.valueHexView);
174
78
  }
175
79
  let timestampToken = null;
176
- const tokenAttr = signerInfo.unsignedAttrs?.attributes.find((a) => a.type === OID.timestampToken);
80
+ const tokenAttr = signerInfo.unsignedAttrs?.attributes.find((attribute) => attribute.type === require_oids.OID.timestampToken);
177
81
  if (tokenAttr?.values[0]) timestampToken = new Uint8Array(tokenAttr.values[0].toBER(false));
178
- const ordered = [signerCertificate, ...certificates.filter((c) => c !== signerCertificate)];
82
+ const ordered = [signerCertificate, ...certificates.filter((certificate) => certificate !== signerCertificate)];
179
83
  return {
180
84
  parsed: {
181
85
  digestAlgorithm,
@@ -191,7 +95,8 @@ function parseCmsInternal(cms) {
191
95
  signerInfo,
192
96
  signerCertificate,
193
97
  certificates: ordered,
194
- essCertHash
98
+ essCertHash,
99
+ eContent
195
100
  };
196
101
  }
197
102
  function certDer(cert) {
@@ -200,24 +105,24 @@ function certDer(cert) {
200
105
  function findSignerCertificate(signerInfo, certificates) {
201
106
  const sid = signerInfo.sid;
202
107
  if (sid instanceof pkijs.IssuerAndSerialNumber) {
203
- for (const cert of certificates) if (cert.issuer.isEqual(sid.issuer) && bytesEqual(new Uint8Array(cert.serialNumber.valueBlock.valueHexView), new Uint8Array(sid.serialNumber.valueBlock.valueHexView))) return cert;
108
+ for (const cert of certificates) if (cert.issuer.isEqual(sid.issuer) && require_engine.bytesEqual(new Uint8Array(cert.serialNumber.valueBlock.valueHexView), new Uint8Array(sid.serialNumber.valueBlock.valueHexView))) return cert;
204
109
  return null;
205
110
  }
206
111
  const ski = sid?.valueBlock?.valueHexView;
207
112
  if (!ski) return null;
208
113
  for (const cert of certificates) {
209
- const ext = cert.extensions?.find((e) => e.extnID === OID.subjectKeyIdentifier);
114
+ const ext = cert.extensions?.find((extension) => extension.extnID === require_oids.OID.subjectKeyIdentifier);
210
115
  const value = ext ? asn1js.fromBER(ext.extnValue.valueBlock.valueHexView) : null;
211
116
  const inner = value && value.offset !== -1 ? value.result : null;
212
- if (inner && bytesEqual(new Uint8Array(inner.valueBlock.valueHexView), new Uint8Array(ski))) return cert;
117
+ if (inner && require_engine.bytesEqual(new Uint8Array(inner.valueBlock.valueHexView), new Uint8Array(ski))) return cert;
213
118
  }
214
119
  return null;
215
120
  }
216
121
  //#endregion
217
122
  //#region src/cms/verify.ts
218
- /** The DER of the signed attributes as they are signed: re-tagged as a SET. */
123
+ /** The DER of the signed attributes as they are signed: re-tagged as a set. */
219
124
  function signedAttributesDer(signerInfo) {
220
- const set = new asn1js.Set({ value: (signerInfo.signedAttrs?.attributes ?? []).map((a) => a.toSchema()) });
125
+ const set = new asn1js.Set({ value: (signerInfo.signedAttrs?.attributes ?? []).map((attribute) => attribute.toSchema()) });
221
126
  return new Uint8Array(set.toBER(false));
222
127
  }
223
128
  /**
@@ -231,18 +136,18 @@ async function verifyCmsSignature(cms) {
231
136
  let internal;
232
137
  try {
233
138
  internal = cms instanceof Uint8Array ? parseCmsInternal(cms) : cms;
234
- } catch (err) {
235
- return err instanceof CmsError && err.reason === "unsupported" ? "unsupported" : "invalid";
139
+ } catch (error) {
140
+ return error instanceof CmsError && error.reason === "unsupported" ? "unsupported" : "invalid";
236
141
  }
237
- const engine = ensureEngine();
142
+ const engine = require_engine.ensureEngine();
238
143
  const { signerInfo, signerCertificate, parsed, essCertHash } = internal;
239
144
  if (essCertHash) {
240
- if (!bytesEqual(new Uint8Array(await engine.digest("SHA-256", toArrayBuffer(parsed.signerCertificate))), essCertHash)) return "invalid";
145
+ if (!require_engine.bytesEqual(new Uint8Array(await engine.digest("SHA-256", require_engine.toArrayBuffer(parsed.signerCertificate))), essCertHash)) return "invalid";
241
146
  }
242
147
  try {
243
- return await engine.verifyWithPublicKey(toArrayBuffer(signedAttributesDer(signerInfo)), signerInfo.signature, signerCertificate.subjectPublicKeyInfo, signerInfo.signatureAlgorithm, WEBCRYPTO_HASH[parsed.digestAlgorithm]) ? "valid" : "invalid";
244
- } catch (err) {
245
- const message = err.message ?? "";
148
+ return await engine.verifyWithPublicKey(require_engine.toArrayBuffer(signedAttributesDer(signerInfo)), signerInfo.signature, signerCertificate.subjectPublicKeyInfo, signerInfo.signatureAlgorithm, require_oids.WEBCRYPTO_HASH[parsed.digestAlgorithm]) ? "valid" : "invalid";
149
+ } catch (error) {
150
+ const message = error.message ?? "";
246
151
  return /unsupported|unknown|not supported|not implemented/i.test(message) ? "unsupported" : "invalid";
247
152
  }
248
153
  }
@@ -255,25 +160,25 @@ async function verifyCmsSignature(cms) {
255
160
  * serial number.
256
161
  */
257
162
  async function buildDetachedCms(input) {
258
- const engine = ensureEngine();
259
- if (input.digest.byteLength !== DIGEST_LENGTH[input.hash]) throw new Error(`digest is ${input.digest.byteLength} bytes; ${input.hash} needs ${DIGEST_LENGTH[input.hash]}`);
163
+ const engine = require_engine.ensureEngine();
164
+ if (input.digest.byteLength !== require_oids.DIGEST_LENGTH[input.hash]) throw new Error(`digest is ${input.digest.byteLength} bytes; ${input.hash} needs ${require_oids.DIGEST_LENGTH[input.hash]}`);
260
165
  if (input.signer.hash !== input.hash) throw new Error(`the signer hashes with ${input.signer.hash}, the document was prepared with ${input.hash}`);
261
166
  if (input.signer.certificateChain.length === 0) throw new Error("the signer carries no certificate");
262
- const chain = input.signer.certificateChain.map((der) => pkijs.Certificate.fromBER(toArrayBuffer(der)));
167
+ const chain = input.signer.certificateChain.map((der) => pkijs.Certificate.fromBER(require_engine.toArrayBuffer(der)));
263
168
  const signerCert = chain[0];
264
169
  const attributes = [new pkijs.Attribute({
265
- type: OID.contentType,
266
- values: [new asn1js.ObjectIdentifier({ value: OID.data })]
170
+ type: require_oids.OID.contentType,
171
+ values: [new asn1js.ObjectIdentifier({ value: require_oids.OID.data })]
267
172
  }), new pkijs.Attribute({
268
- type: OID.messageDigest,
269
- values: [new asn1js.OctetString({ valueHex: toArrayBuffer(input.digest) })]
173
+ type: require_oids.OID.messageDigest,
174
+ values: [new asn1js.OctetString({ valueHex: require_engine.toArrayBuffer(input.digest) })]
270
175
  })];
271
176
  if (input.profile === "pkcs7" && input.signingTime) attributes.push(new pkijs.Attribute({
272
- type: OID.signingTime,
177
+ type: require_oids.OID.signingTime,
273
178
  values: [new asn1js.UTCTime({ valueDate: input.signingTime })]
274
179
  }));
275
180
  if (input.profile === "cades-b") {
276
- const certHash = await engine.digest("SHA-256", toArrayBuffer(input.signer.certificateChain[0]));
181
+ const certHash = await engine.digest("SHA-256", require_engine.toArrayBuffer(input.signer.certificateChain[0]));
277
182
  const issuerSerial = new pkijs.IssuerSerial({
278
183
  issuer: new pkijs.GeneralNames({ names: [new pkijs.GeneralName({
279
184
  type: 4,
@@ -283,11 +188,11 @@ async function buildDetachedCms(input) {
283
188
  });
284
189
  const essCertId = new asn1js.Sequence({ value: [new asn1js.OctetString({ valueHex: certHash }), issuerSerial.toSchema()] });
285
190
  attributes.push(new pkijs.Attribute({
286
- type: OID.signingCertificateV2,
191
+ type: require_oids.OID.signingCertificateV2,
287
192
  values: [new asn1js.Sequence({ value: [new asn1js.Sequence({ value: [essCertId] })] })]
288
193
  }));
289
194
  }
290
- const digestAlgorithm = new pkijs.AlgorithmIdentifier({ algorithmId: OID_BY_DIGEST[input.hash] });
195
+ const digestAlgorithm = new pkijs.AlgorithmIdentifier({ algorithmId: require_oids.OID_BY_DIGEST[input.hash] });
291
196
  const signerInfo = new pkijs.SignerInfo({
292
197
  version: 1,
293
198
  sid: new pkijs.IssuerAndSerialNumber({
@@ -301,59 +206,166 @@ async function buildDetachedCms(input) {
301
206
  }),
302
207
  signatureAlgorithm: signatureAlgorithmFor(input.signer.algorithm, input.hash)
303
208
  });
304
- const toSign = new asn1js.Set({ value: attributes.map((a) => a.toSchema()) }).toBER(false);
209
+ const toSign = new asn1js.Set({ value: attributes.map((attribute) => attribute.toSchema()) }).toBER(false);
305
210
  const raw = await input.signer.sign(new Uint8Array(toSign));
306
- const signature = input.signer.algorithm === "ECDSA" ? pkijs.createCMSECDSASignature(toArrayBuffer(raw)) : toArrayBuffer(raw);
307
- signerInfo.signature = new asn1js.OctetString({ valueHex: signature });
211
+ signerInfo.signature = new asn1js.OctetString({ valueHex: cmsSignatureValue(input.signer.algorithm, raw) });
308
212
  const signedData = new pkijs.SignedData({
309
213
  version: 1,
310
214
  digestAlgorithms: [digestAlgorithm],
311
- encapContentInfo: new pkijs.EncapsulatedContentInfo({ eContentType: OID.data }),
215
+ encapContentInfo: new pkijs.EncapsulatedContentInfo({ eContentType: require_oids.OID.data }),
312
216
  certificates: chain,
313
217
  signerInfos: [signerInfo]
314
218
  });
315
219
  const contentInfo = new pkijs.ContentInfo({
316
- contentType: OID.signedData,
220
+ contentType: require_oids.OID.signedData,
317
221
  content: signedData.toSchema(true)
318
222
  });
319
223
  return new Uint8Array(contentInfo.toSchema().toBER(false));
320
224
  }
225
+ /** A raw signature as a SignerInfo carries it (ECDSA `r || s` becomes DER). */
226
+ function cmsSignatureValue(algorithm, raw) {
227
+ return algorithm === "ECDSA" ? pkijs.createCMSECDSASignature(require_engine.toArrayBuffer(raw)) : require_engine.toArrayBuffer(raw);
228
+ }
229
+ /** The SignerInfo signature algorithm of a raw key and hash. */
321
230
  function signatureAlgorithmFor(algorithm, hash) {
322
231
  switch (algorithm) {
323
232
  case "RSA-PKCS1-v1_5": return new pkijs.AlgorithmIdentifier({
324
233
  algorithmId: {
325
- sha256: OID.sha256WithRsa,
326
- sha384: OID.sha384WithRsa,
327
- sha512: OID.sha512WithRsa
234
+ sha256: require_oids.OID.sha256WithRsa,
235
+ sha384: require_oids.OID.sha384WithRsa,
236
+ sha512: require_oids.OID.sha512WithRsa
328
237
  }[hash],
329
238
  algorithmParams: new asn1js.Null()
330
239
  });
331
240
  case "ECDSA": return new pkijs.AlgorithmIdentifier({ algorithmId: {
332
- sha256: OID.ecdsaSha256,
333
- sha384: OID.ecdsaSha384,
334
- sha512: OID.ecdsaSha512
241
+ sha256: require_oids.OID.ecdsaSha256,
242
+ sha384: require_oids.OID.ecdsaSha384,
243
+ sha512: require_oids.OID.ecdsaSha512
335
244
  }[hash] });
336
245
  case "RSA-PSS": {
337
246
  const hashAlgorithm = new pkijs.AlgorithmIdentifier({
338
- algorithmId: OID_BY_DIGEST[hash],
247
+ algorithmId: require_oids.OID_BY_DIGEST[hash],
339
248
  algorithmParams: new asn1js.Null()
340
249
  });
341
250
  const params = new pkijs.RSASSAPSSParams({
342
251
  hashAlgorithm,
343
252
  maskGenAlgorithm: new pkijs.AlgorithmIdentifier({
344
- algorithmId: OID.mgf1,
253
+ algorithmId: require_oids.OID.mgf1,
345
254
  algorithmParams: hashAlgorithm.toSchema()
346
255
  }),
347
- saltLength: DIGEST_LENGTH[hash]
256
+ saltLength: require_oids.DIGEST_LENGTH[hash]
348
257
  });
349
258
  return new pkijs.AlgorithmIdentifier({
350
- algorithmId: OID.rsaPss,
259
+ algorithmId: require_oids.OID.rsaPss,
351
260
  algorithmParams: params.toSchema()
352
261
  });
353
262
  }
354
263
  }
355
264
  }
356
265
  //#endregion
266
+ //#region src/cms/timestamp.ts
267
+ var timestamp_exports = /* @__PURE__ */ require_engine.__exportAll({
268
+ buildTimestampToken: () => buildTimestampToken,
269
+ readTimestampInfo: () => readTimestampInfo
270
+ });
271
+ /** Read a token's TSTInfo (the encapsulated content of an RFC 3161 token). Throws `CmsError`. */
272
+ function readTimestampInfo(eContent) {
273
+ const asn1 = asn1js.fromBER(require_engine.toArrayBuffer(eContent));
274
+ if (asn1.offset === -1) throw new CmsError("malformed", "TSTInfo is not valid BER");
275
+ let info;
276
+ try {
277
+ info = new pkijs.TSTInfo({ schema: asn1.result });
278
+ } catch (error) {
279
+ throw new CmsError("malformed", `TSTInfo does not parse: ${error.message}`);
280
+ }
281
+ const algorithm = require_oids.DIGEST_BY_OID[info.messageImprint.hashAlgorithm.algorithmId];
282
+ if (!algorithm) throw new CmsError("unsupported", `message imprint algorithm ${info.messageImprint.hashAlgorithm.algorithmId}`);
283
+ return {
284
+ imprintAlgorithm: algorithm,
285
+ imprint: new Uint8Array(info.messageImprint.hashedMessage.valueBlock.valueHexView),
286
+ genTime: info.genTime
287
+ };
288
+ }
289
+ /**
290
+ * Build an RFC 3161 timestamp token: a SignedData whose content is a
291
+ * TSTInfo stamping `digest`, signed by the authority over content-type
292
+ * (id-ct-TSTInfo), message-digest (of the TSTInfo) and ESS
293
+ * signing-certificate-v2. What a timestamp authority returns; used by the
294
+ * test authority, never to stamp a real document.
295
+ */
296
+ async function buildTimestampToken(input) {
297
+ const engine = require_engine.ensureEngine();
298
+ if (input.digest.byteLength !== require_oids.DIGEST_LENGTH[input.hash]) throw new Error(`digest is ${input.digest.byteLength} bytes; ${input.hash} needs ${require_oids.DIGEST_LENGTH[input.hash]}`);
299
+ const authorityCert = pkijs.Certificate.fromBER(require_engine.toArrayBuffer(input.authority.certificateChain[0]));
300
+ const serial = globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(8));
301
+ serial[0] &= 127;
302
+ const eContent = new pkijs.TSTInfo({
303
+ version: 1,
304
+ policy: "1.3.6.1.4.1.57264.1.1",
305
+ messageImprint: new pkijs.MessageImprint({
306
+ hashAlgorithm: new pkijs.AlgorithmIdentifier({ algorithmId: require_oids.OID_BY_DIGEST[input.hash] }),
307
+ hashedMessage: new asn1js.OctetString({ valueHex: require_engine.toArrayBuffer(input.digest) })
308
+ }),
309
+ serialNumber: new asn1js.Integer({ valueHex: require_engine.toArrayBuffer(serial) }),
310
+ genTime: input.genTime ?? /* @__PURE__ */ new Date()
311
+ }).toSchema().toBER(false);
312
+ const contentDigest = await engine.digest(require_oids.WEBCRYPTO_HASH[input.authority.hash], eContent);
313
+ const certHash = await engine.digest("SHA-256", require_engine.toArrayBuffer(input.authority.certificateChain[0]));
314
+ const issuerSerial = new pkijs.IssuerSerial({
315
+ issuer: new pkijs.GeneralNames({ names: [new pkijs.GeneralName({
316
+ type: 4,
317
+ value: authorityCert.issuer
318
+ })] }),
319
+ serialNumber: authorityCert.serialNumber
320
+ });
321
+ const attributes = [
322
+ new pkijs.Attribute({
323
+ type: require_oids.OID.contentType,
324
+ values: [new asn1js.ObjectIdentifier({ value: require_oids.OID.tstInfo })]
325
+ }),
326
+ new pkijs.Attribute({
327
+ type: require_oids.OID.messageDigest,
328
+ values: [new asn1js.OctetString({ valueHex: contentDigest })]
329
+ }),
330
+ new pkijs.Attribute({
331
+ type: require_oids.OID.signingCertificateV2,
332
+ values: [new asn1js.Sequence({ value: [new asn1js.Sequence({ value: [new asn1js.Sequence({ value: [new asn1js.OctetString({ valueHex: certHash }), issuerSerial.toSchema()] })] })] })]
333
+ })
334
+ ];
335
+ const digestAlgorithm = new pkijs.AlgorithmIdentifier({ algorithmId: require_oids.OID_BY_DIGEST[input.authority.hash] });
336
+ const signerInfo = new pkijs.SignerInfo({
337
+ version: 1,
338
+ sid: new pkijs.IssuerAndSerialNumber({
339
+ issuer: authorityCert.issuer,
340
+ serialNumber: authorityCert.serialNumber
341
+ }),
342
+ digestAlgorithm,
343
+ signedAttrs: new pkijs.SignedAndUnsignedAttributes({
344
+ type: 0,
345
+ attributes
346
+ }),
347
+ signatureAlgorithm: signatureAlgorithmFor(input.authority.algorithm, input.authority.hash)
348
+ });
349
+ const toSign = new asn1js.Set({ value: attributes.map((attribute) => attribute.toSchema()) }).toBER(false);
350
+ const raw = await input.authority.sign(new Uint8Array(toSign));
351
+ signerInfo.signature = new asn1js.OctetString({ valueHex: cmsSignatureValue(input.authority.algorithm, raw) });
352
+ const signedData = new pkijs.SignedData({
353
+ version: 3,
354
+ digestAlgorithms: [digestAlgorithm],
355
+ encapContentInfo: new pkijs.EncapsulatedContentInfo({
356
+ eContentType: require_oids.OID.tstInfo,
357
+ eContent: new asn1js.OctetString({ valueHex: eContent })
358
+ }),
359
+ certificates: [authorityCert],
360
+ signerInfos: [signerInfo]
361
+ });
362
+ const contentInfo = new pkijs.ContentInfo({
363
+ contentType: require_oids.OID.signedData,
364
+ content: signedData.toSchema(true)
365
+ });
366
+ return new Uint8Array(contentInfo.toSchema().toBER(false));
367
+ }
368
+ //#endregion
357
369
  //#region src/gate.ts
358
370
  /**
359
371
  * The gate between a signer's answer and `signatures.complete`: the CMS
@@ -370,14 +382,15 @@ async function verifyForCompletion(input) {
370
382
  reason: "too-large",
371
383
  detail: `the CMS is ${cms.byteLength} bytes; ${prepared.contentsSize} were reserved`
372
384
  };
385
+ if (profile === "timestamp") return verifyTimestampToken(cms, prepared);
373
386
  let internal;
374
387
  try {
375
388
  internal = parseCmsInternal(cms);
376
- } catch (err) {
389
+ } catch (error) {
377
390
  return {
378
391
  ok: false,
379
392
  reason: "malformed",
380
- detail: err instanceof CmsError ? err.message : String(err)
393
+ detail: error instanceof CmsError ? error.message : String(error)
381
394
  };
382
395
  }
383
396
  const parsed = internal.parsed;
@@ -386,7 +399,7 @@ async function verifyForCompletion(input) {
386
399
  reason: "algorithm-mismatch",
387
400
  detail: `the CMS digests with ${parsed.digestAlgorithm}, the document was prepared with ${prepared.algorithm}`
388
401
  };
389
- if (!bytesEqual(parsed.messageDigest, prepared.digest)) return {
402
+ if (!require_engine.bytesEqual(parsed.messageDigest, prepared.digest)) return {
390
403
  ok: false,
391
404
  reason: "digest-mismatch",
392
405
  detail: "the CMS message digest is not the prepared digest"
@@ -411,6 +424,47 @@ async function verifyForCompletion(input) {
411
424
  };
412
425
  return { ok: true };
413
426
  }
427
+ /**
428
+ * A document timestamp's token: it must stamp the prepared digest with the
429
+ * prepared algorithm, its own message digest must cover its TSTInfo, and
430
+ * its signature must verify with the authority's certificate.
431
+ */
432
+ async function verifyTimestampToken(cms, prepared) {
433
+ let internal;
434
+ let info;
435
+ try {
436
+ internal = parseCmsInternal(cms, "tst-info");
437
+ info = readTimestampInfo(internal.eContent);
438
+ } catch (error) {
439
+ return {
440
+ ok: false,
441
+ reason: "malformed",
442
+ detail: error instanceof CmsError ? error.message : String(error)
443
+ };
444
+ }
445
+ if (info.imprintAlgorithm !== prepared.algorithm) return {
446
+ ok: false,
447
+ reason: "algorithm-mismatch",
448
+ detail: `the token stamps a ${info.imprintAlgorithm} digest, the document was prepared with ${prepared.algorithm}`
449
+ };
450
+ if (!require_engine.bytesEqual(info.imprint, prepared.digest)) return {
451
+ ok: false,
452
+ reason: "digest-mismatch",
453
+ detail: "the token's message imprint is not the prepared digest"
454
+ };
455
+ if (!require_engine.bytesEqual(new Uint8Array(await require_engine.ensureEngine().digest(require_oids.WEBCRYPTO_HASH[internal.parsed.digestAlgorithm], require_engine.toArrayBuffer(internal.eContent))), internal.parsed.messageDigest)) return {
456
+ ok: false,
457
+ reason: "signature-invalid",
458
+ detail: "the token's message digest does not cover its TSTInfo"
459
+ };
460
+ const cryptography = await verifyCmsSignature(internal);
461
+ if (cryptography !== "valid") return {
462
+ ok: false,
463
+ reason: "signature-invalid",
464
+ detail: cryptography === "unsupported" ? "the signature algorithm is not supported" : "the token signature does not verify"
465
+ };
466
+ return { ok: true };
467
+ }
414
468
  //#endregion
415
469
  //#region src/trust.ts
416
470
  /**
@@ -428,17 +482,17 @@ async function validateChain(cms, trust, at = { kind: "now" }) {
428
482
  status: "unknown",
429
483
  reason: "no trust anchors configured"
430
484
  };
431
- ensureEngine();
485
+ require_engine.ensureEngine();
432
486
  let internal;
433
487
  try {
434
488
  internal = cms instanceof Uint8Array ? parseCmsInternal(cms) : cms;
435
- } catch (err) {
489
+ } catch (error) {
436
490
  return {
437
491
  status: "untrusted",
438
- reason: err.message
492
+ reason: error.message
439
493
  };
440
494
  }
441
- const trustedCerts = anchors.map((der) => pkijs.Certificate.fromBER(toArrayBuffer(der)));
495
+ const trustedCerts = anchors.map((der) => pkijs.Certificate.fromBER(require_engine.toArrayBuffer(der)));
442
496
  const chainEngine = new pkijs.CertificateChainValidationEngine({
443
497
  certs: internal.certificates,
444
498
  trustedCerts,
@@ -452,91 +506,103 @@ async function validateChain(cms, trust, at = { kind: "now" }) {
452
506
  };
453
507
  return {
454
508
  status: "trusted",
455
- path: (result.certificatePath ?? []).map((c) => new Uint8Array(c.toSchema(true).toBER(false)))
509
+ path: (result.certificatePath ?? []).map((certificate) => new Uint8Array(certificate.toSchema(true).toBER(false)))
456
510
  };
457
- } catch (err) {
511
+ } catch (error) {
458
512
  return {
459
513
  status: "untrusted",
460
- reason: err.message
514
+ reason: error.message
461
515
  };
462
516
  }
463
517
  }
464
518
  //#endregion
465
519
  //#region src/sign.ts
466
- var SigningError = class extends Error {
467
- constructor(reason, detail) {
468
- super(`signing refused (${reason}): ${detail}`);
469
- this.reason = reason;
470
- this.name = "SigningError";
471
- }
472
- };
520
+ /**
521
+ * The gate refused the key's answer: `SignatureRefused`, with the gate's
522
+ * reason in `details.reason`.
523
+ */
524
+ function signingRefused(reason, detail) {
525
+ return new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.SignatureRefused, `signing refused (${reason}): ${detail}`, { details: { reason } });
526
+ }
473
527
  function profileFor(subFilter) {
474
- return subFilter === "adbe.pkcs7.detached" ? "pkcs7" : "cades-b";
528
+ switch (subFilter) {
529
+ case "adbe.pkcs7.detached": return "pkcs7";
530
+ case "ETSI.CAdES.detached": return "cades-b";
531
+ case "ETSI.RFC3161": return "timestamp";
532
+ default: throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `unknown subFilter '${String(subFilter)}'`, { details: { field: "subFilter" } });
533
+ }
475
534
  }
476
535
  /**
477
- * Sign a field in one call: `prepare` on the engine, the signer turns the
536
+ * Sign a field in one call: `prepare` on the engine, the key turns the
478
537
  * digest into a CMS (or a raw signature this package wraps), the gate
479
538
  * checks the CMS matches what was prepared, `complete` installs it. Any
480
- * failure aborts the candidate and rethrows; the document is never left
539
+ * failure cancels the candidate and rethrows; the document is never left
481
540
  * with a pending signing.
482
541
  */
483
542
  async function sign(doc, input) {
484
- if (!doc.signatures) throw new Error("this engine does not implement signatures");
485
- const { signer, ...rest } = input;
486
- const subFilter = rest.subFilter ?? "ETSI.CAdES.detached";
543
+ const { key, ...rest } = input;
544
+ const timestamp = rest.kind === "timestamp";
545
+ if (timestamp && key.kind !== "cms") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "a document timestamp needs a timestamp authority: a CMS key that returns an RFC 3161 token", { details: { field: "key" } });
546
+ const subFilter = timestamp ? "ETSI.RFC3161" : rest.subFilter ?? "ETSI.CAdES.detached";
487
547
  const profile = profileFor(subFilter);
488
- const digest = rest.digest ?? (signer.kind === "raw" ? signer.hash : "sha256");
548
+ const digest = rest.digest ?? (key.kind === "raw" ? key.hash : "sha256");
489
549
  const prepared = await doc.signatures.prepare({
490
550
  ...rest,
491
- subFilter,
551
+ ...timestamp ? {} : { subFilter },
492
552
  digest
493
553
  });
494
554
  try {
495
- const cms = signer.kind === "cms" ? await signer.sign({
496
- digest: prepared.digest,
497
- algorithm: prepared.algorithm,
498
- subFilter
499
- }) : await buildDetachedCms({
500
- digest: prepared.digest,
501
- hash: prepared.algorithm,
502
- profile,
503
- signer,
504
- signingTime: profile === "pkcs7" ? /* @__PURE__ */ new Date() : void 0
505
- });
555
+ const cms = await cmsFor(key, prepared, subFilter, profile);
506
556
  const gate = await verifyForCompletion({
507
557
  cms,
508
558
  prepared,
509
559
  profile
510
560
  });
511
- if (!gate.ok) throw new SigningError(gate.reason, gate.detail);
561
+ if (!gate.ok) throw signingRefused(gate.reason, gate.detail);
512
562
  return await doc.signatures.complete({
513
563
  signingId: prepared.signingId,
514
564
  cms,
515
565
  expectedVersion: prepared.expectedVersion
516
566
  });
517
- } catch (err) {
518
- await doc.signatures.abort(prepared.signingId).catch(() => void 0);
519
- throw err;
567
+ } catch (error) {
568
+ await doc.signatures.cancel(prepared.signingId).catch(() => void 0);
569
+ throw error;
520
570
  }
521
571
  }
572
+ /** The key's answer for the prepared digest: a service's CMS, or one built here around a raw signature. */
573
+ async function cmsFor(key, prepared, subFilter, profile) {
574
+ if (key.kind === "cms") return key.sign({
575
+ digest: prepared.digest,
576
+ algorithm: prepared.algorithm,
577
+ subFilter
578
+ });
579
+ if (profile === "timestamp") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "a raw key cannot make a timestamp token");
580
+ return buildDetachedCms({
581
+ digest: prepared.digest,
582
+ hash: prepared.algorithm,
583
+ profile,
584
+ signer: key,
585
+ signingTime: profile === "pkcs7" ? /* @__PURE__ */ new Date() : void 0
586
+ });
587
+ }
522
588
  //#endregion
523
589
  //#region src/verdict.ts
524
590
  /**
525
591
  * Validate every signature of a document: integrity from the engine's
526
592
  * bytes, cryptography from the CMS, trust from the caller's anchors.
527
593
  */
528
- async function validateSignatures(doc, opts = {}) {
594
+ async function validateSignatures(doc, options = {}) {
529
595
  if (!doc.signatures) throw new Error("this engine does not implement signatures");
530
596
  const snapshot = await doc.signatures.list();
531
597
  const verdicts = [];
532
598
  for (const signature of snapshot.signatures) {
533
599
  if (!signature.signed) continue;
534
- verdicts.push(await validateOne(doc, snapshot, signature, opts));
600
+ verdicts.push(await validateOne(doc, snapshot, signature, options));
535
601
  }
536
602
  return verdicts;
537
603
  }
538
- async function validateOne(doc, snapshot, signature, opts) {
539
- const at = opts.at ?? { kind: "now" };
604
+ async function validateOne(doc, snapshot, signature, options) {
605
+ const at = options.at ?? { kind: "now" };
540
606
  let integrity = "indeterminate";
541
607
  let cryptography = "unsupported";
542
608
  let trust = "unknown";
@@ -545,21 +611,21 @@ async function validateOne(doc, snapshot, signature, opts) {
545
611
  let cms = null;
546
612
  if (!snapshot.chainValid || signature.coverage === "malformed") integrity = snapshot.chainValid ? "invalid" : "indeterminate";
547
613
  else try {
548
- internal = parseCmsInternal(await doc.signatures.contents(signature.field));
614
+ internal = parseCmsInternal(await doc.signatures.getContents(signature.field));
549
615
  cms = internal.parsed;
550
- const digestMatches = bytesEqual(await doc.signatures.digest(signature.field, cms.digestAlgorithm), cms.messageDigest);
616
+ const digestMatches = require_engine.bytesEqual(await doc.signatures.getDigest(signature.field, cms.digestAlgorithm), cms.messageDigest);
551
617
  integrity = signature.coverage === "whole-revision" && digestMatches ? "valid" : "invalid";
552
- } catch (err) {
553
- integrity = err instanceof CmsError && err.reason === "unsupported" ? "indeterminate" : "invalid";
554
- if (!(err instanceof CmsError)) integrity = "indeterminate";
618
+ } catch (error) {
619
+ integrity = error instanceof CmsError && error.reason === "unsupported" ? "indeterminate" : "invalid";
620
+ if (!(error instanceof CmsError)) integrity = "indeterminate";
555
621
  }
556
622
  if (internal) {
557
623
  cryptography = await verifyCmsSignature(internal);
558
- const chain = await validateChain(internal, opts.trust, at);
624
+ const chain = await validateChain(internal, options.trust, at);
559
625
  trust = chain.status;
560
626
  trustReason = chain.reason;
561
627
  }
562
- const modifications = await modificationsOf(doc, snapshot, signature, opts.until ?? "persisted");
628
+ const modifications = await modificationsOf(doc, snapshot, signature, options.until ?? "persisted");
563
629
  let summary;
564
630
  if (integrity === "invalid" || cryptography === "invalid" || modifications.verdict === "forbidden") summary = "invalid";
565
631
  else if (integrity === "indeterminate" || cryptography === "unsupported" || modifications.verdict === "indeterminate") summary = "indeterminate";
@@ -606,67 +672,15 @@ async function modificationsOf(doc, snapshot, signature, until) {
606
672
  ...analysis.later.revisionCount > 0 ? { laterRevisions: analysis.later.revisionCount } : {},
607
673
  ...analysis.later.undoneObjectNumbers.length > 0 ? { undone: true } : {}
608
674
  };
609
- } catch (err) {
675
+ } catch (error) {
610
676
  return {
611
677
  verdict: "indeterminate",
612
- detail: `analysis failed: ${err.message}`,
678
+ detail: `analysis failed: ${error.message}`,
613
679
  basis: "persisted"
614
680
  };
615
681
  }
616
682
  }
617
683
  //#endregion
618
- //#region src/signers/self-signed.ts
619
- /**
620
- * A self-signed certificate over a WebCrypto key pair: the shape a personal
621
- * signer, a test signer, and a demo all need. It is its own trust anchor and
622
- * nothing else's — Acrobat shows a signature made with it as "validity
623
- * unknown" until the reader trusts the certificate explicitly.
624
- */
625
- async function selfSignedCertificate(input) {
626
- ensureEngine();
627
- const cert = new pkijs.Certificate();
628
- cert.version = 2;
629
- const serial = /* @__PURE__ */ new Uint8Array(8);
630
- globalThis.crypto.getRandomValues(serial);
631
- serial[0] &= 127;
632
- cert.serialNumber = new asn1js.Integer({ valueHex: serial.buffer });
633
- const name = new pkijs.AttributeTypeAndValue({
634
- type: OID.commonName,
635
- value: new asn1js.Utf8String({ value: input.commonName })
636
- });
637
- cert.issuer.typesAndValues.push(name);
638
- cert.subject.typesAndValues.push(name);
639
- const now = Date.now();
640
- cert.notBefore.value = /* @__PURE__ */ new Date(now - 24 * 3600 * 1e3);
641
- cert.notAfter.value = new Date(now + (input.validityDays ?? 365) * 24 * 3600 * 1e3);
642
- const basicConstraints = new pkijs.BasicConstraints({ cA: true });
643
- const keyUsage = new asn1js.BitString({
644
- valueHex: new Uint8Array([132]).buffer,
645
- unusedBits: 2
646
- });
647
- cert.extensions = [new pkijs.Extension({
648
- extnID: OID.basicConstraints,
649
- critical: true,
650
- extnValue: basicConstraints.toSchema().toBER(false),
651
- parsedValue: basicConstraints
652
- }), new pkijs.Extension({
653
- extnID: OID.keyUsage,
654
- critical: true,
655
- extnValue: keyUsage.toBER(false)
656
- })];
657
- await cert.subjectPublicKeyInfo.importKey(input.publicKey);
658
- await cert.sign(input.privateKey, WEBCRYPTO_HASH[input.hash]);
659
- return new Uint8Array(cert.toSchema(true).toBER(false));
660
- }
661
- /** The subject common name of a DER certificate, or null when it has none. */
662
- function certificateCommonName(der) {
663
- ensureEngine();
664
- const copy = new Uint8Array(der.byteLength);
665
- copy.set(der);
666
- const text = (pkijs.Certificate.fromBER(copy.buffer).subject.typesAndValues.find((entry) => entry.type === OID.commonName)?.value)?.valueBlock?.value;
667
- return typeof text === "string" && text.length > 0 ? text : null;
668
- }
669
- //#endregion
670
684
  //#region src/signers/webcrypto.ts
671
685
  /**
672
686
  * A `RawSigner` over a WebCrypto private key: the key never leaves the
@@ -706,7 +720,7 @@ function webCryptoSigner(input) {
706
720
  }[hash]
707
721
  } : algorithm === "ECDSA" ? {
708
722
  name: "ECDSA",
709
- hash: WEBCRYPTO_HASH[hash]
723
+ hash: require_oids.WEBCRYPTO_HASH[hash]
710
724
  } : { name: "RSASSA-PKCS1-v1_5" };
711
725
  const copy = new Uint8Array(data.byteLength);
712
726
  copy.set(data);
@@ -728,14 +742,14 @@ function hashFromWebCrypto(name) {
728
742
  * self-signed certificate, never persisted. Trust it with
729
743
  * `{ anchors: async () => [signer.certificate] }`.
730
744
  */
731
- async function createTestSigner(opts = {}) {
732
- ensureEngine();
733
- const hash = opts.hash ?? "sha256";
734
- const keys = await generateSigningKeyPair(opts.algorithm ?? "RSA-PKCS1-v1_5", hash, true);
745
+ async function createTestSigner(options = {}) {
746
+ const { selfSignedCertificate } = await Promise.resolve().then(() => self_signed_exports);
747
+ const hash = options.hash ?? "sha256";
748
+ const keys = await generateSigningKeyPair(options.algorithm ?? "RSA-PKCS1-v1_5", hash, true);
735
749
  const certificate = await selfSignedCertificate({
736
750
  publicKey: keys.publicKey,
737
751
  privateKey: keys.privateKey,
738
- commonName: opts.commonName ?? "EmbedPDF test signer",
752
+ commonName: options.commonName ?? "EmbedPDF test signer",
739
753
  hash
740
754
  });
741
755
  return {
@@ -748,6 +762,25 @@ async function createTestSigner(opts = {}) {
748
762
  privateKey: keys.privateKey
749
763
  };
750
764
  }
765
+ /**
766
+ * A throwaway RFC 3161 timestamp authority for tests and demos: it stamps
767
+ * whatever digest it is given with the current time, signed by a fresh
768
+ * self-signed key. The key for `sign(doc, { kind: 'timestamp', key })`;
769
+ * never use it to stamp a real document.
770
+ */
771
+ async function createTestTimestampAuthority(options = {}) {
772
+ const authority = await createTestSigner({ commonName: options.commonName ?? "EmbedPDF test timestamp authority" });
773
+ const { buildTimestampToken } = await Promise.resolve().then(() => timestamp_exports);
774
+ return {
775
+ kind: "cms",
776
+ certificate: authority.certificate,
777
+ sign: ({ digest, algorithm }) => buildTimestampToken({
778
+ digest,
779
+ hash: algorithm,
780
+ authority
781
+ })
782
+ };
783
+ }
751
784
  /** A WebCrypto signing key pair; `extractable: false` keeps the private key inside the runtime for good. */
752
785
  async function generateSigningKeyPair(algorithm, hash, extractable) {
753
786
  return await globalThis.crypto.subtle.generateKey(algorithm === "ECDSA" ? {
@@ -761,15 +794,16 @@ async function generateSigningKeyPair(algorithm, hash, extractable) {
761
794
  0,
762
795
  1
763
796
  ]),
764
- hash: WEBCRYPTO_HASH[hash]
797
+ hash: require_oids.WEBCRYPTO_HASH[hash]
765
798
  }, extractable, ["sign", "verify"]);
766
799
  }
767
800
  //#endregion
768
801
  //#region src/signers/remote.ts
769
802
  /**
770
- * A `CmsSigner` over the customer's own signing service or HSM: the engine's
771
- * digest goes out, a finished detached CMS comes back. Nothing but the
772
- * digest (and which hash and profile it is for) ever leaves the runtime.
803
+ * A `CmsSigner` over the customer's own signing service, HSM or timestamp
804
+ * authority: the engine's digest goes out, a finished detached CMS (or, for
805
+ * `'ETSI.RFC3161'`, an RFC 3161 token) comes back. Nothing but the digest
806
+ * (and which hash and profile it is for) ever leaves the runtime.
773
807
  */
774
808
  function remoteSigner(input) {
775
809
  return {
@@ -789,10 +823,12 @@ function remoteSigner(input) {
789
823
  * unknown", which is the honest answer for a self-issued identity.
790
824
  */
791
825
  async function personalSigner(input) {
826
+ const { ensureEngine } = await Promise.resolve().then(() => require("./engine-BPmX1KyF.cjs")).then((n) => n.engine_exports);
792
827
  ensureEngine();
793
828
  const hash = input.hash ?? "sha256";
794
829
  let record = await input.store.load(input.subject);
795
830
  if (!record) {
831
+ const { selfSignedCertificate } = await Promise.resolve().then(() => self_signed_exports);
796
832
  const keys = await generateSigningKeyPair(input.algorithm ?? "RSA-PKCS1-v1_5", hash, false);
797
833
  const certificate = await selfSignedCertificate({
798
834
  publicKey: keys.publicKey,
@@ -834,8 +870,8 @@ function memoryKeyStore() {
834
870
  * itself (structured clone), so the key survives reloads without ever
835
871
  * existing as bytes.
836
872
  */
837
- function indexedDbKeyStore(dbName, opts = {}) {
838
- const storeName = opts.storeName ?? "keys";
873
+ function indexedDbKeyStore(dbName, options = {}) {
874
+ const storeName = options.storeName ?? "keys";
839
875
  let opening = null;
840
876
  const open = () => {
841
877
  opening ?? (opening = new Promise((resolve, reject) => {
@@ -857,17 +893,73 @@ function indexedDbKeyStore(dbName, opts = {}) {
857
893
  });
858
894
  };
859
895
  return {
860
- load: async (subject) => await run("readonly", (s) => s.get(subject)) ?? null,
861
- save: (subject, record) => run("readwrite", (s) => s.put(record, subject)).then(() => void 0),
862
- remove: (subject) => run("readwrite", (s) => s.delete(subject)).then(() => void 0)
896
+ load: async (subject) => await run("readonly", (store) => store.get(subject)) ?? null,
897
+ save: (subject, record) => run("readwrite", (store) => store.put(record, subject)).then(() => void 0),
898
+ remove: (subject) => run("readwrite", (store) => store.delete(subject)).then(() => void 0)
863
899
  };
864
900
  }
865
901
  //#endregion
902
+ //#region src/signers/self-signed.ts
903
+ var self_signed_exports = /* @__PURE__ */ require_engine.__exportAll({
904
+ certificateCommonName: () => certificateCommonName,
905
+ selfSignedCertificate: () => selfSignedCertificate
906
+ });
907
+ /**
908
+ * A self-signed certificate over a WebCrypto key pair: the shape a personal
909
+ * signer, a test signer, and a demo all need. It is its own trust anchor and
910
+ * nothing else's — Acrobat shows a signature made with it as "validity
911
+ * unknown" until the reader trusts the certificate explicitly.
912
+ */
913
+ async function selfSignedCertificate(input) {
914
+ require_engine.ensureEngine();
915
+ const cert = new pkijs.Certificate();
916
+ cert.version = 2;
917
+ const serial = /* @__PURE__ */ new Uint8Array(8);
918
+ globalThis.crypto.getRandomValues(serial);
919
+ serial[0] &= 127;
920
+ cert.serialNumber = new asn1js.Integer({ valueHex: serial.buffer });
921
+ const name = new pkijs.AttributeTypeAndValue({
922
+ type: require_oids.OID.commonName,
923
+ value: new asn1js.Utf8String({ value: input.commonName })
924
+ });
925
+ cert.issuer.typesAndValues.push(name);
926
+ cert.subject.typesAndValues.push(name);
927
+ const now = Date.now();
928
+ cert.notBefore.value = /* @__PURE__ */ new Date(now - 24 * 3600 * 1e3);
929
+ cert.notAfter.value = new Date(now + (input.validityDays ?? 365) * 24 * 3600 * 1e3);
930
+ const basicConstraints = new pkijs.BasicConstraints({ cA: true });
931
+ const keyUsage = new asn1js.BitString({
932
+ valueHex: new Uint8Array([132]).buffer,
933
+ unusedBits: 2
934
+ });
935
+ cert.extensions = [new pkijs.Extension({
936
+ extnID: require_oids.OID.basicConstraints,
937
+ critical: true,
938
+ extnValue: basicConstraints.toSchema().toBER(false),
939
+ parsedValue: basicConstraints
940
+ }), new pkijs.Extension({
941
+ extnID: require_oids.OID.keyUsage,
942
+ critical: true,
943
+ extnValue: keyUsage.toBER(false)
944
+ })];
945
+ await cert.subjectPublicKeyInfo.importKey(input.publicKey);
946
+ await cert.sign(input.privateKey, require_oids.WEBCRYPTO_HASH[input.hash]);
947
+ return new Uint8Array(cert.toSchema(true).toBER(false));
948
+ }
949
+ /** The subject common name of a DER certificate, or null when it has none. */
950
+ function certificateCommonName(der) {
951
+ require_engine.ensureEngine();
952
+ const copy = new Uint8Array(der.byteLength);
953
+ copy.set(der);
954
+ const text = (pkijs.Certificate.fromBER(copy.buffer).subject.typesAndValues.find((entry) => entry.type === require_oids.OID.commonName)?.value)?.valueBlock?.value;
955
+ return typeof text === "string" && text.length > 0 ? text : null;
956
+ }
957
+ //#endregion
866
958
  exports.CmsError = CmsError;
867
- exports.SigningError = SigningError;
868
959
  exports.buildDetachedCms = buildDetachedCms;
869
960
  exports.certificateCommonName = certificateCommonName;
870
961
  exports.createTestSigner = createTestSigner;
962
+ exports.createTestTimestampAuthority = createTestTimestampAuthority;
871
963
  exports.generateSigningKeyPair = generateSigningKeyPair;
872
964
  exports.indexedDbKeyStore = indexedDbKeyStore;
873
965
  exports.memoryKeyStore = memoryKeyStore;