@f-o-t/e-signature 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,6 +46,23 @@ const signedPdf = await signPdf(pdfBytes, {
46
46
  await Bun.write("signed.pdf", signedPdf);
47
47
  ```
48
48
 
49
+ To stamp multiple pages with the same visual appearance:
50
+
51
+ ```ts
52
+ const signedPdf = await signPdf(pdfBytes, {
53
+ certificate: { p12, password: "secret" },
54
+ reason: "Document approval",
55
+ appearances: [
56
+ { x: 50, y: 730, width: 200, height: 80, page: 0, showCertInfo: true },
57
+ { x: 50, y: 730, width: 200, height: 80, page: 1, showCertInfo: true },
58
+ { x: 50, y: 730, width: 200, height: 80, page: 2, showCertInfo: true },
59
+ ],
60
+ qrCode: { data: "https://validar.iti.gov.br", size: 128 },
61
+ });
62
+ ```
63
+
64
+ `appearance` and `appearances` can be used simultaneously — both stamps are rendered. An empty `appearances: []` is a no-op.
65
+
49
66
  ### `buildSigningCertificateV2(certDer: Uint8Array): Uint8Array`
50
67
 
51
68
  Build the `id-aa-signingCertificateV2` attribute value (RFC 5035). Links the signature to the specific certificate used, preventing substitution attacks.
@@ -102,7 +119,10 @@ type PdfSignOptions = {
102
119
  policy?: "pades-ades" | "pades-icp-brasil";
103
120
  timestamp?: boolean;
104
121
  tsaUrl?: string;
122
+ /** Single visual stamp (false to disable) */
105
123
  appearance?: SignatureAppearance | false;
124
+ /** Multiple visual stamps — renders one per entry, useful for multi-page documents */
125
+ appearances?: SignatureAppearance[];
106
126
  qrCode?: QrCodeConfig;
107
127
  docMdpPermission?: 1 | 2 | 3;
108
128
  };
@@ -5,8 +5,8 @@
5
5
  * for visible digital signatures.
6
6
  */
7
7
  import type { CertificateInfo } from "@f-o-t/digital-certificate";
8
- import type { PdfPage, PdfDocument } from "@f-o-t/pdf/plugins/editing";
9
- import type { SignatureAppearance, QrCodeConfig } from "./types.ts";
8
+ import type { PdfDocument, PdfPage } from "@f-o-t/pdf/plugins/editing";
9
+ import type { QrCodeConfig, SignatureAppearance } from "./types.ts";
10
10
  /**
11
11
  * Draw the visual signature appearance on a PDF page.
12
12
  *
@@ -1 +1 @@
1
- {"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAGvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,uBAAuB,CACtC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,mBAAmB,EAC/B,QAAQ,EAAE,eAAe,GAAG,IAAI,EAChC,OAAO,EAAE;IACR,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;CACpB,GACC,IAAI,CAwCN"}
1
+ {"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAEvE,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,uBAAuB,CACpC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,mBAAmB,EAC/B,QAAQ,EAAE,eAAe,GAAG,IAAI,EAChC,OAAO,EAAE;IACN,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;CACtB,GACD,IAAI,CA6CN"}
@@ -1 +1 @@
1
- {"version":3,"file":"icp-brasil.d.ts","sourceRoot":"","sources":["../src/icp-brasil.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8CH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAuCzE;AA2ED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,UAAU,CAAC,CA8BhE;AAED;;GAEG;AACH,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAMX,qBAAa,oBAAqB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI3B"}
1
+ {"version":3,"file":"icp-brasil.d.ts","sourceRoot":"","sources":["../src/icp-brasil.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8CH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAuCzE;AAyED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,UAAU,CAAC,CA8BhE;AAED;;GAEG;AACH,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAMX,qBAAa,oBAAqB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI7B"}
package/dist/index.d.ts CHANGED
@@ -6,9 +6,9 @@
6
6
  *
7
7
  * @packageDocumentation
8
8
  */
9
- export { signPdf, PdfSignError } from "./sign-pdf.ts";
10
- export { buildSigningCertificateV2, buildSignaturePolicy, clearPolicyCache, SignaturePolicyError, ICP_BRASIL_OIDS, } from "./icp-brasil.ts";
11
- export { requestTimestamp, TimestampError, TIMESTAMP_SERVERS, TIMESTAMP_TOKEN_OID, } from "./timestamp.ts";
12
- export type { PdfSignOptions, SignatureAppearance, QrCodeConfig } from "./types.ts";
9
+ export { buildSignaturePolicy, buildSigningCertificateV2, clearPolicyCache, ICP_BRASIL_OIDS, SignaturePolicyError, } from "./icp-brasil.ts";
13
10
  export { pdfSignOptionsSchema } from "./schemas.ts";
11
+ export { PdfSignError, signPdf } from "./sign-pdf.ts";
12
+ export { requestTimestamp, TIMESTAMP_SERVERS, TIMESTAMP_TOKEN_OID, TimestampError, } from "./timestamp.ts";
13
+ export type { PdfSignOptions, QrCodeConfig, SignatureAppearance, } from "./types.ts";
14
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACN,yBAAyB,EACzB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,GACnB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACJ,oBAAoB,EACpB,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACJ,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACT,cAAc,EACd,YAAY,EACZ,mBAAmB,GACrB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,26 +1,16 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // src/sign-pdf.ts
5
- import { parseCertificate } from "@f-o-t/digital-certificate";
6
- import { parsePkcs12, createSignedData } from "@f-o-t/crypto";
7
- import {
8
- loadPdf,
9
- findByteRange,
10
- extractBytesToSign,
11
- embedSignature
12
- } from "@f-o-t/pdf/plugins/editing";
13
-
14
4
  // src/icp-brasil.ts
15
5
  import {
16
- encodeDer,
6
+ contextTag,
17
7
  decodeDer,
18
- sequence,
19
- oid,
20
- octetString,
21
- nullValue,
8
+ encodeDer,
22
9
  ia5String,
23
- contextTag
10
+ nullValue,
11
+ octetString,
12
+ oid,
13
+ sequence
24
14
  } from "@f-o-t/asn1";
25
15
  import { hash } from "@f-o-t/crypto";
26
16
  var SHA256_OID = "2.16.840.1.101.3.4.2.1";
@@ -107,91 +97,58 @@ class SignaturePolicyError extends Error {
107
97
  this.name = "SignaturePolicyError";
108
98
  }
109
99
  }
110
-
111
- // src/timestamp.ts
100
+ // src/schemas.ts
101
+ import { z } from "zod";
102
+ var signatureAppearanceSchema = z.object({
103
+ x: z.number(),
104
+ y: z.number(),
105
+ width: z.number().positive(),
106
+ height: z.number().positive(),
107
+ page: z.number().int().min(0).optional(),
108
+ showQrCode: z.boolean().optional(),
109
+ showCertInfo: z.boolean().optional()
110
+ });
111
+ var qrCodeConfigSchema = z.object({
112
+ data: z.string().optional(),
113
+ size: z.number().int().positive().optional()
114
+ });
115
+ var pdfSignOptionsSchema = z.object({
116
+ certificate: z.object({
117
+ p12: z.instanceof(Uint8Array).refine((v) => v.length > 0, {
118
+ message: "P12 data must not be empty"
119
+ }),
120
+ password: z.string(),
121
+ name: z.string().optional()
122
+ }),
123
+ reason: z.string().optional(),
124
+ location: z.string().optional(),
125
+ contactInfo: z.string().optional(),
126
+ policy: z.enum(["pades-ades", "pades-icp-brasil"]).optional(),
127
+ timestamp: z.boolean().optional(),
128
+ tsaUrl: z.string().url().optional(),
129
+ appearance: z.union([signatureAppearanceSchema, z.literal(false)]).optional(),
130
+ appearances: z.array(signatureAppearanceSchema).optional(),
131
+ qrCode: qrCodeConfigSchema.optional(),
132
+ docMdpPermission: z.union([z.literal(1), z.literal(2), z.literal(3)]).optional()
133
+ });
134
+ // src/sign-pdf.ts
135
+ import { createSignedData, parsePkcs12 } from "@f-o-t/crypto";
136
+ import { parseCertificate } from "@f-o-t/digital-certificate";
112
137
  import {
113
- encodeDer as encodeDer2,
114
- decodeDer as decodeDer2,
115
- sequence as sequence2,
116
- integer,
117
- oid as oid2,
118
- octetString as octetString2,
119
- boolean as asn1Boolean
120
- } from "@f-o-t/asn1";
121
- import { hash as hash2 } from "@f-o-t/crypto";
122
- var HASH_OIDS = {
123
- sha256: "2.16.840.1.101.3.4.2.1",
124
- sha384: "2.16.840.1.101.3.4.2.2",
125
- sha512: "2.16.840.1.101.3.4.2.3"
126
- };
127
- var TIMESTAMP_SERVERS = {
128
- VALID: "http://timestamp.valid.com.br/tsa",
129
- SAFEWEB: "http://tsa.safeweb.com.br/tsa/tsa",
130
- CERTISIGN: "http://timestamp.certisign.com.br"
131
- };
132
- var TIMESTAMP_TOKEN_OID = "1.2.840.113549.1.9.16.2.14";
133
- async function requestTimestamp(dataToTimestamp, tsaUrl, hashAlgorithm = "sha256") {
134
- const messageHash = hash2(hashAlgorithm, dataToTimestamp);
135
- const timestampReq = buildTimestampRequest(messageHash, hashAlgorithm);
136
- const response = await fetch(tsaUrl, {
137
- method: "POST",
138
- headers: {
139
- "Content-Type": "application/timestamp-query"
140
- },
141
- body: timestampReq,
142
- signal: AbortSignal.timeout(1e4)
143
- });
144
- if (!response.ok) {
145
- throw new TimestampError(`TSA returned HTTP ${response.status}`);
146
- }
147
- const respBuffer = new Uint8Array(await response.arrayBuffer());
148
- return extractTimestampToken(respBuffer);
149
- }
150
- function buildTimestampRequest(messageHash, hashAlgorithm) {
151
- const hashOid = HASH_OIDS[hashAlgorithm];
152
- if (!hashOid) {
153
- throw new TimestampError(`Unsupported hash algorithm: ${hashAlgorithm}`);
154
- }
155
- const timestampReq = sequence2(integer(1), sequence2(sequence2(oid2(hashOid)), octetString2(messageHash)), asn1Boolean(true));
156
- return encodeDer2(timestampReq);
157
- }
158
- function extractTimestampToken(respDer) {
159
- let resp;
160
- try {
161
- resp = decodeDer2(respDer);
162
- } catch {
163
- throw new TimestampError("Invalid timestamp response: not valid DER");
164
- }
165
- const children = resp.value;
166
- if (!Array.isArray(children) || children.length < 1) {
167
- throw new TimestampError("Invalid timestamp response: unexpected structure");
168
- }
169
- const statusInfo = children[0].value;
170
- const statusBytes = statusInfo[0].value;
171
- const statusValue = statusBytes[statusBytes.length - 1];
172
- if (statusValue !== 0 && statusValue !== 1) {
173
- throw new TimestampError(`Timestamp request rejected with status: ${statusValue}`);
174
- }
175
- if (!children[1]) {
176
- throw new TimestampError("Timestamp response does not contain a token");
177
- }
178
- return encodeDer2(children[1]);
179
- }
180
-
181
- class TimestampError extends Error {
182
- constructor(message) {
183
- super(message);
184
- this.name = "TimestampError";
185
- }
186
- }
138
+ embedSignature,
139
+ extractBytesToSign,
140
+ findByteRange,
141
+ loadPdf
142
+ } from "@f-o-t/pdf/plugins/editing";
187
143
 
188
144
  // src/appearance.ts
145
+ import { hash as hash2 } from "@f-o-t/crypto";
189
146
  import { generateQrCode } from "@f-o-t/qrcode";
190
- import { hash as hash3 } from "@f-o-t/crypto";
191
147
  function drawSignatureAppearance(doc, page, appearance, certInfo, options) {
192
- const { x, y, width, height } = appearance;
148
+ const { x, width, height } = appearance;
193
149
  const showQrCode = appearance.showQrCode !== false;
194
150
  const showCertInfo = appearance.showCertInfo !== false;
151
+ const y = page.height - appearance.y - height;
195
152
  let qrSize = 0;
196
153
  if (showQrCode) {
197
154
  const qrData = options.qrCode?.data || createVerificationData(certInfo, options.pdfData);
@@ -288,7 +245,7 @@ function drawCertInfo(page, certInfo, opts) {
288
245
  }
289
246
  }
290
247
  function createVerificationData(certInfo, pdfData) {
291
- const documentHash = toHex(hash3("sha256", pdfData));
248
+ const documentHash = toHex(hash2("sha256", pdfData));
292
249
  const timestamp = new Date().toISOString();
293
250
  if (certInfo) {
294
251
  const certFingerprint = certInfo.fingerprint;
@@ -310,39 +267,82 @@ function toHex(data) {
310
267
  return chars.join("");
311
268
  }
312
269
 
313
- // src/schemas.ts
314
- import { z } from "zod";
315
- var signatureAppearanceSchema = z.object({
316
- x: z.number(),
317
- y: z.number(),
318
- width: z.number().positive(),
319
- height: z.number().positive(),
320
- page: z.number().int().min(0).optional(),
321
- showQrCode: z.boolean().optional(),
322
- showCertInfo: z.boolean().optional()
323
- });
324
- var qrCodeConfigSchema = z.object({
325
- data: z.string().optional(),
326
- size: z.number().int().positive().optional()
327
- });
328
- var pdfSignOptionsSchema = z.object({
329
- certificate: z.object({
330
- p12: z.instanceof(Uint8Array).refine((v) => v.length > 0, {
331
- message: "P12 data must not be empty"
332
- }),
333
- password: z.string(),
334
- name: z.string().optional()
335
- }),
336
- reason: z.string().optional(),
337
- location: z.string().optional(),
338
- contactInfo: z.string().optional(),
339
- policy: z.enum(["pades-ades", "pades-icp-brasil"]).optional(),
340
- timestamp: z.boolean().optional(),
341
- tsaUrl: z.string().url().optional(),
342
- appearance: z.union([signatureAppearanceSchema, z.literal(false)]).optional(),
343
- qrCode: qrCodeConfigSchema.optional(),
344
- docMdpPermission: z.union([z.literal(1), z.literal(2), z.literal(3)]).optional()
345
- });
270
+ // src/timestamp.ts
271
+ import {
272
+ boolean as asn1Boolean,
273
+ decodeDer as decodeDer2,
274
+ encodeDer as encodeDer2,
275
+ integer,
276
+ octetString as octetString2,
277
+ oid as oid2,
278
+ sequence as sequence2
279
+ } from "@f-o-t/asn1";
280
+ import { hash as hash3 } from "@f-o-t/crypto";
281
+ var HASH_OIDS = {
282
+ sha256: "2.16.840.1.101.3.4.2.1",
283
+ sha384: "2.16.840.1.101.3.4.2.2",
284
+ sha512: "2.16.840.1.101.3.4.2.3"
285
+ };
286
+ var TIMESTAMP_SERVERS = {
287
+ VALID: "http://timestamp.valid.com.br/tsa",
288
+ SAFEWEB: "http://tsa.safeweb.com.br/tsa/tsa",
289
+ CERTISIGN: "http://timestamp.certisign.com.br"
290
+ };
291
+ var TIMESTAMP_TOKEN_OID = "1.2.840.113549.1.9.16.2.14";
292
+ async function requestTimestamp(dataToTimestamp, tsaUrl, hashAlgorithm = "sha256") {
293
+ const messageHash = hash3(hashAlgorithm, dataToTimestamp);
294
+ const timestampReq = buildTimestampRequest(messageHash, hashAlgorithm);
295
+ const response = await fetch(tsaUrl, {
296
+ method: "POST",
297
+ headers: {
298
+ "Content-Type": "application/timestamp-query"
299
+ },
300
+ body: timestampReq,
301
+ signal: AbortSignal.timeout(1e4)
302
+ });
303
+ if (!response.ok) {
304
+ throw new TimestampError(`TSA returned HTTP ${response.status}`);
305
+ }
306
+ const respBuffer = new Uint8Array(await response.arrayBuffer());
307
+ return extractTimestampToken(respBuffer);
308
+ }
309
+ function buildTimestampRequest(messageHash, hashAlgorithm) {
310
+ const hashOid = HASH_OIDS[hashAlgorithm];
311
+ if (!hashOid) {
312
+ throw new TimestampError(`Unsupported hash algorithm: ${hashAlgorithm}`);
313
+ }
314
+ const timestampReq = sequence2(integer(1), sequence2(sequence2(oid2(hashOid)), octetString2(messageHash)), asn1Boolean(true));
315
+ return encodeDer2(timestampReq);
316
+ }
317
+ function extractTimestampToken(respDer) {
318
+ let resp;
319
+ try {
320
+ resp = decodeDer2(respDer);
321
+ } catch {
322
+ throw new TimestampError("Invalid timestamp response: not valid DER");
323
+ }
324
+ const children = resp.value;
325
+ if (!Array.isArray(children) || children.length < 1) {
326
+ throw new TimestampError("Invalid timestamp response: unexpected structure");
327
+ }
328
+ const statusInfo = children[0].value;
329
+ const statusBytes = statusInfo[0].value;
330
+ const statusValue = statusBytes[statusBytes.length - 1];
331
+ if (statusValue !== 0 && statusValue !== 1) {
332
+ throw new TimestampError(`Timestamp request rejected with status: ${statusValue}`);
333
+ }
334
+ if (!children[1]) {
335
+ throw new TimestampError("Timestamp response does not contain a token");
336
+ }
337
+ return encodeDer2(children[1]);
338
+ }
339
+
340
+ class TimestampError extends Error {
341
+ constructor(message) {
342
+ super(message);
343
+ this.name = "TimestampError";
344
+ }
345
+ }
346
346
 
347
347
  // src/sign-pdf.ts
348
348
  async function signPdf(pdf, options) {
@@ -365,6 +365,21 @@ async function signPdf(pdf, options) {
365
365
  pdfData: pdf
366
366
  });
367
367
  }
368
+ if (opts.appearances && opts.appearances.length > 0) {
369
+ for (const app of opts.appearances) {
370
+ const pageIndex = app.page ?? 0;
371
+ if (pageIndex < 0 || pageIndex >= doc.pageCount) {
372
+ throw new PdfSignError(`Invalid page index ${pageIndex} in appearances. PDF has ${doc.pageCount} pages.`);
373
+ }
374
+ const page = doc.getPage(pageIndex);
375
+ drawSignatureAppearance(doc, page, app, certInfo, {
376
+ reason: opts.reason,
377
+ location: opts.location,
378
+ qrCode: opts.qrCode,
379
+ pdfData: pdf
380
+ });
381
+ }
382
+ }
368
383
  const signerName = certInfo?.subject.commonName || opts.certificate.name || "Digital Signature";
369
384
  const { pdf: pdfWithPlaceholder } = doc.saveWithPlaceholder({
370
385
  reason: opts.reason || "Digitally signed",
@@ -432,4 +447,4 @@ export {
432
447
  ICP_BRASIL_OIDS
433
448
  };
434
449
 
435
- //# debugId=F1FD1A009917885C64756E2164756E21
450
+ //# debugId=847A450B084A9AAE64756E2164756E21
package/dist/index.js.map CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/sign-pdf.ts", "../src/icp-brasil.ts", "../src/timestamp.ts", "../src/appearance.ts", "../src/schemas.ts"],
3
+ "sources": ["../src/icp-brasil.ts", "../src/schemas.ts", "../src/sign-pdf.ts", "../src/appearance.ts", "../src/timestamp.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * PDF Signing — Main Entry Point\n *\n * Signs a PDF document using PAdES format with optional ICP-Brasil compliance.\n *\n * Flow:\n * 1. Parse certificate for display info\n * 2. Load PDF and draw visual appearance\n * 3. Save PDF with signature placeholder\n * 4. Find byte range and extract bytes to sign\n * 5. Build CMS/PKCS#7 SignedData with ICP-Brasil attributes\n * 6. Embed signature into PDF\n */\n\nimport { parseCertificate } from \"@f-o-t/digital-certificate\";\nimport type { CertificateInfo } from \"@f-o-t/digital-certificate\";\nimport { parsePkcs12, createSignedData } from \"@f-o-t/crypto\";\nimport type { CmsAttribute } from \"@f-o-t/crypto\";\nimport {\n\tloadPdf,\n\tfindByteRange,\n\textractBytesToSign,\n\tembedSignature,\n} from \"@f-o-t/pdf/plugins/editing\";\nimport {\n\tbuildSigningCertificateV2,\n\tbuildSignaturePolicy,\n\tICP_BRASIL_OIDS,\n} from \"./icp-brasil.ts\";\nimport { requestTimestamp } from \"./timestamp.ts\";\nimport { drawSignatureAppearance } from \"./appearance.ts\";\nimport { pdfSignOptionsSchema } from \"./schemas.ts\";\nimport type { PdfSignOptions } from \"./types.ts\";\n\n/**\n * Sign a PDF document with a digital certificate.\n *\n * Supports PAdES-BES and PAdES with ICP-Brasil compliance\n * (signing-certificate-v2 and signature-policy attributes).\n *\n * @param pdf - The PDF document as a Uint8Array\n * @param options - Signing options\n * @returns The signed PDF as a Uint8Array\n *\n * @example\n * ```ts\n * const signedPdf = await signPdf(pdfBytes, {\n * certificate: { p12, password: \"secret\" },\n * reason: \"Document approval\",\n * location: \"Corporate Office\",\n * policy: \"pades-icp-brasil\",\n * });\n * ```\n */\nexport async function signPdf(\n\tpdf: Uint8Array,\n\toptions: PdfSignOptions,\n): Promise<Uint8Array> {\n\t// Validate input\n\tconst opts = pdfSignOptionsSchema.parse(options);\n\n\t// 1. Parse certificate for display info (uses OpenSSL, needs Buffer)\n\tlet certInfo: CertificateInfo | null = null;\n\ttry {\n\t\tcertInfo = parseCertificate(\n\t\t\tBuffer.from(opts.certificate.p12),\n\t\t\topts.certificate.password,\n\t\t);\n\t} catch {\n\t\t// If parsing fails, continue without cert info for display\n\t}\n\n\t// 2. Load PDF via editing plugin\n\tconst doc = loadPdf(pdf);\n\n\t// 3. Draw visual signature appearance if requested\n\tif (opts.appearance !== false && opts.appearance) {\n\t\tconst pageIndex = opts.appearance.page ?? 0;\n\n\t\tif (pageIndex < 0 || pageIndex >= doc.pageCount) {\n\t\t\tthrow new PdfSignError(\n\t\t\t\t`Invalid page index: ${pageIndex}. PDF has ${doc.pageCount} pages.`,\n\t\t\t);\n\t\t}\n\n\t\tconst page = doc.getPage(pageIndex);\n\n\t\tdrawSignatureAppearance(doc, page, opts.appearance, certInfo, {\n\t\t\treason: opts.reason,\n\t\t\tlocation: opts.location,\n\t\t\tqrCode: opts.qrCode,\n\t\t\tpdfData: pdf,\n\t\t});\n\t}\n\n\t// 4. Save with signature placeholder\n\tconst signerName =\n\t\tcertInfo?.subject.commonName ||\n\t\topts.certificate.name ||\n\t\t\"Digital Signature\";\n\n\tconst { pdf: pdfWithPlaceholder } = doc.saveWithPlaceholder({\n\t\treason: opts.reason || \"Digitally signed\",\n\t\tname: signerName,\n\t\tlocation: opts.location,\n\t\tcontactInfo: opts.contactInfo,\n\t\tsignatureLength: 16384,\n\t\tdocMdpPermission: opts.docMdpPermission ?? 2,\n\t});\n\n\t// 5. Find byte range and extract bytes to sign\n\tconst { byteRange } = findByteRange(pdfWithPlaceholder);\n\tconst bytesToSign = extractBytesToSign(pdfWithPlaceholder, byteRange);\n\n\t// 6. Parse PKCS#12 for cryptographic material\n\tconst { certificate, privateKey, chain } = parsePkcs12(\n\t\topts.certificate.p12,\n\t\topts.certificate.password,\n\t);\n\n\t// 7. Build ICP-Brasil authenticated attributes if needed\n\tconst authenticatedAttributes: CmsAttribute[] = [];\n\n\tif (opts.policy === \"pades-icp-brasil\") {\n\t\t// signing-certificate-v2\n\t\tconst sigCertV2 = buildSigningCertificateV2(certificate);\n\t\tauthenticatedAttributes.push({\n\t\t\toid: ICP_BRASIL_OIDS.signingCertificateV2,\n\t\t\tvalues: [sigCertV2],\n\t\t});\n\n\t\t// signature-policy\n\t\ttry {\n\t\t\tconst sigPolicy = await buildSignaturePolicy();\n\t\t\tauthenticatedAttributes.push({\n\t\t\t\toid: ICP_BRASIL_OIDS.signaturePolicy,\n\t\t\t\tvalues: [sigPolicy],\n\t\t\t});\n\t\t} catch {\n\t\t\t// Policy download failure is non-fatal\n\t\t\t// The signature will still be valid PAdES-BES\n\t\t}\n\t}\n\n\t// 8. Build unauthenticated attributes\n\tconst unauthenticatedAttributes: CmsAttribute[] = [];\n\n\t// 9. Create CMS/PKCS#7 SignedData\n\tconst signedData = createSignedData({\n\t\tcontent: bytesToSign,\n\t\tcertificate,\n\t\tprivateKey,\n\t\tchain,\n\t\thashAlgorithm: \"sha256\",\n\t\tauthenticatedAttributes:\n\t\t\tauthenticatedAttributes.length > 0\n\t\t\t\t? authenticatedAttributes\n\t\t\t\t: undefined,\n\t\tunauthenticatedAttributes:\n\t\t\tunauthenticatedAttributes.length > 0\n\t\t\t\t? unauthenticatedAttributes\n\t\t\t\t: undefined,\n\t\tdetached: true,\n\t});\n\n\t// 10. Optionally request timestamp\n\tif (opts.timestamp && opts.tsaUrl) {\n\t\ttry {\n\t\t\tconst tsToken = await requestTimestamp(signedData, opts.tsaUrl);\n\t\t\t// Note: Adding the timestamp as an unauthenticated attribute\n\t\t\t// would require re-building the CMS structure. For now, we log\n\t\t\t// that timestamp was received. A future version will embed it.\n\t\t\t// TODO: Rebuild CMS with timestamp token as unauthenticated attribute\n\t\t\tvoid tsToken;\n\t\t} catch {\n\t\t\t// Timestamp failure is non-fatal\n\t\t}\n\t}\n\n\t// 11. Embed signature into PDF\n\treturn embedSignature(pdfWithPlaceholder, signedData);\n}\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class PdfSignError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"PdfSignError\";\n\t}\n}\n",
6
- "/**\n * ICP-Brasil Attributes for PAdES Signatures\n *\n * Implements the id-aa-signingCertificateV2 (RFC 5035) and\n * id-aa-ets-sigPolicyId attributes required by ICP-Brasil.\n *\n * Uses @f-o-t/asn1 for ASN.1 construction and @f-o-t/crypto for hashing.\n */\n\nimport {\n\tencodeDer,\n\tdecodeDer,\n\tsequence,\n\toid,\n\toctetString,\n\tnullValue,\n\tia5String,\n\tcontextTag,\n\ttype Asn1Node,\n} from \"@f-o-t/asn1\";\nimport { hash } from \"@f-o-t/crypto\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** SHA-256 Algorithm OID */\nconst SHA256_OID = \"2.16.840.1.101.3.4.2.1\";\n\n/** id-aa-signingCertificateV2 OID (RFC 5035) */\nconst SIGNING_CERTIFICATE_V2_OID = \"1.2.840.113549.1.9.16.2.47\";\n\n/** id-aa-ets-sigPolicyId OID */\nconst SIGNATURE_POLICY_OID = \"1.2.840.113549.1.9.16.2.15\";\n\n/** ICP-Brasil PAdES Policy (PA_PAdES_AD_RB_v1_1) */\nconst POLICY_CONFIG = {\n\tOID: \"2.16.76.1.7.1.11.1.1\",\n\tURL: \"http://politicas.icpbrasil.gov.br/PA_PAdES_AD_RB_v1_1.der\",\n} as const;\n\n/** id-spq-ets-uri OID */\nconst SPQ_ETS_URI_OID = \"1.2.840.113549.1.9.16.5.1\";\n\n// ---------------------------------------------------------------------------\n// Cached policy data\n// ---------------------------------------------------------------------------\n\nlet cachedPolicyData: {\n\thashAlgOid: string;\n\tpolicyHash: Uint8Array;\n} | null = null;\n\n/**\n * Clear the cached signature policy data.\n * Useful for testing or forcing a re-download.\n */\nexport function clearPolicyCache(): void {\n\tcachedPolicyData = null;\n}\n\n// ---------------------------------------------------------------------------\n// Signing Certificate V2\n// ---------------------------------------------------------------------------\n\n/**\n * Build the id-aa-signingCertificateV2 attribute value (DER-encoded).\n *\n * This attribute links the signature to the specific certificate used to\n * create it, preventing substitution attacks.\n *\n * ASN.1 structure (RFC 5035):\n *\n * SigningCertificateV2 ::= SEQUENCE {\n * certs SEQUENCE OF ESSCertIDv2\n * }\n *\n * ESSCertIDv2 ::= SEQUENCE {\n * hashAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256},\n * certHash Hash,\n * issuerSerial IssuerSerial OPTIONAL\n * }\n *\n * IssuerSerial ::= SEQUENCE {\n * issuer GeneralNames,\n * serialNumber CertificateSerialNumber\n * }\n *\n * @param certDer - DER-encoded X.509 certificate\n * @returns DER-encoded SigningCertificateV2 value\n */\nexport function buildSigningCertificateV2(certDer: Uint8Array): Uint8Array {\n\t// 1. Hash the DER certificate with SHA-256\n\tconst certHash = hash(\"sha256\", certDer);\n\n\t// 2. Build AlgorithmIdentifier for SHA-256\n\tconst hashAlgId = sequence(oid(SHA256_OID), nullValue());\n\n\t// 3. Extract issuer and serial number from the certificate\n\tconst cert = decodeDer(certDer);\n\tconst tbsCert = (cert.value as Asn1Node[])[0]!;\n\tconst tbs = tbsCert.value as Asn1Node[];\n\n\t// version is [0] EXPLICIT, so tbs[0] may be version context tag\n\tlet idx = 0;\n\tif (tbs[0]!.class === \"context\" && tbs[0]!.tag === 0) {\n\t\tidx = 1;\n\t}\n\n\tconst serialNumber = tbs[idx]!; // INTEGER\n\tconst issuerName = tbs[idx + 2]!; // Name SEQUENCE\n\n\t// 4. Build IssuerSerial\n\t// IssuerSerial ::= SEQUENCE { issuer GeneralNames, serialNumber INTEGER }\n\t// GeneralNames ::= SEQUENCE OF GeneralName\n\t// GeneralName ::= directoryName [4] Name\n\tconst generalName = contextTag(4, [issuerName]);\n\tconst generalNames = sequence(generalName);\n\tconst issuerSerial = sequence(generalNames, serialNumber);\n\n\t// 5. Build ESSCertIDv2\n\tconst essCertIdV2 = sequence(hashAlgId, octetString(certHash), issuerSerial);\n\n\t// 6. Build SigningCertificateV2\n\tconst signingCertV2 = sequence(\n\t\t// certs SEQUENCE OF ESSCertIDv2\n\t\tsequence(essCertIdV2),\n\t);\n\n\treturn encodeDer(signingCertV2);\n}\n\n// ---------------------------------------------------------------------------\n// Signature Policy\n// ---------------------------------------------------------------------------\n\n/**\n * Download and parse the ICP-Brasil signature policy DER file.\n * Extracts the embedded signPolicyHash from the ASN.1 structure.\n */\nasync function downloadAndParsePolicyDocument(): Promise<{\n\thashAlgOid: string;\n\tpolicyHash: Uint8Array;\n}> {\n\tif (cachedPolicyData) {\n\t\treturn cachedPolicyData;\n\t}\n\n\tconst response = await fetch(POLICY_CONFIG.URL);\n\n\tif (!response.ok) {\n\t\tthrow new SignaturePolicyError(\n\t\t\t`Failed to download signature policy: HTTP ${response.status}`,\n\t\t);\n\t}\n\n\tconst arrayBuffer = await response.arrayBuffer();\n\tconst data = new Uint8Array(arrayBuffer);\n\n\tif (data.length === 0 || data[0] !== 0x30) {\n\t\tthrow new SignaturePolicyError(\"Invalid DER format in policy document\");\n\t}\n\n\t// Parse the ASN.1 structure:\n\t// SignaturePolicy ::= SEQUENCE {\n\t// signPolicyHashAlg AlgorithmIdentifier,\n\t// signPolicyInfo SignaturePolicyInfo,\n\t// signPolicyHash OCTET STRING\n\t// }\n\tconst asn1 = decodeDer(data);\n\tconst children = asn1.value as Asn1Node[];\n\n\tif (!Array.isArray(children) || children.length < 3) {\n\t\tthrow new SignaturePolicyError(\n\t\t\t`Unexpected policy structure: expected 3+ children, got ${children?.length}`,\n\t\t);\n\t}\n\n\t// Child[0] = AlgorithmIdentifier\n\tconst algIdChildren = children[0]!.value as Asn1Node[];\n\tif (!Array.isArray(algIdChildren) || algIdChildren.length === 0) {\n\t\tthrow new SignaturePolicyError(\n\t\t\t\"Invalid AlgorithmIdentifier in policy\",\n\t\t);\n\t}\n\n\tconst { bytesToOid } = await import(\"@f-o-t/asn1\");\n\tconst hashAlgOid = bytesToOid(algIdChildren[0]!.value as Uint8Array);\n\n\t// Child[2] = signPolicyHash (OCTET STRING)\n\tconst hashNode = children[2]!;\n\tif (hashNode.tag !== 0x04) {\n\t\tthrow new SignaturePolicyError(\n\t\t\t`Expected OCTET STRING at child[2], got tag 0x${hashNode.tag.toString(16)}`,\n\t\t);\n\t}\n\n\tcachedPolicyData = {\n\t\thashAlgOid,\n\t\tpolicyHash: hashNode.value as Uint8Array,\n\t};\n\n\treturn cachedPolicyData;\n}\n\n/**\n * Build the id-aa-ets-sigPolicyId attribute value (DER-encoded).\n *\n * Downloads the ICP-Brasil PAdES signature policy and extracts the\n * embedded signPolicyHash to build the attribute.\n *\n * @returns DER-encoded SignaturePolicyIdentifier value\n */\nexport async function buildSignaturePolicy(): Promise<Uint8Array> {\n\tconst { hashAlgOid, policyHash } = await downloadAndParsePolicyDocument();\n\n\t// AlgorithmIdentifier for hash (no NULL — matches policy encoding)\n\tconst hashAlgId = sequence(oid(hashAlgOid));\n\n\t// SigPolicyHash (OtherHashAlgAndValue)\n\tconst sigPolicyHash = sequence(hashAlgId, octetString(policyHash));\n\n\t// SigPolicyQualifiers with policy URL\n\tconst sigPolicyQualifiers = sequence(\n\t\tsequence(\n\t\t\t// id-spq-ets-uri\n\t\t\toid(SPQ_ETS_URI_OID),\n\t\t\t// Policy URL as IA5String\n\t\t\tia5String(POLICY_CONFIG.URL),\n\t\t),\n\t);\n\n\t// SignaturePolicyId\n\tconst signaturePolicyId = sequence(\n\t\t// sigPolicyId (policy OID)\n\t\toid(POLICY_CONFIG.OID),\n\t\t// sigPolicyHash\n\t\tsigPolicyHash,\n\t\t// sigPolicyQualifiers\n\t\tsigPolicyQualifiers,\n\t);\n\n\treturn encodeDer(signaturePolicyId);\n}\n\n/**\n * Attribute OID constants for external use\n */\nexport const ICP_BRASIL_OIDS = {\n\tsigningCertificateV2: SIGNING_CERTIFICATE_V2_OID,\n\tsignaturePolicy: SIGNATURE_POLICY_OID,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class SignaturePolicyError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"SignaturePolicyError\";\n\t}\n}\n",
7
- "/**\n * RFC 3161 Timestamp Client\n *\n * Requests trusted timestamps from TSA servers using native fetch.\n * Builds the TimeStampReq using @f-o-t/asn1 instead of forge.\n */\n\nimport {\n\tencodeDer,\n\tdecodeDer,\n\tsequence,\n\tinteger,\n\toid,\n\toctetString,\n\tboolean as asn1Boolean,\n\ttype Asn1Node,\n} from \"@f-o-t/asn1\";\nimport { hash } from \"@f-o-t/crypto\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Well-known hash algorithm OIDs */\nconst HASH_OIDS: Record<string, string> = {\n\tsha256: \"2.16.840.1.101.3.4.2.1\",\n\tsha384: \"2.16.840.1.101.3.4.2.2\",\n\tsha512: \"2.16.840.1.101.3.4.2.3\",\n};\n\n/** ICP-Brasil Approved Timestamp Servers */\nexport const TIMESTAMP_SERVERS = {\n\tVALID: \"http://timestamp.valid.com.br/tsa\",\n\tSAFEWEB: \"http://tsa.safeweb.com.br/tsa/tsa\",\n\tCERTISIGN: \"http://timestamp.certisign.com.br\",\n} as const;\n\n/** id-smime-aa-timeStampToken OID */\nexport const TIMESTAMP_TOKEN_OID = \"1.2.840.113549.1.9.16.2.14\";\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Request a timestamp from a TSA server.\n *\n * @param dataToTimestamp - The data to timestamp (usually the signature value)\n * @param tsaUrl - URL of the timestamp server\n * @param hashAlgorithm - Hash algorithm to use (default: \"sha256\")\n * @returns DER-encoded TimeStampToken\n */\nexport async function requestTimestamp(\n\tdataToTimestamp: Uint8Array,\n\ttsaUrl: string,\n\thashAlgorithm: \"sha256\" | \"sha384\" | \"sha512\" = \"sha256\",\n): Promise<Uint8Array> {\n\t// 1. Hash the data\n\tconst messageHash = hash(hashAlgorithm, dataToTimestamp);\n\n\t// 2. Build TimeStampReq\n\tconst timestampReq = buildTimestampRequest(messageHash, hashAlgorithm);\n\n\t// 3. Send to TSA\n\tconst response = await fetch(tsaUrl, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/timestamp-query\",\n\t\t},\n\t\tbody: timestampReq as unknown as BodyInit,\n\t\tsignal: AbortSignal.timeout(10000),\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new TimestampError(`TSA returned HTTP ${response.status}`);\n\t}\n\n\tconst respBuffer = new Uint8Array(await response.arrayBuffer());\n\n\t// 4. Validate and extract token\n\treturn extractTimestampToken(respBuffer);\n}\n\n// ---------------------------------------------------------------------------\n// Internal\n// ---------------------------------------------------------------------------\n\n/**\n * Build a TimeStampReq (RFC 3161) as DER bytes.\n *\n * TimeStampReq ::= SEQUENCE {\n * version INTEGER { v1(1) },\n * messageImprint MessageImprint,\n * certReq BOOLEAN DEFAULT FALSE\n * }\n *\n * MessageImprint ::= SEQUENCE {\n * hashAlgorithm AlgorithmIdentifier,\n * hashedMessage OCTET STRING\n * }\n */\nfunction buildTimestampRequest(\n\tmessageHash: Uint8Array,\n\thashAlgorithm: string,\n): Uint8Array {\n\tconst hashOid = HASH_OIDS[hashAlgorithm];\n\tif (!hashOid) {\n\t\tthrow new TimestampError(`Unsupported hash algorithm: ${hashAlgorithm}`);\n\t}\n\n\tconst timestampReq = sequence(\n\t\t// version = 1\n\t\tinteger(1),\n\t\t// messageImprint\n\t\tsequence(\n\t\t\t// hashAlgorithm AlgorithmIdentifier\n\t\t\tsequence(oid(hashOid)),\n\t\t\t// hashedMessage OCTET STRING\n\t\t\toctetString(messageHash),\n\t\t),\n\t\t// certReq = TRUE (request certificate in response)\n\t\tasn1Boolean(true),\n\t);\n\n\treturn encodeDer(timestampReq);\n}\n\n/**\n * Extract the TimeStampToken from a TimeStampResp.\n *\n * TimeStampResp ::= SEQUENCE {\n * status PKIStatusInfo,\n * timeStampToken TimeStampToken OPTIONAL\n * }\n *\n * PKIStatusInfo ::= SEQUENCE {\n * status PKIStatus, -- INTEGER\n * ...\n * }\n */\nfunction extractTimestampToken(respDer: Uint8Array): Uint8Array {\n\tlet resp: Asn1Node;\n\ttry {\n\t\tresp = decodeDer(respDer);\n\t} catch {\n\t\tthrow new TimestampError(\"Invalid timestamp response: not valid DER\");\n\t}\n\n\tconst children = resp.value as Asn1Node[];\n\tif (!Array.isArray(children) || children.length < 1) {\n\t\tthrow new TimestampError(\n\t\t\t\"Invalid timestamp response: unexpected structure\",\n\t\t);\n\t}\n\n\t// Check status\n\tconst statusInfo = children[0]!.value as Asn1Node[];\n\tconst statusBytes = statusInfo[0]!.value as Uint8Array;\n\t// Status 0 = granted, 1 = grantedWithMods\n\tconst statusValue = statusBytes[statusBytes.length - 1]!;\n\tif (statusValue !== 0 && statusValue !== 1) {\n\t\tthrow new TimestampError(\n\t\t\t`Timestamp request rejected with status: ${statusValue}`,\n\t\t);\n\t}\n\n\t// Extract token (second child)\n\tif (!children[1]) {\n\t\tthrow new TimestampError(\n\t\t\t\"Timestamp response does not contain a token\",\n\t\t);\n\t}\n\n\treturn encodeDer(children[1]);\n}\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class TimestampError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"TimestampError\";\n\t}\n}\n",
8
- "/**\n * Visual Signature Appearance\n *\n * Draws certificate information and QR code on the PDF page\n * for visible digital signatures.\n */\n\nimport type { CertificateInfo } from \"@f-o-t/digital-certificate\";\nimport type { PdfPage, PdfDocument } from \"@f-o-t/pdf/plugins/editing\";\nimport { generateQrCode } from \"@f-o-t/qrcode\";\nimport { hash } from \"@f-o-t/crypto\";\nimport type { SignatureAppearance, QrCodeConfig } from \"./types.ts\";\n\n/**\n * Draw the visual signature appearance on a PDF page.\n *\n * Includes optional QR code and certificate information text.\n */\nexport function drawSignatureAppearance(\n\tdoc: PdfDocument,\n\tpage: PdfPage,\n\tappearance: SignatureAppearance,\n\tcertInfo: CertificateInfo | null,\n\toptions: {\n\t\treason?: string;\n\t\tlocation?: string;\n\t\tqrCode?: QrCodeConfig;\n\t\tpdfData: Uint8Array;\n\t},\n): void {\n\tconst { x, y, width, height } = appearance;\n\tconst showQrCode = appearance.showQrCode !== false;\n\tconst showCertInfo = appearance.showCertInfo !== false;\n\n\tlet qrSize = 0;\n\n\t// Draw QR code if requested (enabled by default)\n\tif (showQrCode) {\n\t\tconst qrData =\n\t\t\toptions.qrCode?.data ||\n\t\t\tcreateVerificationData(certInfo, options.pdfData);\n\n\t\tconst qrPng = generateQrCode(qrData, {\n\t\t\tsize: options.qrCode?.size || 100,\n\t\t});\n\n\t\tconst qrImage = doc.embedPng(qrPng);\n\t\tqrSize = Math.min(100, height - 20);\n\n\t\tpage.drawImage(qrImage, {\n\t\t\tx: x + 10,\n\t\t\ty: y + 10,\n\t\t\twidth: qrSize,\n\t\t\theight: qrSize,\n\t\t});\n\t}\n\n\t// Draw certificate info text\n\tif (showCertInfo) {\n\t\tdrawCertInfo(page, certInfo, {\n\t\t\tx,\n\t\t\ty,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tqrOffset: qrSize > 0 ? qrSize + 20 : 10,\n\t\t\treason: options.reason,\n\t\t\tlocation: options.location,\n\t\t});\n\t}\n}\n\n/**\n * Draw certificate information text on the page\n */\nfunction drawCertInfo(\n\tpage: PdfPage,\n\tcertInfo: CertificateInfo | null,\n\topts: {\n\t\tx: number;\n\t\ty: number;\n\t\twidth: number;\n\t\theight: number;\n\t\tqrOffset: number;\n\t\treason?: string;\n\t\tlocation?: string;\n\t},\n): void {\n\tconst textX = opts.x + opts.qrOffset;\n\tlet textY = opts.y + opts.height - 20;\n\tconst fontSize = 10;\n\tconst lineHeight = 14;\n\n\t// Header\n\tpage.drawText(\"ASSINADO DIGITALMENTE\", {\n\t\tx: textX,\n\t\ty: textY,\n\t\tsize: 12,\n\t});\n\ttextY -= lineHeight * 1.5;\n\n\tif (certInfo) {\n\t\t// Signer name\n\t\tconst signerName = certInfo.subject.commonName || \"N/A\";\n\t\tpage.drawText(`Assinado por: ${signerName}`, {\n\t\t\tx: textX,\n\t\t\ty: textY,\n\t\t\tsize: fontSize,\n\t\t});\n\t\ttextY -= lineHeight;\n\n\t\t// CNPJ or CPF\n\t\tif (certInfo.brazilian.cnpj) {\n\t\t\tconst cnpj = formatCnpj(certInfo.brazilian.cnpj);\n\t\t\tpage.drawText(`CNPJ: ${cnpj}`, {\n\t\t\t\tx: textX,\n\t\t\t\ty: textY,\n\t\t\t\tsize: fontSize,\n\t\t\t});\n\t\t\ttextY -= lineHeight;\n\t\t} else if (certInfo.brazilian.cpf) {\n\t\t\tconst cpf = formatCpf(certInfo.brazilian.cpf);\n\t\t\tpage.drawText(`CPF: ${cpf}`, {\n\t\t\t\tx: textX,\n\t\t\t\ty: textY,\n\t\t\t\tsize: fontSize,\n\t\t\t});\n\t\t\ttextY -= lineHeight;\n\t\t}\n\n\t\t// Date and time\n\t\tconst now = new Date();\n\t\tconst dateStr = now.toLocaleDateString(\"pt-BR\");\n\t\tconst timeStr = now.toLocaleTimeString(\"pt-BR\");\n\t\tpage.drawText(`Data: ${dateStr} ${timeStr}`, {\n\t\t\tx: textX,\n\t\t\ty: textY,\n\t\t\tsize: fontSize,\n\t\t});\n\t\ttextY -= lineHeight;\n\n\t\t// Location\n\t\tif (opts.location) {\n\t\t\tpage.drawText(`Local: ${opts.location}`, {\n\t\t\t\tx: textX,\n\t\t\t\ty: textY,\n\t\t\t\tsize: fontSize - 1,\n\t\t\t});\n\t\t}\n\t} else {\n\t\t// Fallback if cert info not available\n\t\tpage.drawText(`Signed: ${opts.reason || \"Digital Signature\"}`, {\n\t\t\tx: textX,\n\t\t\ty: textY,\n\t\t\tsize: fontSize,\n\t\t});\n\t\ttextY -= lineHeight;\n\n\t\tif (opts.location) {\n\t\t\tpage.drawText(`Location: ${opts.location}`, {\n\t\t\t\tx: textX,\n\t\t\t\ty: textY,\n\t\t\t\tsize: fontSize,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Generate verification data for the QR code\n */\nfunction createVerificationData(\n\tcertInfo: CertificateInfo | null,\n\tpdfData: Uint8Array,\n): string {\n\tconst documentHash = toHex(hash(\"sha256\", pdfData));\n\tconst timestamp = new Date().toISOString();\n\n\tif (certInfo) {\n\t\tconst certFingerprint = certInfo.fingerprint;\n\t\treturn (\n\t\t\t`https://validar.iti.gov.br/?` +\n\t\t\t`doc=${documentHash.substring(0, 16)}&` +\n\t\t\t`cert=${certFingerprint.substring(0, 16)}&` +\n\t\t\t`time=${encodeURIComponent(timestamp)}`\n\t\t);\n\t}\n\n\treturn `https://validar.iti.gov.br/?doc=${documentHash.substring(0, 16)}&time=${encodeURIComponent(timestamp)}`;\n}\n\n/**\n * Format a CNPJ number with punctuation\n */\nfunction formatCnpj(cnpj: string): string {\n\treturn cnpj.replace(\n\t\t/(\\d{2})(\\d{3})(\\d{3})(\\d{4})(\\d{2})/,\n\t\t\"$1.$2.$3/$4-$5\",\n\t);\n}\n\n/**\n * Format a CPF number with punctuation\n */\nfunction formatCpf(cpf: string): string {\n\treturn cpf.replace(/(\\d{3})(\\d{3})(\\d{3})(\\d{2})/, \"$1.$2.$3-$4\");\n}\n\n/**\n * Convert bytes to hex string\n */\nfunction toHex(data: Uint8Array): string {\n\tconst chars: string[] = [];\n\tfor (let i = 0; i < data.length; i++) {\n\t\tchars.push(data[i]!.toString(16).padStart(2, \"0\"));\n\t}\n\treturn chars.join(\"\");\n}\n",
9
- "/**\n * Zod schemas for input validation\n */\n\nimport { z } from \"zod\";\n\nconst signatureAppearanceSchema = z.object({\n\tx: z.number(),\n\ty: z.number(),\n\twidth: z.number().positive(),\n\theight: z.number().positive(),\n\tpage: z.number().int().min(0).optional(),\n\tshowQrCode: z.boolean().optional(),\n\tshowCertInfo: z.boolean().optional(),\n});\n\nconst qrCodeConfigSchema = z.object({\n\tdata: z.string().optional(),\n\tsize: z.number().int().positive().optional(),\n});\n\nexport const pdfSignOptionsSchema = z.object({\n\tcertificate: z.object({\n\t\tp12: z.instanceof(Uint8Array).refine((v) => v.length > 0, {\n\t\t\tmessage: \"P12 data must not be empty\",\n\t\t}),\n\t\tpassword: z.string(),\n\t\tname: z.string().optional(),\n\t}),\n\treason: z.string().optional(),\n\tlocation: z.string().optional(),\n\tcontactInfo: z.string().optional(),\n\tpolicy: z.enum([\"pades-ades\", \"pades-icp-brasil\"]).optional(),\n\ttimestamp: z.boolean().optional(),\n\ttsaUrl: z.string().url().optional(),\n\tappearance: z\n\t\t.union([signatureAppearanceSchema, z.literal(false)])\n\t\t.optional(),\n\tqrCode: qrCodeConfigSchema.optional(),\n\tdocMdpPermission: z.union([z.literal(1), z.literal(2), z.literal(3)]).optional(),\n});\n"
5
+ "/**\n * ICP-Brasil Attributes for PAdES Signatures\n *\n * Implements the id-aa-signingCertificateV2 (RFC 5035) and\n * id-aa-ets-sigPolicyId attributes required by ICP-Brasil.\n *\n * Uses @f-o-t/asn1 for ASN.1 construction and @f-o-t/crypto for hashing.\n */\n\nimport {\n type Asn1Node,\n contextTag,\n decodeDer,\n encodeDer,\n ia5String,\n nullValue,\n octetString,\n oid,\n sequence,\n} from \"@f-o-t/asn1\";\nimport { hash } from \"@f-o-t/crypto\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** SHA-256 Algorithm OID */\nconst SHA256_OID = \"2.16.840.1.101.3.4.2.1\";\n\n/** id-aa-signingCertificateV2 OID (RFC 5035) */\nconst SIGNING_CERTIFICATE_V2_OID = \"1.2.840.113549.1.9.16.2.47\";\n\n/** id-aa-ets-sigPolicyId OID */\nconst SIGNATURE_POLICY_OID = \"1.2.840.113549.1.9.16.2.15\";\n\n/** ICP-Brasil PAdES Policy (PA_PAdES_AD_RB_v1_1) */\nconst POLICY_CONFIG = {\n OID: \"2.16.76.1.7.1.11.1.1\",\n URL: \"http://politicas.icpbrasil.gov.br/PA_PAdES_AD_RB_v1_1.der\",\n} as const;\n\n/** id-spq-ets-uri OID */\nconst SPQ_ETS_URI_OID = \"1.2.840.113549.1.9.16.5.1\";\n\n// ---------------------------------------------------------------------------\n// Cached policy data\n// ---------------------------------------------------------------------------\n\nlet cachedPolicyData: {\n hashAlgOid: string;\n policyHash: Uint8Array;\n} | null = null;\n\n/**\n * Clear the cached signature policy data.\n * Useful for testing or forcing a re-download.\n */\nexport function clearPolicyCache(): void {\n cachedPolicyData = null;\n}\n\n// ---------------------------------------------------------------------------\n// Signing Certificate V2\n// ---------------------------------------------------------------------------\n\n/**\n * Build the id-aa-signingCertificateV2 attribute value (DER-encoded).\n *\n * This attribute links the signature to the specific certificate used to\n * create it, preventing substitution attacks.\n *\n * ASN.1 structure (RFC 5035):\n *\n * SigningCertificateV2 ::= SEQUENCE {\n * certs SEQUENCE OF ESSCertIDv2\n * }\n *\n * ESSCertIDv2 ::= SEQUENCE {\n * hashAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256},\n * certHash Hash,\n * issuerSerial IssuerSerial OPTIONAL\n * }\n *\n * IssuerSerial ::= SEQUENCE {\n * issuer GeneralNames,\n * serialNumber CertificateSerialNumber\n * }\n *\n * @param certDer - DER-encoded X.509 certificate\n * @returns DER-encoded SigningCertificateV2 value\n */\nexport function buildSigningCertificateV2(certDer: Uint8Array): Uint8Array {\n // 1. Hash the DER certificate with SHA-256\n const certHash = hash(\"sha256\", certDer);\n\n // 2. Build AlgorithmIdentifier for SHA-256\n const hashAlgId = sequence(oid(SHA256_OID), nullValue());\n\n // 3. Extract issuer and serial number from the certificate\n const cert = decodeDer(certDer);\n const tbsCert = (cert.value as Asn1Node[])[0]!;\n const tbs = tbsCert.value as Asn1Node[];\n\n // version is [0] EXPLICIT, so tbs[0] may be version context tag\n let idx = 0;\n if (tbs[0]!.class === \"context\" && tbs[0]!.tag === 0) {\n idx = 1;\n }\n\n const serialNumber = tbs[idx]!; // INTEGER\n const issuerName = tbs[idx + 2]!; // Name SEQUENCE\n\n // 4. Build IssuerSerial\n // IssuerSerial ::= SEQUENCE { issuer GeneralNames, serialNumber INTEGER }\n // GeneralNames ::= SEQUENCE OF GeneralName\n // GeneralName ::= directoryName [4] Name\n const generalName = contextTag(4, [issuerName]);\n const generalNames = sequence(generalName);\n const issuerSerial = sequence(generalNames, serialNumber);\n\n // 5. Build ESSCertIDv2\n const essCertIdV2 = sequence(hashAlgId, octetString(certHash), issuerSerial);\n\n // 6. Build SigningCertificateV2\n const signingCertV2 = sequence(\n // certs SEQUENCE OF ESSCertIDv2\n sequence(essCertIdV2),\n );\n\n return encodeDer(signingCertV2);\n}\n\n// ---------------------------------------------------------------------------\n// Signature Policy\n// ---------------------------------------------------------------------------\n\n/**\n * Download and parse the ICP-Brasil signature policy DER file.\n * Extracts the embedded signPolicyHash from the ASN.1 structure.\n */\nasync function downloadAndParsePolicyDocument(): Promise<{\n hashAlgOid: string;\n policyHash: Uint8Array;\n}> {\n if (cachedPolicyData) {\n return cachedPolicyData;\n }\n\n const response = await fetch(POLICY_CONFIG.URL);\n\n if (!response.ok) {\n throw new SignaturePolicyError(\n `Failed to download signature policy: HTTP ${response.status}`,\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const data = new Uint8Array(arrayBuffer);\n\n if (data.length === 0 || data[0] !== 0x30) {\n throw new SignaturePolicyError(\"Invalid DER format in policy document\");\n }\n\n // Parse the ASN.1 structure:\n // SignaturePolicy ::= SEQUENCE {\n // signPolicyHashAlg AlgorithmIdentifier,\n // signPolicyInfo SignaturePolicyInfo,\n // signPolicyHash OCTET STRING\n // }\n const asn1 = decodeDer(data);\n const children = asn1.value as Asn1Node[];\n\n if (!Array.isArray(children) || children.length < 3) {\n throw new SignaturePolicyError(\n `Unexpected policy structure: expected 3+ children, got ${children?.length}`,\n );\n }\n\n // Child[0] = AlgorithmIdentifier\n const algIdChildren = children[0]!.value as Asn1Node[];\n if (!Array.isArray(algIdChildren) || algIdChildren.length === 0) {\n throw new SignaturePolicyError(\"Invalid AlgorithmIdentifier in policy\");\n }\n\n const { bytesToOid } = await import(\"@f-o-t/asn1\");\n const hashAlgOid = bytesToOid(algIdChildren[0]!.value as Uint8Array);\n\n // Child[2] = signPolicyHash (OCTET STRING)\n const hashNode = children[2]!;\n if (hashNode.tag !== 0x04) {\n throw new SignaturePolicyError(\n `Expected OCTET STRING at child[2], got tag 0x${hashNode.tag.toString(16)}`,\n );\n }\n\n cachedPolicyData = {\n hashAlgOid,\n policyHash: hashNode.value as Uint8Array,\n };\n\n return cachedPolicyData;\n}\n\n/**\n * Build the id-aa-ets-sigPolicyId attribute value (DER-encoded).\n *\n * Downloads the ICP-Brasil PAdES signature policy and extracts the\n * embedded signPolicyHash to build the attribute.\n *\n * @returns DER-encoded SignaturePolicyIdentifier value\n */\nexport async function buildSignaturePolicy(): Promise<Uint8Array> {\n const { hashAlgOid, policyHash } = await downloadAndParsePolicyDocument();\n\n // AlgorithmIdentifier for hash (no NULL matches policy encoding)\n const hashAlgId = sequence(oid(hashAlgOid));\n\n // SigPolicyHash (OtherHashAlgAndValue)\n const sigPolicyHash = sequence(hashAlgId, octetString(policyHash));\n\n // SigPolicyQualifiers with policy URL\n const sigPolicyQualifiers = sequence(\n sequence(\n // id-spq-ets-uri\n oid(SPQ_ETS_URI_OID),\n // Policy URL as IA5String\n ia5String(POLICY_CONFIG.URL),\n ),\n );\n\n // SignaturePolicyId\n const signaturePolicyId = sequence(\n // sigPolicyId (policy OID)\n oid(POLICY_CONFIG.OID),\n // sigPolicyHash\n sigPolicyHash,\n // sigPolicyQualifiers\n sigPolicyQualifiers,\n );\n\n return encodeDer(signaturePolicyId);\n}\n\n/**\n * Attribute OID constants for external use\n */\nexport const ICP_BRASIL_OIDS = {\n signingCertificateV2: SIGNING_CERTIFICATE_V2_OID,\n signaturePolicy: SIGNATURE_POLICY_OID,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class SignaturePolicyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SignaturePolicyError\";\n }\n}\n",
6
+ "/**\n * Zod schemas for input validation\n */\n\nimport { z } from \"zod\";\n\nconst signatureAppearanceSchema = z.object({\n x: z.number(),\n y: z.number(),\n width: z.number().positive(),\n height: z.number().positive(),\n page: z.number().int().min(0).optional(),\n showQrCode: z.boolean().optional(),\n showCertInfo: z.boolean().optional(),\n});\n\nconst qrCodeConfigSchema = z.object({\n data: z.string().optional(),\n size: z.number().int().positive().optional(),\n});\n\nexport const pdfSignOptionsSchema = z.object({\n certificate: z.object({\n p12: z.instanceof(Uint8Array).refine((v) => v.length > 0, {\n message: \"P12 data must not be empty\",\n }),\n password: z.string(),\n name: z.string().optional(),\n }),\n reason: z.string().optional(),\n location: z.string().optional(),\n contactInfo: z.string().optional(),\n policy: z.enum([\"pades-ades\", \"pades-icp-brasil\"]).optional(),\n timestamp: z.boolean().optional(),\n tsaUrl: z.string().url().optional(),\n appearance: z\n .union([signatureAppearanceSchema, z.literal(false)])\n .optional(),\n appearances: z.array(signatureAppearanceSchema).optional(),\n qrCode: qrCodeConfigSchema.optional(),\n docMdpPermission: z\n .union([z.literal(1), z.literal(2), z.literal(3)])\n .optional(),\n});\n",
7
+ "/**\n * PDF Signing Main Entry Point\n *\n * Signs a PDF document using PAdES format with optional ICP-Brasil compliance.\n *\n * Flow:\n * 1. Parse certificate for display info\n * 2. Load PDF and draw visual appearance\n * 3. Save PDF with signature placeholder\n * 4. Find byte range and extract bytes to sign\n * 5. Build CMS/PKCS#7 SignedData with ICP-Brasil attributes\n * 6. Embed signature into PDF\n */\n\nimport type { CmsAttribute } from \"@f-o-t/crypto\";\nimport { createSignedData, parsePkcs12 } from \"@f-o-t/crypto\";\nimport type { CertificateInfo } from \"@f-o-t/digital-certificate\";\nimport { parseCertificate } from \"@f-o-t/digital-certificate\";\nimport {\n embedSignature,\n extractBytesToSign,\n findByteRange,\n loadPdf,\n} from \"@f-o-t/pdf/plugins/editing\";\nimport { drawSignatureAppearance } from \"./appearance.ts\";\nimport {\n buildSignaturePolicy,\n buildSigningCertificateV2,\n ICP_BRASIL_OIDS,\n} from \"./icp-brasil.ts\";\nimport { pdfSignOptionsSchema } from \"./schemas.ts\";\nimport { requestTimestamp } from \"./timestamp.ts\";\nimport type { PdfSignOptions } from \"./types.ts\";\n\n/**\n * Sign a PDF document with a digital certificate.\n *\n * Supports PAdES-BES and PAdES with ICP-Brasil compliance\n * (signing-certificate-v2 and signature-policy attributes).\n *\n * @param pdf - The PDF document as a Uint8Array\n * @param options - Signing options\n * @returns The signed PDF as a Uint8Array\n *\n * @example\n * ```ts\n * const signedPdf = await signPdf(pdfBytes, {\n * certificate: { p12, password: \"secret\" },\n * reason: \"Document approval\",\n * location: \"Corporate Office\",\n * policy: \"pades-icp-brasil\",\n * });\n * ```\n */\nexport async function signPdf(\n pdf: Uint8Array,\n options: PdfSignOptions,\n): Promise<Uint8Array> {\n // Validate input\n const opts = pdfSignOptionsSchema.parse(options);\n\n // 1. Parse certificate for display info (uses OpenSSL, needs Buffer)\n let certInfo: CertificateInfo | null = null;\n try {\n certInfo = parseCertificate(\n Buffer.from(opts.certificate.p12),\n opts.certificate.password,\n );\n } catch {\n // If parsing fails, continue without cert info for display\n }\n\n // 2. Load PDF via editing plugin\n const doc = loadPdf(pdf);\n\n // 3. Draw visual signature appearance if requested\n if (opts.appearance !== false && opts.appearance) {\n const pageIndex = opts.appearance.page ?? 0;\n\n if (pageIndex < 0 || pageIndex >= doc.pageCount) {\n throw new PdfSignError(\n `Invalid page index: ${pageIndex}. PDF has ${doc.pageCount} pages.`,\n );\n }\n\n const page = doc.getPage(pageIndex);\n\n drawSignatureAppearance(doc, page, opts.appearance, certInfo, {\n reason: opts.reason,\n location: opts.location,\n qrCode: opts.qrCode,\n pdfData: pdf,\n });\n }\n\n // 3b. Draw multiple visual signature appearances if provided\n if (opts.appearances && opts.appearances.length > 0) {\n for (const app of opts.appearances) {\n const pageIndex = app.page ?? 0;\n\n if (pageIndex < 0 || pageIndex >= doc.pageCount) {\n throw new PdfSignError(\n `Invalid page index ${pageIndex} in appearances. PDF has ${doc.pageCount} pages.`,\n );\n }\n\n const page = doc.getPage(pageIndex);\n\n drawSignatureAppearance(doc, page, app, certInfo, {\n reason: opts.reason,\n location: opts.location,\n qrCode: opts.qrCode,\n pdfData: pdf,\n });\n }\n }\n\n // 4. Save with signature placeholder\n const signerName =\n certInfo?.subject.commonName ||\n opts.certificate.name ||\n \"Digital Signature\";\n\n const { pdf: pdfWithPlaceholder } = doc.saveWithPlaceholder({\n reason: opts.reason || \"Digitally signed\",\n name: signerName,\n location: opts.location,\n contactInfo: opts.contactInfo,\n signatureLength: 16384,\n docMdpPermission: opts.docMdpPermission ?? 2,\n });\n\n // 5. Find byte range and extract bytes to sign\n const { byteRange } = findByteRange(pdfWithPlaceholder);\n const bytesToSign = extractBytesToSign(pdfWithPlaceholder, byteRange);\n\n // 6. Parse PKCS#12 for cryptographic material\n const { certificate, privateKey, chain } = parsePkcs12(\n opts.certificate.p12,\n opts.certificate.password,\n );\n\n // 7. Build ICP-Brasil authenticated attributes if needed\n const authenticatedAttributes: CmsAttribute[] = [];\n\n if (opts.policy === \"pades-icp-brasil\") {\n // signing-certificate-v2\n const sigCertV2 = buildSigningCertificateV2(certificate);\n authenticatedAttributes.push({\n oid: ICP_BRASIL_OIDS.signingCertificateV2,\n values: [sigCertV2],\n });\n\n // signature-policy\n try {\n const sigPolicy = await buildSignaturePolicy();\n authenticatedAttributes.push({\n oid: ICP_BRASIL_OIDS.signaturePolicy,\n values: [sigPolicy],\n });\n } catch {\n // Policy download failure is non-fatal\n // The signature will still be valid PAdES-BES\n }\n }\n\n // 8. Build unauthenticated attributes\n const unauthenticatedAttributes: CmsAttribute[] = [];\n\n // 9. Create CMS/PKCS#7 SignedData\n const signedData = createSignedData({\n content: bytesToSign,\n certificate,\n privateKey,\n chain,\n hashAlgorithm: \"sha256\",\n authenticatedAttributes:\n authenticatedAttributes.length > 0\n ? authenticatedAttributes\n : undefined,\n unauthenticatedAttributes:\n unauthenticatedAttributes.length > 0\n ? unauthenticatedAttributes\n : undefined,\n detached: true,\n });\n\n // 10. Optionally request timestamp\n if (opts.timestamp && opts.tsaUrl) {\n try {\n const tsToken = await requestTimestamp(signedData, opts.tsaUrl);\n // Note: Adding the timestamp as an unauthenticated attribute\n // would require re-building the CMS structure. For now, we log\n // that timestamp was received. A future version will embed it.\n // TODO: Rebuild CMS with timestamp token as unauthenticated attribute\n void tsToken;\n } catch {\n // Timestamp failure is non-fatal\n }\n }\n\n // 11. Embed signature into PDF\n return embedSignature(pdfWithPlaceholder, signedData);\n}\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class PdfSignError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PdfSignError\";\n }\n}\n",
8
+ "/**\n * Visual Signature Appearance\n *\n * Draws certificate information and QR code on the PDF page\n * for visible digital signatures.\n */\n\nimport { hash } from \"@f-o-t/crypto\";\nimport type { CertificateInfo } from \"@f-o-t/digital-certificate\";\nimport type { PdfDocument, PdfPage } from \"@f-o-t/pdf/plugins/editing\";\nimport { generateQrCode } from \"@f-o-t/qrcode\";\nimport type { QrCodeConfig, SignatureAppearance } from \"./types.ts\";\n\n/**\n * Draw the visual signature appearance on a PDF page.\n *\n * Includes optional QR code and certificate information text.\n */\nexport function drawSignatureAppearance(\n doc: PdfDocument,\n page: PdfPage,\n appearance: SignatureAppearance,\n certInfo: CertificateInfo | null,\n options: {\n reason?: string;\n location?: string;\n qrCode?: QrCodeConfig;\n pdfData: Uint8Array;\n },\n): void {\n const { x, width, height } = appearance;\n const showQrCode = appearance.showQrCode !== false;\n const showCertInfo = appearance.showCertInfo !== false;\n\n // Convert from top-left origin (user-facing) to PDF bottom-left origin.\n // Users specify y as distance from the top of the page, but PDF coordinates\n // have y=0 at the bottom.\n const y = page.height - appearance.y - height;\n\n let qrSize = 0;\n\n // Draw QR code if requested (enabled by default)\n if (showQrCode) {\n const qrData =\n options.qrCode?.data ||\n createVerificationData(certInfo, options.pdfData);\n\n const qrPng = generateQrCode(qrData, {\n size: options.qrCode?.size || 100,\n });\n\n const qrImage = doc.embedPng(qrPng);\n qrSize = Math.min(100, height - 20);\n\n page.drawImage(qrImage, {\n x: x + 10,\n y: y + 10,\n width: qrSize,\n height: qrSize,\n });\n }\n\n // Draw certificate info text\n if (showCertInfo) {\n drawCertInfo(page, certInfo, {\n x,\n y,\n width,\n height,\n qrOffset: qrSize > 0 ? qrSize + 20 : 10,\n reason: options.reason,\n location: options.location,\n });\n }\n}\n\n/**\n * Draw certificate information text on the page\n */\nfunction drawCertInfo(\n page: PdfPage,\n certInfo: CertificateInfo | null,\n opts: {\n x: number;\n y: number;\n width: number;\n height: number;\n qrOffset: number;\n reason?: string;\n location?: string;\n },\n): void {\n const textX = opts.x + opts.qrOffset;\n let textY = opts.y + opts.height - 20;\n const fontSize = 10;\n const lineHeight = 14;\n\n // Header\n page.drawText(\"ASSINADO DIGITALMENTE\", {\n x: textX,\n y: textY,\n size: 12,\n });\n textY -= lineHeight * 1.5;\n\n if (certInfo) {\n // Signer name\n const signerName = certInfo.subject.commonName || \"N/A\";\n page.drawText(`Assinado por: ${signerName}`, {\n x: textX,\n y: textY,\n size: fontSize,\n });\n textY -= lineHeight;\n\n // CNPJ or CPF\n if (certInfo.brazilian.cnpj) {\n const cnpj = formatCnpj(certInfo.brazilian.cnpj);\n page.drawText(`CNPJ: ${cnpj}`, {\n x: textX,\n y: textY,\n size: fontSize,\n });\n textY -= lineHeight;\n } else if (certInfo.brazilian.cpf) {\n const cpf = formatCpf(certInfo.brazilian.cpf);\n page.drawText(`CPF: ${cpf}`, {\n x: textX,\n y: textY,\n size: fontSize,\n });\n textY -= lineHeight;\n }\n\n // Date and time\n const now = new Date();\n const dateStr = now.toLocaleDateString(\"pt-BR\");\n const timeStr = now.toLocaleTimeString(\"pt-BR\");\n page.drawText(`Data: ${dateStr} ${timeStr}`, {\n x: textX,\n y: textY,\n size: fontSize,\n });\n textY -= lineHeight;\n\n // Location\n if (opts.location) {\n page.drawText(`Local: ${opts.location}`, {\n x: textX,\n y: textY,\n size: fontSize - 1,\n });\n }\n } else {\n // Fallback if cert info not available\n page.drawText(`Signed: ${opts.reason || \"Digital Signature\"}`, {\n x: textX,\n y: textY,\n size: fontSize,\n });\n textY -= lineHeight;\n\n if (opts.location) {\n page.drawText(`Location: ${opts.location}`, {\n x: textX,\n y: textY,\n size: fontSize,\n });\n }\n }\n}\n\n/**\n * Generate verification data for the QR code\n */\nfunction createVerificationData(\n certInfo: CertificateInfo | null,\n pdfData: Uint8Array,\n): string {\n const documentHash = toHex(hash(\"sha256\", pdfData));\n const timestamp = new Date().toISOString();\n\n if (certInfo) {\n const certFingerprint = certInfo.fingerprint;\n return (\n `https://validar.iti.gov.br/?` +\n `doc=${documentHash.substring(0, 16)}&` +\n `cert=${certFingerprint.substring(0, 16)}&` +\n `time=${encodeURIComponent(timestamp)}`\n );\n }\n\n return `https://validar.iti.gov.br/?doc=${documentHash.substring(0, 16)}&time=${encodeURIComponent(timestamp)}`;\n}\n\n/**\n * Format a CNPJ number with punctuation\n */\nfunction formatCnpj(cnpj: string): string {\n return cnpj.replace(/(\\d{2})(\\d{3})(\\d{3})(\\d{4})(\\d{2})/, \"$1.$2.$3/$4-$5\");\n}\n\n/**\n * Format a CPF number with punctuation\n */\nfunction formatCpf(cpf: string): string {\n return cpf.replace(/(\\d{3})(\\d{3})(\\d{3})(\\d{2})/, \"$1.$2.$3-$4\");\n}\n\n/**\n * Convert bytes to hex string\n */\nfunction toHex(data: Uint8Array): string {\n const chars: string[] = [];\n for (let i = 0; i < data.length; i++) {\n chars.push(data[i]!.toString(16).padStart(2, \"0\"));\n }\n return chars.join(\"\");\n}\n",
9
+ "/**\n * RFC 3161 Timestamp Client\n *\n * Requests trusted timestamps from TSA servers using native fetch.\n * Builds the TimeStampReq using @f-o-t/asn1 instead of forge.\n */\n\nimport {\n type Asn1Node,\n boolean as asn1Boolean,\n decodeDer,\n encodeDer,\n integer,\n octetString,\n oid,\n sequence,\n} from \"@f-o-t/asn1\";\nimport { hash } from \"@f-o-t/crypto\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Well-known hash algorithm OIDs */\nconst HASH_OIDS: Record<string, string> = {\n sha256: \"2.16.840.1.101.3.4.2.1\",\n sha384: \"2.16.840.1.101.3.4.2.2\",\n sha512: \"2.16.840.1.101.3.4.2.3\",\n};\n\n/** ICP-Brasil Approved Timestamp Servers */\nexport const TIMESTAMP_SERVERS = {\n VALID: \"http://timestamp.valid.com.br/tsa\",\n SAFEWEB: \"http://tsa.safeweb.com.br/tsa/tsa\",\n CERTISIGN: \"http://timestamp.certisign.com.br\",\n} as const;\n\n/** id-smime-aa-timeStampToken OID */\nexport const TIMESTAMP_TOKEN_OID = \"1.2.840.113549.1.9.16.2.14\";\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Request a timestamp from a TSA server.\n *\n * @param dataToTimestamp - The data to timestamp (usually the signature value)\n * @param tsaUrl - URL of the timestamp server\n * @param hashAlgorithm - Hash algorithm to use (default: \"sha256\")\n * @returns DER-encoded TimeStampToken\n */\nexport async function requestTimestamp(\n dataToTimestamp: Uint8Array,\n tsaUrl: string,\n hashAlgorithm: \"sha256\" | \"sha384\" | \"sha512\" = \"sha256\",\n): Promise<Uint8Array> {\n // 1. Hash the data\n const messageHash = hash(hashAlgorithm, dataToTimestamp);\n\n // 2. Build TimeStampReq\n const timestampReq = buildTimestampRequest(messageHash, hashAlgorithm);\n\n // 3. Send to TSA\n const response = await fetch(tsaUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/timestamp-query\",\n },\n body: timestampReq as unknown as BodyInit,\n signal: AbortSignal.timeout(10000),\n });\n\n if (!response.ok) {\n throw new TimestampError(`TSA returned HTTP ${response.status}`);\n }\n\n const respBuffer = new Uint8Array(await response.arrayBuffer());\n\n // 4. Validate and extract token\n return extractTimestampToken(respBuffer);\n}\n\n// ---------------------------------------------------------------------------\n// Internal\n// ---------------------------------------------------------------------------\n\n/**\n * Build a TimeStampReq (RFC 3161) as DER bytes.\n *\n * TimeStampReq ::= SEQUENCE {\n * version INTEGER { v1(1) },\n * messageImprint MessageImprint,\n * certReq BOOLEAN DEFAULT FALSE\n * }\n *\n * MessageImprint ::= SEQUENCE {\n * hashAlgorithm AlgorithmIdentifier,\n * hashedMessage OCTET STRING\n * }\n */\nfunction buildTimestampRequest(\n messageHash: Uint8Array,\n hashAlgorithm: string,\n): Uint8Array {\n const hashOid = HASH_OIDS[hashAlgorithm];\n if (!hashOid) {\n throw new TimestampError(`Unsupported hash algorithm: ${hashAlgorithm}`);\n }\n\n const timestampReq = sequence(\n // version = 1\n integer(1),\n // messageImprint\n sequence(\n // hashAlgorithm AlgorithmIdentifier\n sequence(oid(hashOid)),\n // hashedMessage OCTET STRING\n octetString(messageHash),\n ),\n // certReq = TRUE (request certificate in response)\n asn1Boolean(true),\n );\n\n return encodeDer(timestampReq);\n}\n\n/**\n * Extract the TimeStampToken from a TimeStampResp.\n *\n * TimeStampResp ::= SEQUENCE {\n * status PKIStatusInfo,\n * timeStampToken TimeStampToken OPTIONAL\n * }\n *\n * PKIStatusInfo ::= SEQUENCE {\n * status PKIStatus, -- INTEGER\n * ...\n * }\n */\nfunction extractTimestampToken(respDer: Uint8Array): Uint8Array {\n let resp: Asn1Node;\n try {\n resp = decodeDer(respDer);\n } catch {\n throw new TimestampError(\"Invalid timestamp response: not valid DER\");\n }\n\n const children = resp.value as Asn1Node[];\n if (!Array.isArray(children) || children.length < 1) {\n throw new TimestampError(\n \"Invalid timestamp response: unexpected structure\",\n );\n }\n\n // Check status\n const statusInfo = children[0]!.value as Asn1Node[];\n const statusBytes = statusInfo[0]!.value as Uint8Array;\n // Status 0 = granted, 1 = grantedWithMods\n const statusValue = statusBytes[statusBytes.length - 1]!;\n if (statusValue !== 0 && statusValue !== 1) {\n throw new TimestampError(\n `Timestamp request rejected with status: ${statusValue}`,\n );\n }\n\n // Extract token (second child)\n if (!children[1]) {\n throw new TimestampError(\"Timestamp response does not contain a token\");\n }\n\n return encodeDer(children[1]);\n}\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class TimestampError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TimestampError\";\n }\n}\n"
10
10
  ],
11
- "mappings": "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",
12
- "debugId": "F1FD1A009917885C64756E2164756E21",
11
+ "mappings": "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",
12
+ "debugId": "847A450B084A9AAE64756E2164756E21",
13
13
  "names": []
14
14
  }
package/dist/schemas.d.ts CHANGED
@@ -26,6 +26,15 @@ export declare const pdfSignOptionsSchema: z.ZodObject<{
26
26
  showQrCode: z.ZodOptional<z.ZodBoolean>;
27
27
  showCertInfo: z.ZodOptional<z.ZodBoolean>;
28
28
  }, z.core.$strip>, z.ZodLiteral<false>]>>;
29
+ appearances: z.ZodOptional<z.ZodArray<z.ZodObject<{
30
+ x: z.ZodNumber;
31
+ y: z.ZodNumber;
32
+ width: z.ZodNumber;
33
+ height: z.ZodNumber;
34
+ page: z.ZodOptional<z.ZodNumber>;
35
+ showQrCode: z.ZodOptional<z.ZodBoolean>;
36
+ showCertInfo: z.ZodOptional<z.ZodBoolean>;
37
+ }, z.core.$strip>>>;
29
38
  qrCode: z.ZodOptional<z.ZodObject<{
30
39
  data: z.ZodOptional<z.ZodString>;
31
40
  size: z.ZodOptional<z.ZodNumber>;
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiBxB,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmB/B,CAAC"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiBxB,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsB/B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sign-pdf.d.ts","sourceRoot":"","sources":["../src/sign-pdf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,OAAO,CAC5B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC,UAAU,CAAC,CA4HrB;AAMD,qBAAa,YAAa,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI3B"}
1
+ {"version":3,"file":"sign-pdf.d.ts","sourceRoot":"","sources":["../src/sign-pdf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,OAAO,CAC1B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,cAAc,GACvB,OAAO,CAAC,UAAU,CAAC,CAkJrB;AAMD,qBAAa,YAAa,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"timestamp.d.ts","sourceRoot":"","sources":["../src/timestamp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyBH,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB;;;;CAIpB,CAAC;AAEX,qCAAqC;AACrC,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAMhE;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACrC,eAAe,EAAE,UAAU,EAC3B,MAAM,EAAE,MAAM,EACd,aAAa,GAAE,QAAQ,GAAG,QAAQ,GAAG,QAAmB,GACtD,OAAO,CAAC,UAAU,CAAC,CAyBrB;AAmGD,qBAAa,cAAe,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI3B"}
1
+ {"version":3,"file":"timestamp.d.ts","sourceRoot":"","sources":["../src/timestamp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyBH,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB;;;;CAIpB,CAAC;AAEX,qCAAqC;AACrC,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAMhE;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACnC,eAAe,EAAE,UAAU,EAC3B,MAAM,EAAE,MAAM,EACd,aAAa,GAAE,QAAQ,GAAG,QAAQ,GAAG,QAAmB,GACxD,OAAO,CAAC,UAAU,CAAC,CAyBrB;AAiGD,qBAAa,cAAe,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI7B"}
package/dist/types.d.ts CHANGED
@@ -58,6 +58,8 @@ export type PdfSignOptions = {
58
58
  tsaUrl?: string;
59
59
  /** Visual signature appearance (false to disable) */
60
60
  appearance?: SignatureAppearance | false;
61
+ /** Multiple visual signature appearances — renders a stamp on each specified page */
62
+ appearances?: SignatureAppearance[];
61
63
  /** QR code configuration for the visual signature */
62
64
  qrCode?: QrCodeConfig;
63
65
  /** DocMDP permission level: 1 = no changes, 2 = form fill + sign (default), 3 = form fill + sign + annotate */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IACjC,yCAAyC;IACzC,CAAC,EAAE,MAAM,CAAC;IACV,2CAA2C;IAC3C,CAAC,EAAE,MAAM,CAAC;IACV,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IAC1B,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,8BAA8B;IAC9B,WAAW,EAAE;QACZ,gCAAgC;QAChC,GAAG,EAAE,UAAU,CAAC;QAChB,oCAAoC;QACpC,QAAQ,EAAE,MAAM,CAAC;QACjB,2CAA2C;QAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,YAAY,GAAG,kBAAkB,CAAC;IAC3C,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,UAAU,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;IACzC,qDAAqD;IACrD,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,+GAA+G;IAC/G,gBAAgB,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC7B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC/B,yCAAyC;IACzC,CAAC,EAAE,MAAM,CAAC;IACV,2CAA2C;IAC3C,CAAC,EAAE,MAAM,CAAC;IACV,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACxB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC1B,8BAA8B;IAC9B,WAAW,EAAE;QACV,gCAAgC;QAChC,GAAG,EAAE,UAAU,CAAC;QAChB,oCAAoC;QACpC,QAAQ,EAAE,MAAM,CAAC;QACjB,2CAA2C;QAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,YAAY,GAAG,kBAAkB,CAAC;IAC3C,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,UAAU,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;IACzC,qFAAqF;IACrF,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACpC,qDAAqD;IACrD,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,+GAA+G;IAC/G,gBAAgB,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC/B,CAAC"}
package/package.json CHANGED
@@ -1,42 +1,42 @@
1
1
  {
2
- "name": "@f-o-t/e-signature",
3
- "version": "1.0.6",
4
- "description": "PAdES PDF signing with ICP-Brasil compliance",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "default": "./dist/index.js"
12
- }
13
- },
14
- "files": [
15
- "dist"
16
- ],
17
- "scripts": {
18
- "build": "bun x --bun fot build",
19
- "test": "bun x --bun fot test",
20
- "lint": "bun x --bun fot lint",
21
- "format": "bun x --bun fot format",
22
- "check": "bun x --bun fot check"
23
- },
24
- "dependencies": {
25
- "@f-o-t/asn1": "^1.0.0",
26
- "@f-o-t/crypto": "^1.0.0",
27
- "@f-o-t/digital-certificate": "^2.2.0",
28
- "@f-o-t/pdf": "^0.3.4",
29
- "@f-o-t/qrcode": "^1.0.0",
30
- "zod": "^4.3.6"
31
- },
32
- "devDependencies": {
33
- "@f-o-t/cli": "^1.0.1",
34
- "@f-o-t/config": "^1.0.3",
35
- "@types/bun": "latest"
36
- },
37
- "repository": {
38
- "type": "git",
39
- "url": "https://github.com/F-O-T/libraries.git",
40
- "directory": "libraries/e-signature"
41
- }
2
+ "name": "@f-o-t/e-signature",
3
+ "version": "1.1.0",
4
+ "description": "PAdES PDF signing with ICP-Brasil compliance",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "bun x --bun fot build",
19
+ "test": "bun x --bun fot test",
20
+ "lint": "bun x --bun fot lint",
21
+ "format": "bun x --bun fot format",
22
+ "check": "bun x --bun fot check"
23
+ },
24
+ "dependencies": {
25
+ "@f-o-t/asn1": "^1.0.0",
26
+ "@f-o-t/crypto": "^1.0.0",
27
+ "@f-o-t/digital-certificate": "^2.2.0",
28
+ "@f-o-t/pdf": "^0.3.4",
29
+ "@f-o-t/qrcode": "^1.0.0",
30
+ "zod": "^4.3.6"
31
+ },
32
+ "devDependencies": {
33
+ "@f-o-t/cli": "^1.0.1",
34
+ "@f-o-t/config": "^1.0.3",
35
+ "@types/bun": "latest"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/F-O-T/libraries.git",
40
+ "directory": "libraries/e-signature"
41
+ }
42
42
  }