@awacloud/pdf 0.0.0-stage → 1.0.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/CHANGELOG.md +609 -0
- package/LICENSE +661 -0
- package/NOTICE +77 -0
- package/README.md +363 -2
- package/dist/build/index.js +21 -0
- package/dist/build/pdf-full-rw.js +10972 -0
- package/dist/build/pdf-full-rw.meta.json +105 -0
- package/dist/build/pdf-full-rw.min.js +53 -0
- package/dist/build/pdf-full.js +6078 -0
- package/dist/build/pdf-full.meta.json +90 -0
- package/dist/build/pdf-full.min.js +32 -0
- package/dist/build/pdf-large-rw.js +10367 -0
- package/dist/build/pdf-large-rw.meta.json +99 -0
- package/dist/build/pdf-large-rw.min.js +53 -0
- package/dist/build/pdf-large.js +5473 -0
- package/dist/build/pdf-large.meta.json +84 -0
- package/dist/build/pdf-large.min.js +32 -0
- package/dist/build/pdf-legacy-rw.js +12402 -0
- package/dist/build/pdf-legacy-rw.meta.json +110 -0
- package/dist/build/pdf-legacy-rw.min.js +53 -0
- package/dist/build/pdf-legacy.js +7508 -0
- package/dist/build/pdf-legacy.meta.json +95 -0
- package/dist/build/pdf-legacy.min.js +32 -0
- package/dist/build/pdf-rw.js +7578 -0
- package/dist/build/pdf-rw.meta.json +77 -0
- package/dist/build/pdf-rw.min.js +53 -0
- package/dist/build/pdf.js +2684 -0
- package/dist/build/pdf.meta.json +62 -0
- package/dist/build/pdf.min.js +32 -0
- package/dist/standalone/pdf-full-rw.js +16798 -0
- package/dist/standalone/pdf-full-rw.meta.json +78 -0
- package/dist/standalone/pdf-full-rw.min.js +56 -0
- package/dist/standalone/pdf-full.js +11904 -0
- package/dist/standalone/pdf-full.meta.json +63 -0
- package/dist/standalone/pdf-full.min.js +35 -0
- package/dist/standalone/pdf-large-rw.js +16193 -0
- package/dist/standalone/pdf-large-rw.meta.json +72 -0
- package/dist/standalone/pdf-large-rw.min.js +56 -0
- package/dist/standalone/pdf-large.js +11299 -0
- package/dist/standalone/pdf-large.meta.json +57 -0
- package/dist/standalone/pdf-large.min.js +35 -0
- package/dist/standalone/pdf-legacy-rw.js +18228 -0
- package/dist/standalone/pdf-legacy-rw.meta.json +83 -0
- package/dist/standalone/pdf-legacy-rw.min.js +56 -0
- package/dist/standalone/pdf-legacy.js +13334 -0
- package/dist/standalone/pdf-legacy.meta.json +68 -0
- package/dist/standalone/pdf-legacy.min.js +35 -0
- package/dist/standalone/pdf-rw.js +13404 -0
- package/dist/standalone/pdf-rw.meta.json +50 -0
- package/dist/standalone/pdf-rw.min.js +56 -0
- package/dist/standalone/pdf.js +8510 -0
- package/dist/standalone/pdf.meta.json +35 -0
- package/dist/standalone/pdf.min.js +35 -0
- package/docs/README.md +53 -0
- package/docs/api/README.md +38 -0
- package/docs/api/_shared/README.md +91 -0
- package/docs/api/action/README.md +29 -0
- package/docs/api/action/action.md +81 -0
- package/docs/api/action/goTo.md +66 -0
- package/docs/api/action/launch.md +58 -0
- package/docs/api/action/named.md +55 -0
- package/docs/api/action/uri.md +54 -0
- package/docs/api/annot/README.md +53 -0
- package/docs/api/annot/annot.md +114 -0
- package/docs/api/annot/fileAttach.md +53 -0
- package/docs/api/annot/freeText.md +68 -0
- package/docs/api/annot/ink.md +69 -0
- package/docs/api/annot/link.md +74 -0
- package/docs/api/annot/markup.md +83 -0
- package/docs/api/annot/popup.md +52 -0
- package/docs/api/annot/projection.md +56 -0
- package/docs/api/annot/redact.md +67 -0
- package/docs/api/annot/square.md +87 -0
- package/docs/api/annot/stamp.md +54 -0
- package/docs/api/annot/text.md +69 -0
- package/docs/api/annot/widget.md +69 -0
- package/docs/api/associatedFiles/README.md +9 -0
- package/docs/api/associatedFiles/associatedFiles.md +78 -0
- package/docs/api/bundles/README.md +68 -0
- package/docs/api/bundles/dist-matrix.md +165 -0
- package/docs/api/bundles/pdf-full.md +148 -0
- package/docs/api/bundles/pdf-large.md +144 -0
- package/docs/api/bundles/pdf-legacy.md +169 -0
- package/docs/api/content/README.md +29 -0
- package/docs/api/content/color.md +99 -0
- package/docs/api/content/graphics.md +114 -0
- package/docs/api/content/images.md +124 -0
- package/docs/api/content/ops.md +100 -0
- package/docs/api/content/stream.md +107 -0
- package/docs/api/content/text.md +98 -0
- package/docs/api/crypto/README.md +29 -0
- package/docs/api/crypto/aesGcm.md +72 -0
- package/docs/api/crypto/permissions.md +79 -0
- package/docs/api/crypto/security.md +98 -0
- package/docs/api/crypto/standardV4.md +104 -0
- package/docs/api/crypto/standardV5.md +84 -0
- package/docs/api/crypto/standardV6.md +93 -0
- package/docs/api/destination/README.md +9 -0
- package/docs/api/destination/destination.md +79 -0
- package/docs/api/document/README.md +29 -0
- package/docs/api/document/builder.md +281 -0
- package/docs/api/document/catalog.md +98 -0
- package/docs/api/document/document.md +187 -0
- package/docs/api/document/encryptedWriter.md +149 -0
- package/docs/api/document/incrementalWriter.md +148 -0
- package/docs/api/document/page.md +99 -0
- package/docs/api/document/pages.md +82 -0
- package/docs/api/document/resources.md +102 -0
- package/docs/api/document/writer.md +157 -0
- package/docs/api/document/xrefStreamWriter.md +122 -0
- package/docs/api/embedded/README.md +13 -0
- package/docs/api/embedded/collection.md +80 -0
- package/docs/api/embedded/embeddedFile.md +86 -0
- package/docs/api/embedded/fileSpec.md +87 -0
- package/docs/api/errors.md +110 -0
- package/docs/api/extra/3d-richmedia.md +76 -0
- package/docs/api/extra/README.md +99 -0
- package/docs/api/extra/annot-extended.md +71 -0
- package/docs/api/extra/associated-files.md +70 -0
- package/docs/api/extra/ccitt-fax-decoder.md +74 -0
- package/docs/api/extra/color-spaces-extended.md +72 -0
- package/docs/api/extra/content-ops-extended.md +82 -0
- package/docs/api/extra/document-parts.md +69 -0
- package/docs/api/extra/embedded-files-portfolio.md +87 -0
- package/docs/api/extra/font-cid-typed.md +77 -0
- package/docs/api/extra/font-color-tagging.md +76 -0
- package/docs/api/extra/form-actions-extended.md +75 -0
- package/docs/api/extra/info-dict-deprecated.md +72 -0
- package/docs/api/extra/jbig2-read.md +80 -0
- package/docs/api/extra/legacy-deprecated-annots.md +89 -0
- package/docs/api/extra/legacy-deprecated-filters.md +78 -0
- package/docs/api/extra/legacy-rc4-read.md +74 -0
- package/docs/api/extra/legacy-xfa-read.md +65 -0
- package/docs/api/extra/linearization-write.md +71 -0
- package/docs/api/extra/misc.md +93 -0
- package/docs/api/extra/optional-content-extended.md +83 -0
- package/docs/api/extra/pdf-a-output-intent.md +65 -0
- package/docs/api/extra/pdf-sandbox.md +76 -0
- package/docs/api/extra/pdf-ua-tagged.md +63 -0
- package/docs/api/extra/pdf-x-prepress.md +65 -0
- package/docs/api/extra/redaction-iso32005.md +65 -0
- package/docs/api/extra/shading-typed.md +73 -0
- package/docs/api/extra/sig-aes-gcm.md +69 -0
- package/docs/api/extra/sig-pades.md +103 -0
- package/docs/api/extra/tagged-pdf-typed.md +78 -0
- package/docs/api/extra/transparency-typed.md +74 -0
- package/docs/api/extra/well-tagged-pdf.md +61 -0
- package/docs/api/extra/xmp-extended.md +65 -0
- package/docs/api/font/README.md +25 -0
- package/docs/api/font/embed.md +157 -0
- package/docs/api/font/encoding.md +95 -0
- package/docs/api/font/font.md +97 -0
- package/docs/api/font/type3.md +89 -0
- package/docs/api/form/README.md +35 -0
- package/docs/api/form/acroform.md +88 -0
- package/docs/api/form/appearance.md +87 -0
- package/docs/api/form/button.md +97 -0
- package/docs/api/form/choice.md +96 -0
- package/docs/api/form/fieldTree.md +93 -0
- package/docs/api/form/signature.md +90 -0
- package/docs/api/form/text.md +88 -0
- package/docs/api/linearization/README.md +11 -0
- package/docs/api/linearization/linearization.md +81 -0
- package/docs/api/main.md +116 -0
- package/docs/api/metadata/README.md +10 -0
- package/docs/api/metadata/info.md +70 -0
- package/docs/api/metadata/xmp.md +62 -0
- package/docs/api/ocg/README.md +23 -0
- package/docs/api/ocg/config.md +95 -0
- package/docs/api/ocg/ocg.md +77 -0
- package/docs/api/outline/README.md +11 -0
- package/docs/api/outline/outline.md +107 -0
- package/docs/api/pdf.md +152 -0
- package/docs/api/prepress/README.md +10 -0
- package/docs/api/prepress/outputIntent.md +79 -0
- package/docs/api/prepress/pageBoundary.md +75 -0
- package/docs/api/sig/README.md +32 -0
- package/docs/api/sig/byteRange.md +120 -0
- package/docs/api/sig/certChain.md +84 -0
- package/docs/api/sig/dss.md +111 -0
- package/docs/api/sig/oids.md +76 -0
- package/docs/api/sig/sha1.md +72 -0
- package/docs/api/sig/sign.md +317 -0
- package/docs/api/sig/signature.md +178 -0
- package/docs/api/sig/timestamp.md +84 -0
- package/docs/api/syntax/README.md +29 -0
- package/docs/api/syntax/crossRefStream.md +115 -0
- package/docs/api/syntax/filters/README.md +50 -0
- package/docs/api/syntax/filters/ascii85.md +76 -0
- package/docs/api/syntax/filters/asciiHex.md +73 -0
- package/docs/api/syntax/filters/dispatch.md +125 -0
- package/docs/api/syntax/filters/flate.md +134 -0
- package/docs/api/syntax/filters/runLength.md +78 -0
- package/docs/api/syntax/objStream.md +88 -0
- package/docs/api/syntax/parser-obj.md +97 -0
- package/docs/api/syntax/parser.md +151 -0
- package/docs/api/syntax/serializer.md +109 -0
- package/docs/api/syntax/tokenizer.md +104 -0
- package/docs/api/syntax/trailer.md +85 -0
- package/docs/api/syntax/xref.md +139 -0
- package/docs/api/tagged/README.md +25 -0
- package/docs/api/tagged/classMap.md +67 -0
- package/docs/api/tagged/markedContent.md +62 -0
- package/docs/api/tagged/parentTree.md +67 -0
- package/docs/api/tagged/roleMap.md +67 -0
- package/docs/api/tagged/structElement.md +76 -0
- package/docs/api/tagged/structTree.md +75 -0
- package/docs/guide/coverage.md +113 -0
- package/docs/guide/crypto.md +121 -0
- package/docs/guide/extending.md +76 -0
- package/docs/guide/getting-started.md +75 -0
- package/docs/guide/legacy-1.7.md +42 -0
- package/docs/guide/pades-integration.md +579 -0
- package/docs/guide/read-pdf.md +89 -0
- package/package.json +97 -4
- package/src/_shared/index.js +179 -0
- package/src/action/action.js +119 -0
- package/src/action/goTo.js +89 -0
- package/src/action/launch.js +61 -0
- package/src/action/named.js +54 -0
- package/src/action/uri.js +51 -0
- package/src/annot/annot.js +212 -0
- package/src/annot/fileAttach.js +55 -0
- package/src/annot/freeText.js +82 -0
- package/src/annot/ink.js +77 -0
- package/src/annot/link.js +77 -0
- package/src/annot/markup.js +91 -0
- package/src/annot/popup.js +53 -0
- package/src/annot/projection.js +52 -0
- package/src/annot/redact.js +87 -0
- package/src/annot/square.js +132 -0
- package/src/annot/stamp.js +48 -0
- package/src/annot/text.js +54 -0
- package/src/annot/widget.js +61 -0
- package/src/associatedFiles/associatedFiles.js +86 -0
- package/src/bundles/pdf-full.js +91 -0
- package/src/bundles/pdf-large.js +81 -0
- package/src/bundles/pdf-legacy.js +107 -0
- package/src/content/color.js +114 -0
- package/src/content/graphics.js +192 -0
- package/src/content/images.js +160 -0
- package/src/content/ops.js +137 -0
- package/src/content/stream.js +154 -0
- package/src/content/text.js +125 -0
- package/src/crypto/aesGcm.js +123 -0
- package/src/crypto/permissions.js +112 -0
- package/src/crypto/security.js +327 -0
- package/src/crypto/standardV4.js +443 -0
- package/src/crypto/standardV5.js +306 -0
- package/src/crypto/standardV6.js +334 -0
- package/src/destination/destination.js +183 -0
- package/src/document/builder.js +618 -0
- package/src/document/catalog.js +100 -0
- package/src/document/document.js +472 -0
- package/src/document/encryptedWriter.js +554 -0
- package/src/document/incrementalWriter.js +514 -0
- package/src/document/page.js +131 -0
- package/src/document/pages.js +103 -0
- package/src/document/resources.js +146 -0
- package/src/document/writer.js +211 -0
- package/src/document/xrefStreamWriter.js +353 -0
- package/src/embedded/collection.js +102 -0
- package/src/embedded/embeddedFile.js +99 -0
- package/src/embedded/fileSpec.js +137 -0
- package/src/errors.js +78 -0
- package/src/extra/3d-richmedia.js +171 -0
- package/src/extra/annot-extended.js +200 -0
- package/src/extra/associated-files.js +131 -0
- package/src/extra/ccitt-fax-decoder.js +776 -0
- package/src/extra/color-spaces-extended.js +196 -0
- package/src/extra/content-ops-extended.js +153 -0
- package/src/extra/document-parts.js +149 -0
- package/src/extra/embedded-files-portfolio.js +234 -0
- package/src/extra/font-cid-typed.js +185 -0
- package/src/extra/font-color-tagging.js +144 -0
- package/src/extra/form-actions-extended.js +196 -0
- package/src/extra/info-dict-deprecated.js +137 -0
- package/src/extra/jbig2-read.js +169 -0
- package/src/extra/legacy-deprecated-annots.js +198 -0
- package/src/extra/legacy-deprecated-filters.js +167 -0
- package/src/extra/legacy-rc4-read.js +235 -0
- package/src/extra/legacy-xfa-read.js +104 -0
- package/src/extra/linearization-write.js +97 -0
- package/src/extra/misc.js +217 -0
- package/src/extra/optional-content-extended.js +142 -0
- package/src/extra/pdf-a-output-intent.js +112 -0
- package/src/extra/pdf-sandbox.js +88 -0
- package/src/extra/pdf-ua-tagged.js +116 -0
- package/src/extra/pdf-x-prepress.js +114 -0
- package/src/extra/redaction-iso32005.js +136 -0
- package/src/extra/shading-typed.js +222 -0
- package/src/extra/sig-aes-gcm.js +135 -0
- package/src/extra/sig-pades.js +242 -0
- package/src/extra/tagged-pdf-typed.js +203 -0
- package/src/extra/transparency-typed.js +135 -0
- package/src/extra/well-tagged-pdf.js +138 -0
- package/src/extra/xmp-extended.js +190 -0
- package/src/font/embed.js +480 -0
- package/src/font/encoding.js +92 -0
- package/src/font/font.js +101 -0
- package/src/font/type3.js +75 -0
- package/src/form/acroform.js +94 -0
- package/src/form/appearance.js +90 -0
- package/src/form/button.js +105 -0
- package/src/form/choice.js +152 -0
- package/src/form/fieldTree.js +120 -0
- package/src/form/signature.js +100 -0
- package/src/form/text.js +101 -0
- package/src/linearization/linearization.js +107 -0
- package/src/main.js +411 -0
- package/src/metadata/info.js +87 -0
- package/src/metadata/xmp.js +62 -0
- package/src/ocg/config.js +156 -0
- package/src/ocg/ocg.js +124 -0
- package/src/outline/outline.js +157 -0
- package/src/pdf.js +133 -0
- package/src/prepress/outputIntent.js +118 -0
- package/src/prepress/pageBoundary.js +108 -0
- package/src/sig/byteRange.js +306 -0
- package/src/sig/certChain.js +247 -0
- package/src/sig/dss.js +317 -0
- package/src/sig/oids.js +157 -0
- package/src/sig/sha1.js +142 -0
- package/src/sig/sign.js +1899 -0
- package/src/sig/signature.js +1441 -0
- package/src/sig/timestamp.js +236 -0
- package/src/syntax/crossRefStream.js +133 -0
- package/src/syntax/filters/ascii85.js +122 -0
- package/src/syntax/filters/asciiHex.js +83 -0
- package/src/syntax/filters/dispatch.js +176 -0
- package/src/syntax/filters/flate.js +316 -0
- package/src/syntax/filters/runLength.js +96 -0
- package/src/syntax/objStream.js +99 -0
- package/src/syntax/parser-obj.js +52 -0
- package/src/syntax/parser.js +321 -0
- package/src/syntax/serializer.js +221 -0
- package/src/syntax/tokenizer.js +290 -0
- package/src/syntax/trailer.js +76 -0
- package/src/syntax/xref.js +341 -0
- package/src/tagged/classMap.js +81 -0
- package/src/tagged/markedContent.js +123 -0
- package/src/tagged/parentTree.js +126 -0
- package/src/tagged/roleMap.js +107 -0
- package/src/tagged/structElement.js +138 -0
- package/src/tagged/structTree.js +94 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
---
|
|
2
|
+
module: pdfSign
|
|
3
|
+
category: pdf/sig
|
|
4
|
+
dependencies: [pdfErrors, pdfSigOids, pdfByteRange, pdfDssBuilder, pdfIncrementalWriter, pdfParser, asn1, rsa, ecc, ed25519, sha256, sha384, sha512, bitArray, pdfDocument, pdfSecurity, pdfStandardV4, pdfStandardV5, pdfStandardV6]
|
|
5
|
+
returns: object
|
|
6
|
+
worker-safe: true
|
|
7
|
+
status: complete
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# pdfSign
|
|
11
|
+
|
|
12
|
+
> PAdES signature generation (write side) — levels B / T / LT / LTA.
|
|
13
|
+
|
|
14
|
+
**Module** `pdfSign` | **Source** `packages/front/office/pdf/src/sig/sign.js` | **Deps** `pdfErrors`, `pdfSigOids`, `pdfByteRange`, `pdfDssBuilder`, `pdfIncrementalWriter`, `pdfParser`, `asn1`, `rsa`, `ecc`, `ed25519`, `sha256`, `sha384`, `sha512`, `bitArray`, `pdfDocument`, `pdfSecurity`, `pdfStandardV4`, `pdfStandardV5`, `pdfStandardV6` | **Worker-safe** yes
|
|
15
|
+
|
|
16
|
+
Companion of [`pdfSignature`](./signature.md) (verify side). Produces a
|
|
17
|
+
detached PKCS#7/CMS `SignedData` blob and embeds it into a signature
|
|
18
|
+
dictionary whose `/ByteRange` covers the whole document minus the
|
|
19
|
+
whole `/Contents` `<…>` token, delimiters included (ISO 32000-2 §12.8.3.3.1
|
|
20
|
+
— see [`pdfByteRange`](./byteRange.md)); `_emitWithPlaceholder`'s
|
|
21
|
+
`contentsOffset` / `contentsLength` still name the hex-digit span. Algorithms: RSA-PSS, ECDSA (P-256/P-384/P-521),
|
|
22
|
+
Ed25519 — PKCS#1 v1.5 is deliberately refused (fw policy, NIST SP
|
|
23
|
+
800-131A Rev.2). ECDSA signature values are DER `ECDSA-Sig-Value`
|
|
24
|
+
(RFC 3279 §2.2.3); Ed25519 always uses SHA-512 (RFC 8419 §3.1).
|
|
25
|
+
**Default `subFilter` is `adbe.pkcs7.detached`** — a PAdES
|
|
26
|
+
emitter MUST explicitly pass `subFilter: 'ETSI.CAdES.detached'`.
|
|
27
|
+
|
|
28
|
+
## PAdES levels
|
|
29
|
+
|
|
30
|
+
| Level | Requires | Behavior |
|
|
31
|
+
|-------|----------|----------|
|
|
32
|
+
| `B` (default) | — | Single embedded signature, `eContentInfo` absent, no `signedAttrs` unless `opts.useSignedAttrs`. |
|
|
33
|
+
| `T` | `opts.tsaSign` callback | Adds `signedAttrs` (contentType, messageDigest, signingTime, ESS `signing-certificate-v2`) and an `unsignedAttrs` RFC 3161 timestamp token. |
|
|
34
|
+
| `LT` | `T` requirements + `pdfDssBuilder`/`pdfDocument` (always present via the registered `modules` deps) | As `T`, then appends a DSS (`/Certs`, `/OCSPs`, `/CRLs`, `/VRI`) and an updated Catalog via an incremental update. |
|
|
35
|
+
| `LTA` | `LT` requirements | As `LT`, then appends a second incremental update carrying a `/DocTimeStamp` (fresh `tsaSign` call over the LT bytes). |
|
|
36
|
+
|
|
37
|
+
Every level signs by **incremental update** (§7.5.6, non-destructive): the
|
|
38
|
+
base bytes are kept verbatim and the signature object is appended through
|
|
39
|
+
[`pdfIncrementalWriter`](../document/incrementalWriter.md). The update section
|
|
40
|
+
therefore takes the form of the base's newest cross-reference section
|
|
41
|
+
(classical table or cross-reference stream), and its `/Root`, `/Info` and
|
|
42
|
+
`/ID` come from the newest-first merged trailer. The signature takes the
|
|
43
|
+
first object number at or past the merged `/Size`, so an object held in an
|
|
44
|
+
object stream is never overwritten. The fixed-width `/ByteRange`
|
|
45
|
+
and `/Contents` placeholders are found inside the signature object from its
|
|
46
|
+
own offset, and they are patched without changing the byte length. `LT`
|
|
47
|
+
adds a second update: the DSS, plus the Catalog re-defined under its own
|
|
48
|
+
number with `/DSS`. The Catalog is resolved through `readDocument` from the
|
|
49
|
+
trailer `/Root`, wherever it lives, including inside an object stream.
|
|
50
|
+
`LTA` adds a third update, the DocTimeStamp, appended the same way
|
|
51
|
+
as the signature. A hybrid-reference base (a classical trailer carrying
|
|
52
|
+
`/XRefStm`) is refused, and nothing is written.
|
|
53
|
+
|
|
54
|
+
The `pdfParser` dependency is no longer read. It keeps its position so that
|
|
55
|
+
code calling the factory with positional arguments still works.
|
|
56
|
+
`pdfDocument` comes next. Every level needs it, because the signature
|
|
57
|
+
field is written from the Catalog and page 1 that `readDocument` resolves.
|
|
58
|
+
`pdfSecurity`, `pdfStandardV4`, `pdfStandardV5` and `pdfStandardV6` are
|
|
59
|
+
appended last. They are read only when the base is encrypted, so a
|
|
60
|
+
hand-wired factory that signs only unencrypted documents may pass `null`
|
|
61
|
+
for them.
|
|
62
|
+
|
|
63
|
+
## What the update contains
|
|
64
|
+
|
|
65
|
+
ISO 32000-2 §12.7.5.5 makes a signature dictionary the value (`/V`) of a
|
|
66
|
+
signature field (`/FT /Sig`), and §12.8.5.2 says a document timestamp is
|
|
67
|
+
found the same way, by examining signature fields. So the incremental
|
|
68
|
+
update that carries a signature dictionary also carries the field that
|
|
69
|
+
holds it:
|
|
70
|
+
|
|
71
|
+
| Object | Content |
|
|
72
|
+
|--------|---------|
|
|
73
|
+
| Signature dictionary | `/Type /Sig` (`/Type /DocTimeStamp` for the LTA timestamp), `/Filter /Adobe.PPKLite`, `/SubFilter`, `/ByteRange`, `/Contents`. It takes the first object number at or past the merged `/Size`. |
|
|
74
|
+
| Field / widget | One object, the field dictionary merged with its widget annotation (§12.7.4, §12.5.6.19), numbered right after the signature: `/Type /Annot`, `/Subtype /Widget`, `/FT /Sig`, `/T (Signature<n>)`, `/V` → the signature dictionary, `/Rect [0 0 0 0]` (invisible), `/F 132` (Print + Locked), `/P` → page 1. `<n>` is the lowest index no root field of the document already uses, so a second `sign()` writes `Signature2`. |
|
|
75
|
+
| Page 1 | Re-emitted under its own number with the widget appended to `/Annots`. If `/Annots` is an indirect array, that array object is re-emitted instead and the page is not. Page 1 is the first leaf of the page tree. |
|
|
76
|
+
| `/AcroForm` | `/Fields` gets the new field appended, and `/SigFlags` is set to `3` (SignaturesExist + AppendOnly). An indirect `/AcroForm` is re-emitted under its own number. A direct or absent one is written into a re-emission of the Catalog. An indirect `/Fields` array is re-emitted under its own number. |
|
|
77
|
+
|
|
78
|
+
Every entry these objects already had is copied as it is, so existing form
|
|
79
|
+
fields, annotations and `/AcroForm` keys (`/DA`, `/DR`, `/NeedAppearances`
|
|
80
|
+
and so on) are kept. All new objects come before the update's
|
|
81
|
+
cross-reference section, so the `/ByteRange` covers them like every other
|
|
82
|
+
byte. `LT` copies the Catalog, `/AcroForm` included, when it adds `/DSS`.
|
|
83
|
+
`LTA` then appends a second field for its `/DocTimeStamp` in the timestamp's
|
|
84
|
+
own update.
|
|
85
|
+
|
|
86
|
+
### Ed25519: the ISO/TS 32002 declaration
|
|
87
|
+
|
|
88
|
+
EdDSA signatures come from ISO/TS 32002, which requires "PDF documents
|
|
89
|
+
using enhancements described in this document" to declare it in the
|
|
90
|
+
Catalog (ISO/TS 32002 §4). For `algorithm: 'ed25519'`, at every level and
|
|
91
|
+
with every `subFilter`, the signing update therefore also re-emits the
|
|
92
|
+
Catalog under its own number with:
|
|
93
|
+
|
|
94
|
+
- `/Extensions` holding the prefix `ISO_` → a developer extensions
|
|
95
|
+
dictionary (ISO 32000-2 §7.12.3) with exactly `/Type /DeveloperExtensions`,
|
|
96
|
+
`/BaseVersion /2.0`, `/ExtensionLevel 32002`, `/ExtensionRevision (:2022)`
|
|
97
|
+
and `/URL (https://www.iso.org/standard/45875.html)`;
|
|
98
|
+
- `/Version /2.0` when the document's effective version is below 2.0: the
|
|
99
|
+
Catalog `/Version` name when there is one, else the header version
|
|
100
|
+
(ISO 32000-2 §7.7.2, the entry exists so that an incremental update can
|
|
101
|
+
raise the version; ISO/TS 32002 Table 2 marks EdDSA as PDF 2.x). A PDF 2.0
|
|
102
|
+
document gets no `/Version` entry. A `/Version` that is not a name counts
|
|
103
|
+
as absent.
|
|
104
|
+
|
|
105
|
+
The declaration is merged with what the document already has (ISO 32000-2
|
|
106
|
+
§7.12.2: the value of a prefix is a developer extensions dictionary or an
|
|
107
|
+
array of them):
|
|
108
|
+
|
|
109
|
+
| Existing `/Extensions` | Result |
|
|
110
|
+
|------------------------|--------|
|
|
111
|
+
| absent | `<< /ISO_ ext >>` |
|
|
112
|
+
| present, no `ISO_` | `ISO_` added; the other prefixes (`ADBE_`, …) kept |
|
|
113
|
+
| `ISO_` a dictionary at `/ExtensionLevel 32002` | unchanged — signing an already declared document again adds nothing |
|
|
114
|
+
| `ISO_` a dictionary at another level | `ISO_` becomes the array `[existing ext]` |
|
|
115
|
+
| `ISO_` an array | `ext` appended, unless an element is already at level 32002 |
|
|
116
|
+
| any of these held by an indirect reference | resolved; the merged result is written as a direct dictionary in the Catalog, the referenced object stays in the file, no longer referenced by it |
|
|
117
|
+
| any other shape (`/Extensions 5`, an `ISO_` string, an array element that is not a dictionary) | `pdf/sign/bad-extensions`, nothing written |
|
|
118
|
+
|
|
119
|
+
The Catalog is re-emitted whenever the declaration or the version changes
|
|
120
|
+
it, including when the `/AcroForm` is indirect (otherwise the signature
|
|
121
|
+
field alone does not touch the Catalog); every other Catalog entry is
|
|
122
|
+
copied as it is. The `LT` and `LTA` updates copy the Catalog, so the
|
|
123
|
+
declaration stays in the newest Catalog. ECDSA and RSA-PSS signatures do
|
|
124
|
+
not touch `/Extensions` or `/Version`.
|
|
125
|
+
|
|
126
|
+
**Viewers.** Adobe Acrobat Reader does not validate Ed25519 (EdDSA)
|
|
127
|
+
signatures: it reports an error about the formatting of the signature,
|
|
128
|
+
with or without the declaration, and also on an Ed25519 signature produced
|
|
129
|
+
by OpenSSL (measured 2026-10-05). OpenSSL 3.5 and later verify them
|
|
130
|
+
(`openssl cms -verify`). Use ECDSA P-256 where Acrobat interoperability
|
|
131
|
+
matters — see the [PAdES guide](../../guide/pades-integration.md).
|
|
132
|
+
|
|
133
|
+
### Encrypted base
|
|
134
|
+
|
|
135
|
+
A base whose trailer carries `/Encrypt` gets the same field, with its
|
|
136
|
+
field name encrypted:
|
|
137
|
+
|
|
138
|
+
- **Password.** `opts.password` is required. It may be the owner or the
|
|
139
|
+
user password, and `''` is a valid empty user password. The document key
|
|
140
|
+
is derived from it through the standard security handler, once per
|
|
141
|
+
`sign()` call and before anything is written.
|
|
142
|
+
- **Permissions.** With the user password, `/P` must grant bit 4 (modify)
|
|
143
|
+
and bit 6 (annotations and form fields), ISO 32000-2 Table 22. The owner
|
|
144
|
+
password is not checked against `/P`.
|
|
145
|
+
- **Supported encryption.** AES only, for both the string and the stream
|
|
146
|
+
crypt filters: V=4 R=4 with `AESV2`, V=5 R=5 and V=5 R=6 with `AESV3`.
|
|
147
|
+
RC4 (V below 4, or a `/V2` crypt filter), AES-GCM (`AESV4`, ISO/TS
|
|
148
|
+
32003) and any handler other than `/Standard` are refused with a typed
|
|
149
|
+
error. Nothing is ever signed without its field.
|
|
150
|
+
- **What is encrypted.** The new field's `/T` is encrypted with the string
|
|
151
|
+
crypt filter, keyed by the field's own object number, behind a fresh
|
|
152
|
+
16-byte IV from `opts.randomBytes` (default `crypto.getRandomValues`).
|
|
153
|
+
When the string filter is `Identity`, the name is written in clear.
|
|
154
|
+
The signature dictionary's `/Contents` is never encrypted (ISO 32000-2
|
|
155
|
+
§7.6.2), and `/ByteRange` holds integers. Re-emitted objects (Catalog,
|
|
156
|
+
page 1, `/AcroForm`, `/Fields`) keep their number and generation, so the
|
|
157
|
+
encrypted strings they already held keep their key.
|
|
158
|
+
- **Ed25519 declaration.** The two new strings of the ISO/TS 32002
|
|
159
|
+
dictionary (`/ExtensionRevision`, `/URL`) are encrypted with the string
|
|
160
|
+
crypt filter under the Catalog's object number and generation, each
|
|
161
|
+
behind a fresh IV; names and integers are never encrypted. The strings
|
|
162
|
+
of an indirect `/Extensions` value that is inlined into the Catalog are
|
|
163
|
+
decrypted under their former object and encrypted again under the
|
|
164
|
+
Catalog's, since the V=4 key depends on the object number.
|
|
165
|
+
- **Trailer.** Every incremental update written over an encrypted base
|
|
166
|
+
repeats the base's `/Encrypt` in its trailer or cross-reference stream
|
|
167
|
+
dictionary (ISO 32000-2 §7.5.6).
|
|
168
|
+
- **Levels LT and LTA.** Levels LT and LTA are supported on an encrypted
|
|
169
|
+
base. The DSS certificate, OCSP and CRL streams, any VRI timestamp
|
|
170
|
+
stream and the VRI `/TU` string are encrypted with the document key
|
|
171
|
+
(stream and string crypt filters of the base's `/Encrypt`), as is the
|
|
172
|
+
DocTimeStamp field's `/T`. Each of these strings and streams gets its
|
|
173
|
+
own fresh 16-byte IV from `opts.randomBytes`. The hexadecimal
|
|
174
|
+
`/Contents` of the `/Sig` and `/DocTimeStamp` dictionaries is never
|
|
175
|
+
encrypted (ISO 32000-2 §7.6.2). Every incremental update repeats the
|
|
176
|
+
base's `/Encrypt` (§7.5.6). Copied objects (Catalog, page, AcroForm)
|
|
177
|
+
keep their existing ciphertext under their own object numbers.
|
|
178
|
+
|
|
179
|
+
To pick a field name that is not taken yet, the existing root field names
|
|
180
|
+
are decrypted first.
|
|
181
|
+
|
|
182
|
+
### Signed attributes
|
|
183
|
+
|
|
184
|
+
When signed attributes are emitted (levels T, LT and LTA, or
|
|
185
|
+
`useSignedAttrs: true`), they are written in DER `SET OF` order. Each
|
|
186
|
+
attribute is encoded first. The encodings are then sorted as unsigned byte
|
|
187
|
+
strings, and an encoding that is a prefix of a longer one sorts first
|
|
188
|
+
(X.690 §11.6). The signature covers these sorted bytes (RFC 5652 §5.4).
|
|
189
|
+
A verifier that re-encodes the attributes before it checks the signature,
|
|
190
|
+
as OpenSSL does for Ed25519, therefore checks the bytes that were signed.
|
|
191
|
+
Signatures written in the earlier fixed order (contentType, messageDigest,
|
|
192
|
+
signingTime, signing-certificate-v2) still verify with `pdfSignature`,
|
|
193
|
+
which checks the attributes as received.
|
|
194
|
+
|
|
195
|
+
## Resolve
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
const s = runtime.resolve('pdfSign');
|
|
199
|
+
// Returns: { sign, _buildPkcs7, _buildSignedAttrs, _sortDerSetOf,
|
|
200
|
+
// _buildUnsignedAttrsTsa, _extractIssuerSerial,
|
|
201
|
+
// _emitWithPlaceholder, _hashBytes, _toDer, HASH_TABLE }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The factory also returns internal helpers with a leading underscore,
|
|
205
|
+
exposed for white-box testing only — prefer `sign` for the stable contract.
|
|
206
|
+
|
|
207
|
+
## API
|
|
208
|
+
|
|
209
|
+
| Method | Signature | Returns |
|
|
210
|
+
|--------|-----------|---------|
|
|
211
|
+
| `sign` | `(pdfBytes: Uint8Array, opts: SignOpts) => Uint8Array` | The signed PDF bytes: the base kept verbatim plus one incremental update for the signature and its field, then one for the DSS (`LT`/`LTA`) and one for the DocTimeStamp and its field (`LTA`). |
|
|
212
|
+
| `HASH_TABLE` | `{ sha256, sha384, sha512 }` → `{ mod, oid, len }` | The supported digest algorithms, by `opts.hashAlg` name. |
|
|
213
|
+
| `_buildPkcs7` / `_buildSignedAttrs` / `_sortDerSetOf` / `_buildUnsignedAttrsTsa` / `_extractIssuerSerial` / `_emitWithPlaceholder` / `_hashBytes` / `_toDer` | internal helpers | Returned for white-box tests only; not a stable contract — use `sign`. |
|
|
214
|
+
|
|
215
|
+
### `SignOpts`
|
|
216
|
+
|
|
217
|
+
```js
|
|
218
|
+
{
|
|
219
|
+
cert: Uint8Array | string, // X.509 DER bytes or PEM string
|
|
220
|
+
privateKey: // algorithm-specific:
|
|
221
|
+
{ n, e, d } // RSA-PSS — Uint8Array each
|
|
222
|
+
| { curve, secretKey } // ECDSA — ecc.curves.c256|c384|c521 + ecc.ecdsa.secretKey
|
|
223
|
+
| Uint8Array, // Ed25519 — 64 bytes (seed || pub)
|
|
224
|
+
algorithm: 'rsa-pss' | 'ecdsa' | 'ed25519',
|
|
225
|
+
hashAlg?: 'sha256' | 'sha384' | 'sha512', // default 'sha256'; 'sha512' only (and default) for Ed25519
|
|
226
|
+
level?: 'B' | 'T' | 'LT' | 'LTA', // default 'B'
|
|
227
|
+
subFilter?: string, // default 'adbe.pkcs7.detached'
|
|
228
|
+
useSignedAttrs?: boolean, // default false for level 'B'
|
|
229
|
+
placeholderBytes?: number, // default 8192
|
|
230
|
+
docTimeStamp?: boolean, // internal — DocTimeStamp object emission
|
|
231
|
+
signingTime?: Date,
|
|
232
|
+
tsaSign?: (args: { digest, hashAlg }) => Uint8Array, // required for T/LT/LTA
|
|
233
|
+
dss?: { certs?, ocsps?, crls?, vri?, autoVri?, vriTime? }, // LT/LTA — see pdfDssBuilder
|
|
234
|
+
docTimeStampPlaceholder?: number, // LTA — default = placeholderBytes
|
|
235
|
+
password?: string | Uint8Array, // encrypted base: owner or user password ('' = empty user password)
|
|
236
|
+
randomBytes?: (n: number) => Uint8Array // encrypted base: IV source (field names, DSS strings and streams), default crypto.getRandomValues
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Examples
|
|
241
|
+
|
|
242
|
+
### Level B — RSA-PSS, PAdES `SubFilter`
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
const s = runtime.resolve('pdfSign');
|
|
246
|
+
const signed = s.sign(pdfBytes, {
|
|
247
|
+
cert, privateKey: { n, e, d },
|
|
248
|
+
algorithm: 'rsa-pss', hashAlg: 'sha256',
|
|
249
|
+
subFilter: 'ETSI.CAdES.detached' // required for PAdES — not the default
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Level T — with a timestamp authority callback
|
|
254
|
+
|
|
255
|
+
```js
|
|
256
|
+
const signed = s.sign(pdfBytes, {
|
|
257
|
+
cert, privateKey, algorithm: 'ecdsa',
|
|
258
|
+
level: 'T',
|
|
259
|
+
tsaSign: ({ digest, hashAlg }) => callRealTsa(digest, hashAlg) // returns RFC 3161 TimeStampToken DER
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Level LTA — full chain with DSS + DocTimeStamp
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
const signed = s.sign(pdfBytes, {
|
|
267
|
+
cert, privateKey, algorithm: 'ed25519',
|
|
268
|
+
level: 'LTA',
|
|
269
|
+
tsaSign: myTsaCallback,
|
|
270
|
+
dss: { certs: [leafDer, caDer], ocsps: [ocspDer], autoVri: true }
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Errors
|
|
275
|
+
|
|
276
|
+
Raised as `ContractError` unless noted.
|
|
277
|
+
|
|
278
|
+
| Code | When |
|
|
279
|
+
|------|------|
|
|
280
|
+
| `pdf/sign/bad-input` | `pdfBytes` not `Uint8Array`, or a cert/privateKey argument neither `Uint8Array` nor PEM string. |
|
|
281
|
+
| `pdf/sign/bad-opts` | `opts` missing or not an object. |
|
|
282
|
+
| `pdf/sign/level-not-implemented` | `opts.level` outside `B`/`T`/`LT`/`LTA`. |
|
|
283
|
+
| `pdf/sign/no-dss-builder` | Level `LT`/`LTA` requested without `pdfDssBuilder` wired. |
|
|
284
|
+
| `pdf/sign/no-document-reader` | Any level requested without `pdfDocument` wired (the signature field needs the Catalog and page 1). |
|
|
285
|
+
| `pdf/sign/no-incremental-writer` | Any level requested without `pdfIncrementalWriter` wired. |
|
|
286
|
+
| `pdf/sign/no-algorithm` | `opts.algorithm` missing. |
|
|
287
|
+
| `pdf/sign/bad-hash-alg` | `opts.hashAlg` outside `sha256`/`sha384`/`sha512`. |
|
|
288
|
+
| `pdf/sign/ed25519-requires-sha512` | `algorithm: 'ed25519'` with an `opts.hashAlg` other than `sha512` (RFC 8419 §3.1); `context.hashAlg` names it. Omit `hashAlg` or pass `'sha512'`. |
|
|
289
|
+
| `pdf/sign/tsa-required-for-level-T` | Level `T`/`LT`/`LTA` without `opts.tsaSign`. |
|
|
290
|
+
| `pdf/sign/tsa-bad-result` / `tsa-bad-result-lta` | `tsaSign` did not return a `Uint8Array`. |
|
|
291
|
+
| `pdf/sign/cert-parse` | Cert DER doesn't parse as a valid X.509 SEQUENCE (issuer/serial extraction). |
|
|
292
|
+
| `pdf/sign/unknown-hash` / `unknown-sigalg` / `unknown-algorithm` | Unsupported `hashAlg`/`signatureAlg`/`algorithm` value reaching the PKCS#7 builder or signer dispatch. |
|
|
293
|
+
| `pdf/sign/no-startxref` / `bad-startxref` | Base `pdfBytes` has no (or an unparsable) `startxref` — required to append the placeholder signature object. |
|
|
294
|
+
| `pdf/sign/no-trailer` | No cross-reference section of the base supplies a usable `/Size` and `/Root`. |
|
|
295
|
+
| `pdf/incremental/hybrid-base` (`RenderError`) | The base is a hybrid-reference file; propagated from `pdfIncrementalWriter`, nothing written. `pdf/incremental/unsupported-base` likewise when `startxref` designates neither a table nor an xref stream. |
|
|
296
|
+
| `pdf/sign/br-placeholder-missing` / `br-overflow` | Internal `/ByteRange` / `/Contents` placeholder location or patching failed (should not occur in practice). |
|
|
297
|
+
| `pdf/sign/no-rsa` / `no-ecc` / `no-ed25519` (`EncryptionError`) | The matching fw crypto primitive (`rsa.pssSign`, `ecc.ecdsa`, `ed25519.sign`) is unavailable. |
|
|
298
|
+
| `pdf/sign/bad-rsa-key` / `bad-ecdsa-key` / `bad-ed25519-key` | `privateKey` shape doesn't match the selected `algorithm`. |
|
|
299
|
+
| `pdf/sign/rsa-failed` / `ed25519-failed` (`EncryptionError`) | The underlying fw signer returned `false`. |
|
|
300
|
+
| `pdf/sign/pkcs7-too-large` | The built PKCS#7 blob exceeds `placeholderBytes`. |
|
|
301
|
+
| `pdf/sign/hex-overflow` / `dts-hex-overflow` | PKCS#7 (or TimeStampToken) hex exceeds the reserved `/Contents` placeholder length. |
|
|
302
|
+
| `pdf/sign/tst-too-large` | LTA `DocTimeStamp` TimeStampToken exceeds its placeholder. |
|
|
303
|
+
| `pdf/sign/bad-extensions` | `algorithm: 'ed25519'` and the Catalog `/Extensions` (or its `ISO_` entry, or an element of an `ISO_` array) is not a dictionary, an array of dictionaries or a reference to one (ISO 32000-2 §7.12). `context: { shape }` names the offending value's type. Nothing is written. |
|
|
304
|
+
| `pdf/sign/catalog-not-found` | The trailer `/Root` does not resolve to a dictionary (signature field, or the LT/LTA Catalog update). |
|
|
305
|
+
| `pdf/sign/no-security-handler` | Encrypted base, and `pdfSecurity`, `pdfStandardV4`, `pdfStandardV5` or `pdfStandardV6` is not wired. |
|
|
306
|
+
| `pdf/sign/encrypted-password-required` | Encrypted base without `opts.password`. Pass `''` for an empty user password. |
|
|
307
|
+
| `pdf/sign/encrypted-bad-password` | `opts.password` is neither the owner nor the user password. The password is not echoed in the error. |
|
|
308
|
+
| `pdf/sign/encrypted-unsupported` | Encryption that cannot be signed: `context.filter` names a handler other than `Standard`; `context.reason` is `'rc4'` (V below 4, or a `/V2` crypt filter), `'aes-gcm'` (`AESV4`) or `'no-id'` (V=4 without a trailer `/ID`); `context.cause` carries the handler's error code for an unreadable or unsupported `/Encrypt`. |
|
|
309
|
+
| `pdf/sign/encrypted-permission-denied` | User password, and `/P` lacks bit 4 or bit 6. `context: { P, required: ['modify', 'annot'] }`. Use the owner password. |
|
|
310
|
+
| `pdf/sign/no-random` (`EncryptionError`) | Encrypted base, and neither `opts.randomBytes` nor `crypto.getRandomValues` is available for the IV. |
|
|
311
|
+
|
|
312
|
+
## See also
|
|
313
|
+
|
|
314
|
+
- [`pdfSignature`](./signature.md) — verify side (the counterpart this module's output is checked against).
|
|
315
|
+
- [`pdfDssBuilder`](./dss.md) — DSS construction for levels LT/LTA.
|
|
316
|
+
- [`pdfByteRange`](./byteRange.md) · [`pdfTimestamp`](./timestamp.md) · [`pdfCertChain`](./certChain.md)
|
|
317
|
+
- [`pdfIncrementalWriter`](../document/incrementalWriter.md) — underlying append mechanism for every level.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
---
|
|
2
|
+
module: pdfSignature
|
|
3
|
+
category: pdf/sig
|
|
4
|
+
dependencies: [pdfErrors, pdfParser, pdfSigOids, asn1, rsa, ecc, ed25519, sha256, sha384, sha512, bitArray]
|
|
5
|
+
returns: object
|
|
6
|
+
worker-safe: true
|
|
7
|
+
status: complete
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# pdfSignature
|
|
11
|
+
|
|
12
|
+
> Digital signature handler — ISO 32000-2 §12.8 + ISO TS 32001 (CAdES) + ISO TS 32002 (Ed25519).
|
|
13
|
+
|
|
14
|
+
**Module** `pdfSignature` | **Source** `packages/front/office/pdf/src/sig/signature.js` | **Deps** `pdfErrors`, `pdfParser`, `pdfSigOids`, `asn1`, `rsa`, `ecc`, `ed25519`, `sha256`, `sha384`, `sha512`, `bitArray` | **Worker-safe** yes
|
|
15
|
+
|
|
16
|
+
Typing and end-to-end verification of PDF signatures (detached PKCS#7/CMS). Supported SubFilters: `adbe.pkcs7.detached`, `adbe.pkcs7.sha1` (legacy), `ETSI.CAdES.detached`, `ETSI.RFC3161` (timestamp only), plus Ed25519 (ISO TS 32002). `verifySignature` reconstructs the `/ByteRange`-covered bytes, recomputes the digest, locates the signer's certificate inside the embedded PKCS#7 `SignedData`, and **does dispatch and execute** the public-key check itself (`rsa.pssVerify` / ECDSA / `ed25519.verify` via the internal `verifyPk` dispatcher) — see § *Semantics of `verified` vs `valid` vs `pkVerified`* below. The result's `valid` field is a deprecated alias of `verified`: read `verified`.
|
|
17
|
+
|
|
18
|
+
## Resolve
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
const sig = runtime.resolve('pdfSignature');
|
|
22
|
+
// Returns: { typeSignature, verifySignature, verifyAllSignatures, verifyPk,
|
|
23
|
+
// locatePkcs7, DIGEST_OIDS, SIG_OIDS }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## API
|
|
27
|
+
|
|
28
|
+
| Method | Signature | Returns |
|
|
29
|
+
|--------|-----------|---------|
|
|
30
|
+
| `typeSignature` | `(dict, refInfo?) => Signature` | Strict typing, §12.8.1 Table 252. |
|
|
31
|
+
| `verifySignature` | `(typedSig, documentBytes, fwBundle?) => VerifyResult` | Full verification — digests `/ByteRange`, locates the signer cert, and runs the public-key check. |
|
|
32
|
+
| `verifyAllSignatures` | `(documentBytes, fwBundle?) => { signatures: VerifyResult[], timestamps: TsVerifyResult[] }` | Scans the raw bytes for every `/Type /Sig` and `/Type /DocTimeStamp` object (across incremental updates) and verifies each. |
|
|
33
|
+
| `verifyPk` | `(args: { algorithm, pubKey, signature, message?, digest?, hashMod?, sLen? }) => { verified: boolean, code?: string, error?: string }` | Public-key verification primitive dispatcher — routes to `rsa.pssVerify` (`'rsa-pss'`), fw's deliberate RSA PKCS#1 v1.5 refusal (`'rsa-v15'`/`'rsa'`), ECDSA (`'ecdsa'`/`'ecc'`) or `ed25519.verify` (`'ed25519'`). For ECDSA, `signature` is the DER `ECDSA-Sig-Value` or the legacy fixed-width raw r‖s (DER tried first; anything else → `pdf/sig/verify-pk/bad-ecdsa-sig`). |
|
|
34
|
+
| `locatePkcs7` | `(blob: Uint8Array, asn1Mod?) => asn1Children \| false` | Decodes the CMS `ContentInfo` → `SignedData` and returns its parsed children (`false` on malformed input). |
|
|
35
|
+
|
|
36
|
+
`DIGEST_OIDS` and `SIG_OIDS` (constants, re-exported from `pdfSigOids`) are also returned for callers that need to map OIDs to algorithm names directly.
|
|
37
|
+
|
|
38
|
+
The factory also returns five internal helpers with a leading underscore
|
|
39
|
+
(`_hashByteRange`, `_scanSignatureObjects`, `_verifyPkcs7Signature`,
|
|
40
|
+
`_verifyTsaSignature`, `_ecdsaSigToRaw`) — exposed for internal reuse/testing, not part of the
|
|
41
|
+
stable public contract; prefer the methods above.
|
|
42
|
+
|
|
43
|
+
### Shape `Signature`
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
{
|
|
47
|
+
kind: 'Sig' | 'DocTimeStamp',
|
|
48
|
+
filter, subFilter, contents: Uint8Array,
|
|
49
|
+
byteRange: [start1, len1, start2, len2],
|
|
50
|
+
reference, cert, name, m, location, reason, contactInfo,
|
|
51
|
+
v, propBuild, propAuthTime, propAuthType,
|
|
52
|
+
raw // the source dict
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
### Semantics of `verified` vs `valid` vs `pkVerified`
|
|
59
|
+
|
|
60
|
+
`verifySignature(...)` returns:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
{
|
|
64
|
+
verified: true, // public-key check executed AND succeeded
|
|
65
|
+
valid: true, // deprecated alias of verified
|
|
66
|
+
pkVerified: true, // explicit strict-crypto boolean
|
|
67
|
+
errors: [ /* { code, message, context?, cause? } */ ],
|
|
68
|
+
signerCerts: [ /* { der } */ ],
|
|
69
|
+
hashAlg: 'sha256' | 'sha384' | 'sha512' | null,
|
|
70
|
+
signatureAlg:'rsa' | 'rsa-pss' | 'ecc' | 'ed25519' | null,
|
|
71
|
+
computedDigest: Uint8Array | null // digest recomputed over /ByteRange
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`valid` is a **deprecated alias of `verified`**: it always carries the same
|
|
76
|
+
value, is kept for compatibility, and will be removed in a future major
|
|
77
|
+
version. Read `verified`. The same holds for the `valid` field of each
|
|
78
|
+
`verifyAllSignatures` entry, signatures and document timestamps alike.
|
|
79
|
+
|
|
80
|
+
`verified` / `pkVerified` are `true` only when the public-key check
|
|
81
|
+
(`verifyPk`, dispatched internally) actually ran and succeeded against the
|
|
82
|
+
signer's certificate SPKI. There is **no** `pdf/sig/pk-verify-not-wired`
|
|
83
|
+
code in this module — the public-key verification path is wired by
|
|
84
|
+
construction; a failed check surfaces a specific `errors[]` record instead
|
|
85
|
+
(e.g. `pdf/sig/pk-verify-failed`, `pdf/sig/rsa-pkcs1v15-deprecated` for the
|
|
86
|
+
deliberately-refused legacy scheme).
|
|
87
|
+
|
|
88
|
+
### Structural verification + digest
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
const sig = runtime.resolve('pdfSignature');
|
|
92
|
+
const typed = sig.typeSignature(sigFieldDict.v);
|
|
93
|
+
const result = sig.verifySignature(typed, documentBytes, fwBundle);
|
|
94
|
+
result.verified; // true when the PK check passed
|
|
95
|
+
result.computedDigest; // Uint8Array — /ByteRange digest
|
|
96
|
+
result.pkVerified; // strict-crypto boolean, same as `verified`
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Multi-signature + timestamp scan
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
const sig = runtime.resolve('pdfSignature');
|
|
103
|
+
const { signatures, timestamps } = sig.verifyAllSignatures(documentBytes, fwBundle);
|
|
104
|
+
signatures.every((s) => s.verified);
|
|
105
|
+
timestamps.every((t) => t.verified);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Each `timestamps[]` entry (`TsVerifyResult`) has this shape:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
{
|
|
112
|
+
objNum, objGen, // the /DocTimeStamp object
|
|
113
|
+
verified: true, // imprint matches AND the gap is exact
|
|
114
|
+
valid: true, // deprecated alias of verified
|
|
115
|
+
kind: 'DocTimeStamp',
|
|
116
|
+
subFilter: 'ETSI.RFC3161' | string | null,
|
|
117
|
+
hashAlg: 'sha256' | 'sha384' | 'sha512' | string | null,
|
|
118
|
+
tstInfo: { hashAlg, imprint, rawSd } | null,
|
|
119
|
+
imprintVerified: true, // messageImprint == /ByteRange digest (imprint outcome alone)
|
|
120
|
+
tsaVerified: false, // TSA signature check — informational
|
|
121
|
+
gapForm: 'token' | 'digits' | null,
|
|
122
|
+
errors: [ /* { code, message, context?, cause? } */ ],
|
|
123
|
+
signerCerts: [ /* the TSA signer certificates, when located */ ]
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`gapForm` names the accepted `/ByteRange` gap form, in the vocabulary of
|
|
128
|
+
`pdfByteRange.auditByteRange`: `'token'` when the gap is exactly the whole
|
|
129
|
+
`<…>` token of the `/Contents` value, `'digits'` when it is exactly its hex
|
|
130
|
+
digits, `null` for any other gap. It is present on every entry, failure
|
|
131
|
+
paths included. A non-exact gap adds a
|
|
132
|
+
`pdf/sig/byterange/gap-start-mismatch` / `gap-end-mismatch` record to
|
|
133
|
+
`errors[]` and leaves `verified: false` even when `imprintVerified` is
|
|
134
|
+
`true`; the earlier `pdf/ts/*` failures keep their first error code.
|
|
135
|
+
`signatures[]` entries carry `objNum` / `objGen` plus the `verifySignature`
|
|
136
|
+
result shape above, with no `gapForm` key.
|
|
137
|
+
|
|
138
|
+
### Inspecting the decoded CMS
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
const sd = sig.locatePkcs7(typed.contents);
|
|
142
|
+
// sd is the parsed SignedData children array (ASN.1 nodes), or `false`.
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Errors
|
|
146
|
+
|
|
147
|
+
| Code | Class | When |
|
|
148
|
+
|------|--------|------|
|
|
149
|
+
| `pdf/sig/not-dict` | `ParseError` | Argument is not a dict. |
|
|
150
|
+
| `pdf/sig/bad-type` | `ParseError` | `/Type` is neither `/Sig` nor `/DocTimeStamp`. |
|
|
151
|
+
| `pdf/sig/missing-filter` | `ParseError` | `/Filter` missing. |
|
|
152
|
+
| `pdf/sig/missing-subfilter` | `ParseError` | `/SubFilter` missing. |
|
|
153
|
+
| `pdf/sig/unknown-subfilter` | `ParseError` | SubFilter outside the supported set. |
|
|
154
|
+
| `pdf/sig/missing-contents` | `ParseError` | `/Contents` missing. |
|
|
155
|
+
| `pdf/sig/missing-byterange` | `ParseError` | `/ByteRange` missing. |
|
|
156
|
+
| `pdf/sig/bad-byterange` | `ParseError` | `/ByteRange` malformed (≠ 4 integers). |
|
|
157
|
+
| `pdf/sig/missing-fw` | `EncryptionError` | fw bundle incomplete for `verifySignature`. |
|
|
158
|
+
| `pdf/sig/byterange/inconsistent` | `ParseError` | ByteRange offsets inconsistent with the document length. |
|
|
159
|
+
| `pdf/sig/pkcs7-malformed` | record `errors[]` | PKCS#7 `SignedData` parse failed. |
|
|
160
|
+
| `pdf/sig/no-signer` / `empty-signers` / `bad-signer` | record `errors[]` | `signerInfos` absent / empty / malformed. |
|
|
161
|
+
| `pdf/sig/unknown-digest` / `unknown-sigalg` | record `errors[]` | digestAlgorithm / signatureAlgorithm OID outside the supported set. |
|
|
162
|
+
| `pdf/sig/no-hash` | record `errors[]` | Hash module unavailable for the resolved `hashAlg`. |
|
|
163
|
+
| `pdf/sig/signer-info-short` / `no-encrypted-digest` | record `errors[]` | SignerInfo ASN.1 shape unexpected. |
|
|
164
|
+
| `pdf/sig/digest-failed` | record `errors[]` | Failed to recompute the `/ByteRange` digest. The record is `{ code, message }`: the underlying error's message is appended to `message`, and no `cause` is attached. |
|
|
165
|
+
| `pdf/sig/signed-attrs-parse-failed` / `digest-not-computed` / `digest-length-mismatch` / `digest-mismatch` / `no-message-digest-attr` | record `errors[]` | `signedAttrs` present but its `messageDigest` attribute fails to parse or match. |
|
|
166
|
+
| `pdf/sig/signer-cert-not-found` | record `errors[]` | No embedded cert matches the SignerInfo's `IssuerAndSerialNumber`. |
|
|
167
|
+
| `pdf/sig/spki-*` (`cert-parse`, `not-found`, `malformed`, `rsa-parse`, `rsa-fields`, `ecc-not-uncompressed`, `ed25519-bad-len`, `unsupported-alg`) | record `errors[]` | SubjectPublicKeyInfo extraction/shape failure. |
|
|
168
|
+
| `pdf/sig/byterange/gap-start-mismatch` / `gap-end-mismatch` | record `errors[]` (`verified: false`) | `/Sig` and `/DocTimeStamp`: the `/ByteRange` gap is neither exactly the hex digits of `/Contents` nor exactly its whole `<…>` token — both forms are accepted, every other gap is refused by `verifySignature` / `verifyAllSignatures` (a `/Sig`) and by `verifyAllSignatures` (a `/DocTimeStamp`, whose `imprintVerified` still reports the imprint outcome alone). The `timestamps[]` entry names the accepted form in `gapForm`. |
|
|
169
|
+
| `pdf/sig/byterange-concat-failed` | record `errors[]` | Failed to extract the ByteRange-covered bytes for the fallback signed-payload path. |
|
|
170
|
+
| `pdf/sig/pk-verify-failed` | record `errors[]` | `verifyPk` ran and returned `verified: false` (see `verifyPk` codes below). |
|
|
171
|
+
| `pdf/sig/verify-pk/bad-args` / `no-alg` / `bad-sig` / `no-message` / `no-hash` / `no-rsa` / `bad-rsa-key` / `no-ecc` / `bad-ecc-key` / `unknown-curve` / `bad-ecc-point` / `bad-ecdsa-sig` / `no-digest` / `no-ed25519` / `bad-ed25519-key` / `unknown-alg` / `throw` | `verifyPk` return `{ code }` | Invalid inputs or dispatch failure inside `verifyPk`. |
|
|
172
|
+
| `pdf/sig/rsa-pkcs1v15-deprecated` | `verifyPk` return `{ code }` | RSA PKCS#1 v1.5 is deliberately refused (NIST SP 800-131A Rev.2) — use RSA-PSS. |
|
|
173
|
+
| `pdf/ts/*` (`empty-contents`, `parse-failed`, `unknown-hash`, `imprint-length-mismatch`, `imprint-mismatch`, `byterange-failed`, `no-ci`, `short-ci`, `no-sd`, `short-sd`, `no-eci`, `no-econtent`, `no-tst`, `short-tstinfo`, `no-imprint`, `tsa-no-sd`, `tsa-throw`) | record `errors[]` | Emitted by `verifyAllSignatures`'s `/DocTimeStamp` branch (RFC 3161 TimeStampToken parsing + TSA signature check). |
|
|
174
|
+
|
|
175
|
+
## See also
|
|
176
|
+
|
|
177
|
+
- [`pdfByteRange`](./byteRange.md) · [`pdfTimestamp`](./timestamp.md) · [`pdfCertChain`](./certChain.md)
|
|
178
|
+
- [`pdfSignatureField`](../form/signature.md)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
module: pdfTimestamp
|
|
3
|
+
category: pdf/sig
|
|
4
|
+
dependencies: [pdfErrors, pdfSigOids, asn1, rsa, ecc, ed25519, sha256, sha384, sha512]
|
|
5
|
+
returns: object
|
|
6
|
+
worker-safe: true
|
|
7
|
+
status: complete
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# pdfTimestamp
|
|
11
|
+
|
|
12
|
+
> Document Timestamp + TSA tokens — ISO 32000-2 §12.8.5 / RFC 3161.
|
|
13
|
+
|
|
14
|
+
**Module** `pdfTimestamp` | **Source** `packages/front/office/pdf/src/sig/timestamp.js` | **Deps** `pdfErrors`, `pdfSigOids`, `asn1`, `rsa`, `ecc`, `ed25519`, `sha256`, `sha384`, `sha512` | **Worker-safe** yes
|
|
15
|
+
|
|
16
|
+
Parsing and verification of RFC 3161 Time-Stamp Authority tokens — used either as a standalone document timestamp (`SubFilter = ETSI.RFC3161`) or as an unsigned attribute of a signature for proof of existence. The module extracts `TSTInfo` (genTime, policy, messageImprint, serialNumber) and verifies the TSA signature against the embedded certificate.
|
|
17
|
+
|
|
18
|
+
## Resolve
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
const ts = runtime.resolve('pdfTimestamp');
|
|
22
|
+
// Returns: { parseTimestampToken, verifyTimestamp,
|
|
23
|
+
// extractTimestampFromUnsignedAttrs,
|
|
24
|
+
// OID_TST_INFO, OID_AA_TIMESTAMP }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## API
|
|
28
|
+
|
|
29
|
+
| Method | Signature | Returns |
|
|
30
|
+
|--------|-----------|---------|
|
|
31
|
+
| `parseTimestampToken` | `(blob: Uint8Array, asn1Mod?) => TstInfo` | Decodes the RFC 3161 ContentInfo. |
|
|
32
|
+
| `verifyTimestamp` | `(blob: Uint8Array, fwBundle?) => VerifyResult` | Cryptographic verification. |
|
|
33
|
+
| `extractTimestampFromUnsignedAttrs` | `(signedAttrs, asn1Mod?) => Uint8Array \| null` | Looks up `id-aa-timeStampToken` (1.2.840.113549.1.9.16.2.14). |
|
|
34
|
+
| `OID_TST_INFO` | `string` (constant) | `id-ct-TSTInfo` OID (1.2.840.113549.1.9.16.1.4), used to validate `encapContentInfo`'s eContentType. |
|
|
35
|
+
| `OID_AA_TIMESTAMP` | `string` (constant) | `id-aa-timeStampToken` OID, used by `extractTimestampFromUnsignedAttrs`. |
|
|
36
|
+
|
|
37
|
+
### Shape `TstInfo`
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
{
|
|
41
|
+
version, policy: oid,
|
|
42
|
+
serialNumber: Uint8Array,
|
|
43
|
+
messageImprint: { hashAlg, hashedMessage: Uint8Array } | null,
|
|
44
|
+
genTime: Date | null,
|
|
45
|
+
nonce?: Uint8Array, tsa?: Uint8Array
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Examples
|
|
50
|
+
|
|
51
|
+
### Document timestamp
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
const ts = runtime.resolve('pdfTimestamp');
|
|
55
|
+
const result = ts.verifyTimestamp(sig.contents);
|
|
56
|
+
result.valid;
|
|
57
|
+
result.tstInfo.genTime; // Date — proof of existence
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Timestamp embedded in a signature
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
const pk7 = runtime.resolve('pdfSignature').locatePkcs7(sig.contents);
|
|
64
|
+
const tsBytes = ts.extractTimestampFromUnsignedAttrs(pk7.signerInfo.unsignedAttrs);
|
|
65
|
+
if (tsBytes) {
|
|
66
|
+
const tstInfo = ts.parseTimestampToken(tsBytes);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Errors
|
|
71
|
+
|
|
72
|
+
| Code | Class | When |
|
|
73
|
+
|------|--------|------|
|
|
74
|
+
| `pdf/ts/bad-input` | `ParseError` | Argument is not a Uint8Array. |
|
|
75
|
+
| `pdf/ts/missing-fw` | `EncryptionError` | fw bundle incomplete. |
|
|
76
|
+
| `pdf/ts/malformed` | `ParseError` | Invalid ASN.1 structure (ContentInfo, SignedData, or top-level TSTInfo). |
|
|
77
|
+
| `pdf/ts/no-econtent` | `ParseError` | `encapContentInfo` has no `eContent`. |
|
|
78
|
+
| `pdf/ts/wrong-econtent` | `ParseError` | eContentType ≠ `id-ct-TSTInfo`. |
|
|
79
|
+
| `pdf/ts/no-tstinfo` | `ParseError` | TSTInfo not found inside eContent. |
|
|
80
|
+
| `pdf/ts/bad-tstinfo` | `ParseError` | Required fields missing or mistyped. |
|
|
81
|
+
|
|
82
|
+
## See also
|
|
83
|
+
|
|
84
|
+
- [`pdfSignature`](./signature.md) · [`pdfByteRange`](./byteRange.md) · [`pdfCertChain`](./certChain.md)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Syntax — ISO 32000-2 §7.2–§7.5
|
|
2
|
+
|
|
3
|
+
Binary layer: `Uint8Array` → tokens → typed objects → xref table → trailer.
|
|
4
|
+
|
|
5
|
+
| Module | Returns | Deps | Description |
|
|
6
|
+
|--------|---------|------|-------------|
|
|
7
|
+
| [`pdfTokenizer`](./tokenizer.md) | `{ tokenize, lastIndexOfBytes }` | `pdfErrors`, `pdfShared` | Lexer §7.2. |
|
|
8
|
+
| [`pdfParserObj`](./parser-obj.md) | `{ obj, getEntry, isType }` | none | Typed-object builders and reflection helpers. |
|
|
9
|
+
| [`pdfParser`](./parser.md) | `{ tokenize, parseObject, parseIndirect, parseFromBytes, parseIndirectFromBytes, parserLimits, setParserLimits, obj, getEntry, isType }` | `pdfErrors`, `pdfParserObj`, `pdfTokenizer` | Typed objects §7.3. |
|
|
10
|
+
| [`pdfXref`](./xref.md) | `{ locateStartXref, readStartXref, parseXrefTable, parseTrailerDict, readXrefStreamDict, buildXrefStream }` | `pdfErrors`, `pdfTokenizer`, `pdfParser` | Classical table §7.5.4. |
|
|
11
|
+
| [`pdfTrailer`](./trailer.md) | `{ typeTrailer }` | `pdfErrors`, `pdfParserObj` | Typed trailer §7.5.5. |
|
|
12
|
+
| [`pdfSerializer`](./serializer.md) | `{ serializeObject, serializeIndirect, formatReal }` | `pdfErrors` | Object emission §7.3. |
|
|
13
|
+
| [`pdfObjStream`](./objStream.md) | `{ parseObjectStream }` | `pdfErrors`, `pdfParserObj`, `pdfTokenizer`, `pdfParser` | Object streams §7.5.7. |
|
|
14
|
+
| [`pdfCrossRefStream`](./crossRefStream.md) | `{ parseCrossRefStream }` | `pdfErrors`, `pdfParserObj` | Cross-reference streams §7.5.8. |
|
|
15
|
+
| [Filters](./filters/README.md) | `{ decode, encode }` × 4 + dispatch | `pdfErrors`, `zlib` | The `/Filter` chain §7.4. |
|
|
16
|
+
|
|
17
|
+
## Common pattern
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
const tokMod = runtime.resolve('pdfTokenizer');
|
|
21
|
+
const tok = tokMod.tokenize(bytes);
|
|
22
|
+
let t;
|
|
23
|
+
while ((t = tok.next())) { /* … */ }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## See also
|
|
27
|
+
|
|
28
|
+
- [Document layer](../document/README.md)
|
|
29
|
+
- [Read pipeline](../../guide/read-pdf.md)
|