@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,247 @@
|
|
|
1
|
+
// Copyright (c) 2026 AwaCloud SAS
|
|
2
|
+
// Author: Matthieu Bouilloux
|
|
3
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
4
|
+
// Dual-licensed; see the NOTICE file for licensing and any additional terms.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @fileoverview X.509 certificate chain extraction from a PKCS#7
|
|
8
|
+
* SignedData blob.
|
|
9
|
+
*
|
|
10
|
+
* - `extractCertsFromCms(blob, asn1)` → typed records for each cert in
|
|
11
|
+
* the SignedData `certificates [0]` field.
|
|
12
|
+
* - `extractCertFromPem(pem, pemMod, asn1)` → typed record from a PEM
|
|
13
|
+
* block.
|
|
14
|
+
* - `findIssuer(cert, candidates)` / `validateChainOrder(chain)` →
|
|
15
|
+
* structural chain checks (subject = next.issuer), no signature
|
|
16
|
+
* verification at this layer.
|
|
17
|
+
*
|
|
18
|
+
* @module pdf/sig/certChain
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Module factory.
|
|
23
|
+
*/
|
|
24
|
+
import { pdfErrors } from '../errors.js';
|
|
25
|
+
import { pdfSigOids } from './oids.js';
|
|
26
|
+
import { asn1 } from '@awacloud/fw/crypto/utils/asn1.js';
|
|
27
|
+
import { pem } from '@awacloud/fw/crypto/utils/pem.js';
|
|
28
|
+
|
|
29
|
+
export const pdfCertChain = {
|
|
30
|
+
name: 'pdfCertChain',
|
|
31
|
+
dependencies: ['pdfErrors', 'pdfSigOids', 'asn1', 'pem'],
|
|
32
|
+
deps: [pdfErrors, pdfSigOids, asn1, pem],
|
|
33
|
+
factory(errors, sigOids, asn1, pem) {
|
|
34
|
+
const { EncryptionError, ParseError } = errors;
|
|
35
|
+
const {
|
|
36
|
+
KEY_ALG_OIDS,
|
|
37
|
+
SIG_ALG_OIDS_VERBOSE: SIG_ALG_OIDS,
|
|
38
|
+
shortOid: _shortOid
|
|
39
|
+
} = sigOids;
|
|
40
|
+
|
|
41
|
+
function _parseUtcOrGeneralizedTime(node) {
|
|
42
|
+
if (!node) return null;
|
|
43
|
+
const s = String.fromCharCode.apply(null, Array.from(node.value));
|
|
44
|
+
let yr, mo, da, hr, mi, se;
|
|
45
|
+
if (node.tag === 0x17) { // UTCTime: YYMMDDhhmmssZ
|
|
46
|
+
const m = /^(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(s);
|
|
47
|
+
if (!m) return null;
|
|
48
|
+
yr = +m[1]; yr += yr < 50 ? 2000 : 1900;
|
|
49
|
+
mo = +m[2] - 1; da = +m[3]; hr = +m[4]; mi = +m[5]; se = +m[6];
|
|
50
|
+
} else if (node.tag === 0x18) { // GeneralizedTime: YYYYMMDDhhmmssZ
|
|
51
|
+
const m = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(s);
|
|
52
|
+
if (!m) return null;
|
|
53
|
+
yr = +m[1]; mo = +m[2] - 1; da = +m[3];
|
|
54
|
+
hr = +m[4]; mi = +m[5]; se = +m[6];
|
|
55
|
+
} else { return null; }
|
|
56
|
+
return new Date(Date.UTC(yr, mo, da, hr, mi, se));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function _renderName(seqValue, asn1Mod) {
|
|
60
|
+
const rdns = asn1Mod.parseChildren(seqValue);
|
|
61
|
+
if (!rdns) return '';
|
|
62
|
+
const parts = [];
|
|
63
|
+
for (let i = 0; i < rdns.length; i++) {
|
|
64
|
+
const set = asn1Mod.parseChildren(rdns[i].value);
|
|
65
|
+
if (!set) continue;
|
|
66
|
+
for (let j = 0; j < set.length; j++) {
|
|
67
|
+
const tv = asn1Mod.parseChildren(set[j].value);
|
|
68
|
+
if (!tv || tv.length < 2) continue;
|
|
69
|
+
const oid = asn1Mod.readOid(tv[0]);
|
|
70
|
+
const valBytes = tv[1].value;
|
|
71
|
+
const val = String.fromCharCode.apply(null, Array.from(valBytes));
|
|
72
|
+
parts.push(_shortOid(oid) + '=' + val);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return parts.join(',');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function _reconstructDer(node, parentBuf) {
|
|
79
|
+
const len = node.length;
|
|
80
|
+
let lenLen;
|
|
81
|
+
if (len < 0x80) lenLen = 1;
|
|
82
|
+
else if (len < 0x100) lenLen = 2;
|
|
83
|
+
else if (len < 0x10000) lenLen = 3;
|
|
84
|
+
else if (len < 0x1000000) lenLen = 4;
|
|
85
|
+
else lenLen = 5;
|
|
86
|
+
return parentBuf.subarray(node.valueOff - 1 - lenLen, node.next);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function parseCertificate(der, asn1Mod) {
|
|
90
|
+
const a1 = asn1Mod || asn1;
|
|
91
|
+
const top = a1.parseOne(der, 0);
|
|
92
|
+
if (!top) {
|
|
93
|
+
throw new ParseError('pdf/cert/malformed',
|
|
94
|
+
'Certificate top-level SEQUENCE parse failed');
|
|
95
|
+
}
|
|
96
|
+
const cert = a1.parseChildren(top.value);
|
|
97
|
+
if (!cert || cert.length < 3) {
|
|
98
|
+
throw new ParseError('pdf/cert/short',
|
|
99
|
+
'Certificate has fewer than 3 children');
|
|
100
|
+
}
|
|
101
|
+
const tbs = a1.parseChildren(cert[0].value);
|
|
102
|
+
if (!tbs) {
|
|
103
|
+
throw new ParseError('pdf/cert/bad-tbs',
|
|
104
|
+
'tbsCertificate parse failed');
|
|
105
|
+
}
|
|
106
|
+
let idx = 0;
|
|
107
|
+
let version = 1;
|
|
108
|
+
if (tbs[idx] && tbs[idx].tag === 0xA0) { // [0] EXPLICIT version
|
|
109
|
+
const v = a1.parseOne(tbs[idx].value, 0);
|
|
110
|
+
if (v && v.value && v.value.length) version = v.value[0] + 1;
|
|
111
|
+
idx++;
|
|
112
|
+
}
|
|
113
|
+
const serialNode = tbs[idx++];
|
|
114
|
+
idx++;
|
|
115
|
+
const issuerNode = tbs[idx++];
|
|
116
|
+
const validityNode = tbs[idx++];
|
|
117
|
+
const subjectNode = tbs[idx++];
|
|
118
|
+
const spkiNode = tbs[idx];
|
|
119
|
+
const validity = a1.parseChildren(validityNode.value);
|
|
120
|
+
const sigAlgChildren = a1.parseChildren(cert[1].value);
|
|
121
|
+
const sigAlgOid = sigAlgChildren && sigAlgChildren[0]
|
|
122
|
+
? a1.readOid(sigAlgChildren[0]) : null;
|
|
123
|
+
const spki = a1.parseChildren(spkiNode.value);
|
|
124
|
+
const keyAlgChildren = spki && spki[0]
|
|
125
|
+
? a1.parseChildren(spki[0].value) : null;
|
|
126
|
+
const keyAlgOid = keyAlgChildren && keyAlgChildren[0]
|
|
127
|
+
? a1.readOid(keyAlgChildren[0]) : null;
|
|
128
|
+
return {
|
|
129
|
+
version,
|
|
130
|
+
serialNumber: serialNode ? serialNode.value : null,
|
|
131
|
+
issuer: _renderName(issuerNode.value, a1),
|
|
132
|
+
subject: _renderName(subjectNode.value, a1),
|
|
133
|
+
notBefore: validity ? _parseUtcOrGeneralizedTime(validity[0]) : null,
|
|
134
|
+
notAfter: validity ? _parseUtcOrGeneralizedTime(validity[1]) : null,
|
|
135
|
+
publicKey: spki && spki[1] ? spki[1].value : null,
|
|
136
|
+
keyAlgorithm: KEY_ALG_OIDS[keyAlgOid] || keyAlgOid,
|
|
137
|
+
sigAlgorithm: SIG_ALG_OIDS[sigAlgOid] || sigAlgOid,
|
|
138
|
+
raw: der
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function extractCertsFromCms(blob, asn1Mod, opts) {
|
|
143
|
+
const a1 = asn1Mod || asn1;
|
|
144
|
+
if (!(blob instanceof Uint8Array)) {
|
|
145
|
+
throw new ParseError('pdf/cert/bad-input',
|
|
146
|
+
'extractCertsFromCms expects a Uint8Array');
|
|
147
|
+
}
|
|
148
|
+
if (!a1 || typeof a1.parseOne !== 'function') {
|
|
149
|
+
throw new EncryptionError('pdf/cert/missing-fw',
|
|
150
|
+
'extractCertsFromCms requires the fw asn1 module');
|
|
151
|
+
}
|
|
152
|
+
const warnings = opts && Array.isArray(opts.warnings) ? opts.warnings : null;
|
|
153
|
+
const ci = a1.parseOne(blob, 0);
|
|
154
|
+
if (!ci) return [];
|
|
155
|
+
const ciKids = a1.parseChildren(ci.value);
|
|
156
|
+
if (!ciKids || ciKids.length < 2) return [];
|
|
157
|
+
const sdNode = a1.parseOne(ciKids[1].value, 0);
|
|
158
|
+
if (!sdNode) return [];
|
|
159
|
+
const sd = a1.parseChildren(sdNode.value);
|
|
160
|
+
if (!sd) return [];
|
|
161
|
+
let certsField = null;
|
|
162
|
+
for (let i = 0; i < sd.length; i++) {
|
|
163
|
+
if (sd[i].tag === 0xA0) { certsField = sd[i]; break; }
|
|
164
|
+
}
|
|
165
|
+
if (!certsField) return [];
|
|
166
|
+
const certs = a1.parseChildren(certsField.value);
|
|
167
|
+
if (!certs) return [];
|
|
168
|
+
const out = [];
|
|
169
|
+
for (let i = 0; i < certs.length; i++) {
|
|
170
|
+
if (certs[i].tag !== 0x30) continue;
|
|
171
|
+
const der = _reconstructDer(certs[i], certsField.value);
|
|
172
|
+
try {
|
|
173
|
+
out.push(parseCertificate(der, a1));
|
|
174
|
+
} catch (e) {
|
|
175
|
+
if (warnings) {
|
|
176
|
+
warnings.push({
|
|
177
|
+
code: e && e.code ? e.code : 'pdf/cert/malformed',
|
|
178
|
+
message: 'skipped malformed cert at index ' + i
|
|
179
|
+
+ ': ' + (e && e.message),
|
|
180
|
+
context: { index: i }
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function extractCertFromPem(pemString, pemMod, asn1Mod) {
|
|
189
|
+
const pm = pemMod || pem;
|
|
190
|
+
const a1 = asn1Mod || asn1;
|
|
191
|
+
if (typeof pemString !== 'string') {
|
|
192
|
+
throw new ParseError('pdf/cert/bad-pem-input',
|
|
193
|
+
'extractCertFromPem expects a string');
|
|
194
|
+
}
|
|
195
|
+
if (!pm || typeof pm.decode !== 'function') {
|
|
196
|
+
throw new EncryptionError('pdf/cert/missing-pem',
|
|
197
|
+
'extractCertFromPem requires the fw pem module');
|
|
198
|
+
}
|
|
199
|
+
const dec = pm.decode(pemString);
|
|
200
|
+
if (!dec || !dec.bytes) {
|
|
201
|
+
throw new ParseError('pdf/cert/pem-decode-failed',
|
|
202
|
+
'PEM decode failed');
|
|
203
|
+
}
|
|
204
|
+
return parseCertificate(dec.bytes, a1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function findIssuer(cert, candidates) {
|
|
208
|
+
if (!cert || !Array.isArray(candidates)) return null;
|
|
209
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
210
|
+
if (candidates[i] === cert) continue;
|
|
211
|
+
if (candidates[i].subject === cert.issuer) return candidates[i];
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function validateChainOrder(chain) {
|
|
217
|
+
const errs = [];
|
|
218
|
+
if (!Array.isArray(chain) || chain.length === 0) {
|
|
219
|
+
errs.push({ code: 'pdf/cert/empty-chain',
|
|
220
|
+
message: 'chain must be a non-empty array' });
|
|
221
|
+
return { valid: false, errors: errs };
|
|
222
|
+
}
|
|
223
|
+
for (let i = 0; i < chain.length - 1; i++) {
|
|
224
|
+
if (chain[i].issuer !== chain[i + 1].subject) {
|
|
225
|
+
errs.push({
|
|
226
|
+
code: 'pdf/cert/chain-break',
|
|
227
|
+
message: 'cert[' + i + '].issuer != cert['
|
|
228
|
+
+ (i + 1) + '].subject',
|
|
229
|
+
context: {
|
|
230
|
+
issuer: chain[i].issuer,
|
|
231
|
+
subject: chain[i + 1].subject
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { valid: errs.length === 0, errors: errs };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
extractCertsFromCms,
|
|
241
|
+
extractCertFromPem,
|
|
242
|
+
parseCertificate,
|
|
243
|
+
findIssuer,
|
|
244
|
+
validateChainOrder
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
};
|
package/src/sig/dss.js
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
// Copyright (c) 2026 AwaCloud SAS
|
|
2
|
+
// Author: Matthieu Bouilloux
|
|
3
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
4
|
+
// Dual-licensed; see the NOTICE file for licensing and any additional terms.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @fileoverview PAdES-LT Document Security Store builder — produces
|
|
8
|
+
* the indirect objects (certs / OCSPs / CRLs / DSS dict / VRI dict)
|
|
9
|
+
* that a level-LT signer appends via the incremental writer to the
|
|
10
|
+
* already-signed (level B/T) PDF bytes.
|
|
11
|
+
*
|
|
12
|
+
* Spec : ETSI EN 319 142-1 V1.1.1 §5.4, ISO 32000-2:2020 §12.8.4.3.
|
|
13
|
+
*
|
|
14
|
+
* DSS ::= <<
|
|
15
|
+
* /Type /DSS
|
|
16
|
+
* /Certs [N₁ 0 R N₂ 0 R …] -- optional
|
|
17
|
+
* /OCSPs [Nk 0 R …] -- optional
|
|
18
|
+
* /CRLs [Nj 0 R …] -- optional
|
|
19
|
+
* /VRI << /<sigHashHex> << /Cert [...] /OCSP [...] /CRL [...]
|
|
20
|
+
* /TU? (M:...) /TS? Nₜ 0 R >> … >>
|
|
21
|
+
* >>
|
|
22
|
+
*
|
|
23
|
+
* Each referenced cert/OCSP/CRL is an indirect *stream* whose body is
|
|
24
|
+
* the raw DER bytes and whose dict carries the `/Type /CertVal`,
|
|
25
|
+
* `/OCSPVal` or `/CRLVal` marker (Adobe extension, widely deployed).
|
|
26
|
+
*
|
|
27
|
+
* The factory does NOT touch the byte buffer. It produces a list of
|
|
28
|
+
* `{ num, gen, value }` updates compatible with
|
|
29
|
+
* `pdfIncrementalWriter.appendIncremental`, plus the DSS object number
|
|
30
|
+
* so the caller can also append an updated Catalog carrying
|
|
31
|
+
* `/DSS dssNum 0 R`.
|
|
32
|
+
*
|
|
33
|
+
* @module pdf/sig/dss
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Module factory.
|
|
38
|
+
*/
|
|
39
|
+
import { pdfErrors } from '../errors.js';
|
|
40
|
+
import { pdfSha1 } from './sha1.js';
|
|
41
|
+
import { bitArray } from '@awacloud/fw/crypto/utils/bitArray.js';
|
|
42
|
+
|
|
43
|
+
export const pdfDssBuilder = {
|
|
44
|
+
name: 'pdfDssBuilder',
|
|
45
|
+
dependencies: ['pdfErrors', 'pdfSha1', 'bitArray'],
|
|
46
|
+
deps: [pdfErrors, pdfSha1, bitArray],
|
|
47
|
+
factory(errors, sha1Mod, bitArray) {
|
|
48
|
+
const { ContractError } = errors;
|
|
49
|
+
|
|
50
|
+
// ── SHA-1 hoisted to fw (FIPS 180-4 §6.1) ────────────────────
|
|
51
|
+
// Used solely for VRI key derivation per ISO 32000-2 §12.8.4.3
|
|
52
|
+
// (the VRI dict keys are the uppercase hex SHA-1 of each
|
|
53
|
+
// signature value bytes). SHA-1 is deprecated for general
|
|
54
|
+
// cryptographic use but PDF VRI keys still mandate it.
|
|
55
|
+
function _sha1(bytes) {
|
|
56
|
+
const digestBa = sha1Mod.hash(bitArray.ui8_to_ba(bytes));
|
|
57
|
+
return bitArray.ba_to_ui8(digestBa);
|
|
58
|
+
}
|
|
59
|
+
// PDF date string per ISO 32000-2:2020 §7.9.4 :
|
|
60
|
+
// (D:YYYYMMDDHHmmSS+HH'mm') — used here for VRI `TU` entries
|
|
61
|
+
// (ISO 32000-2 §12.8.4.3 : the time at which validation data
|
|
62
|
+
// was obtained). Always emitted in UTC (`+00'00'`) so the
|
|
63
|
+
// value is stable across runs and machine timezones.
|
|
64
|
+
function _pdfDate(date) {
|
|
65
|
+
const d = date instanceof Date ? date : new Date();
|
|
66
|
+
function p2(n) { return (n < 10 ? '0' : '') + n; }
|
|
67
|
+
return '(D:'
|
|
68
|
+
+ d.getUTCFullYear()
|
|
69
|
+
+ p2(d.getUTCMonth() + 1)
|
|
70
|
+
+ p2(d.getUTCDate())
|
|
71
|
+
+ p2(d.getUTCHours())
|
|
72
|
+
+ p2(d.getUTCMinutes())
|
|
73
|
+
+ p2(d.getUTCSeconds())
|
|
74
|
+
+ "+00'00')";
|
|
75
|
+
}
|
|
76
|
+
function _toHexUpper(bytes) {
|
|
77
|
+
let s = '';
|
|
78
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
79
|
+
const b = bytes[i];
|
|
80
|
+
s += (b < 16 ? '0' : '') + b.toString(16).toUpperCase();
|
|
81
|
+
}
|
|
82
|
+
return s;
|
|
83
|
+
}
|
|
84
|
+
function _scanSignatureContentsHex(bytes) {
|
|
85
|
+
// Find every `/Type /Sig` (incl. /DocTimeStamp) and extract
|
|
86
|
+
// the `/Contents <…hex…>` literal. Returns an array of
|
|
87
|
+
// uppercase hex strings (the raw hex from /Contents).
|
|
88
|
+
let s = '';
|
|
89
|
+
const CHUNK = 0x8000;
|
|
90
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
91
|
+
s += String.fromCharCode.apply(null,
|
|
92
|
+
bytes.subarray(i, Math.min(i + CHUNK, bytes.length)));
|
|
93
|
+
}
|
|
94
|
+
const out = [];
|
|
95
|
+
const re = /(\d+)\s+(\d+)\s+obj\s*<<([\s\S]*?)>>\s*endobj/g;
|
|
96
|
+
let m;
|
|
97
|
+
while ((m = re.exec(s)) !== null) {
|
|
98
|
+
const body = m[3];
|
|
99
|
+
if (!/\/Type\s*\/(?:Sig|DocTimeStamp)\b/.test(body)) continue;
|
|
100
|
+
const cm = body.match(/\/Contents\s*<([0-9A-Fa-f]*)>/);
|
|
101
|
+
if (!cm) continue;
|
|
102
|
+
out.push(cm[1]);
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
function _refObj(num, gen) {
|
|
109
|
+
return { type: 'ref', num: num | 0, gen: gen | 0 };
|
|
110
|
+
}
|
|
111
|
+
function _intObj(v) { return { type: 'int', value: v | 0 }; }
|
|
112
|
+
function _nameObj(v) { return { type: 'name', value: String(v) }; }
|
|
113
|
+
function _strHexObj(s) {
|
|
114
|
+
// s is an ASCII hex string (e.g. SHA-1 of signature value).
|
|
115
|
+
// Emit it as a regular PDF string ; PDF VRI keys are NAMES
|
|
116
|
+
// composed of the hex characters per ISO 32000-2 §12.8.4.3.
|
|
117
|
+
return { type: 'name', value: String(s) };
|
|
118
|
+
}
|
|
119
|
+
function _arrayObj(items) { return { type: 'array', items }; }
|
|
120
|
+
function _dictObj(entries) { return { type: 'dict', entries }; }
|
|
121
|
+
function _streamObj(rawBytes, typeName) {
|
|
122
|
+
// Stream dict carries /Type — the writer auto-adds /Length.
|
|
123
|
+
return {
|
|
124
|
+
type: 'stream',
|
|
125
|
+
dict: _dictObj({
|
|
126
|
+
Type: _nameObj(typeName)
|
|
127
|
+
}),
|
|
128
|
+
raw: rawBytes
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Build DSS + supporting streams for an incremental append.
|
|
134
|
+
*
|
|
135
|
+
* @param {Object} args
|
|
136
|
+
* @param {Uint8Array[]} [args.certs] Cert DER bytes — required for
|
|
137
|
+
* a useful DSS (at least one signer cert + chain).
|
|
138
|
+
* @param {Uint8Array[]} [args.ocsps] OCSP response DER bytes.
|
|
139
|
+
* @param {Uint8Array[]} [args.crls] CRL DER bytes.
|
|
140
|
+
* @param {Object<string, Object>} [args.vri] Map of signature-hash
|
|
141
|
+
* hex → { certs?, ocsps?, crls?, tu?, ts? } (each list is an
|
|
142
|
+
* array of *indices* into the parent certs/ocsps/crls lists, or
|
|
143
|
+
* raw DER bytes; ts? is a TimeStampToken DER as a stream).
|
|
144
|
+
* @param {number} args.startNum First free object number to
|
|
145
|
+
* allocate (subsequent objects are sequential).
|
|
146
|
+
* @returns {{ updates: Array, dssNum: number, lastNum: number,
|
|
147
|
+
* certNums: number[], ocspNums: number[],
|
|
148
|
+
* crlNums: number[] }}
|
|
149
|
+
*/
|
|
150
|
+
function buildDss(args) {
|
|
151
|
+
args = args || {};
|
|
152
|
+
const startNum = args.startNum | 0;
|
|
153
|
+
if (!Number.isFinite(startNum) || startNum < 2) {
|
|
154
|
+
throw new ContractError('pdf/dss/bad-startNum',
|
|
155
|
+
'buildDss requires opts.startNum >= 2',
|
|
156
|
+
{ context: { startNum } });
|
|
157
|
+
}
|
|
158
|
+
const certs = Array.isArray(args.certs) ? args.certs : [];
|
|
159
|
+
const ocsps = Array.isArray(args.ocsps) ? args.ocsps : [];
|
|
160
|
+
const crls = Array.isArray(args.crls) ? args.crls : [];
|
|
161
|
+
|
|
162
|
+
for (const c of certs) {
|
|
163
|
+
if (!(c instanceof Uint8Array)) {
|
|
164
|
+
throw new ContractError('pdf/dss/bad-cert',
|
|
165
|
+
'each cert must be a Uint8Array (DER)');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const o of ocsps) {
|
|
169
|
+
if (!(o instanceof Uint8Array)) {
|
|
170
|
+
throw new ContractError('pdf/dss/bad-ocsp',
|
|
171
|
+
'each ocsp must be a Uint8Array (DER)');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const c of crls) {
|
|
175
|
+
if (!(c instanceof Uint8Array)) {
|
|
176
|
+
throw new ContractError('pdf/dss/bad-crl',
|
|
177
|
+
'each crl must be a Uint8Array (DER)');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const updates = [];
|
|
182
|
+
let n = startNum;
|
|
183
|
+
|
|
184
|
+
const certNums = [];
|
|
185
|
+
for (const der of certs) {
|
|
186
|
+
certNums.push(n);
|
|
187
|
+
updates.push({ num: n, gen: 0, value: _streamObj(der, 'CertVal') });
|
|
188
|
+
n++;
|
|
189
|
+
}
|
|
190
|
+
const ocspNums = [];
|
|
191
|
+
for (const der of ocsps) {
|
|
192
|
+
ocspNums.push(n);
|
|
193
|
+
updates.push({ num: n, gen: 0, value: _streamObj(der, 'OCSPVal') });
|
|
194
|
+
n++;
|
|
195
|
+
}
|
|
196
|
+
const crlNums = [];
|
|
197
|
+
for (const der of crls) {
|
|
198
|
+
crlNums.push(n);
|
|
199
|
+
updates.push({ num: n, gen: 0, value: _streamObj(der, 'CRLVal') });
|
|
200
|
+
n++;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Optional VRI auto-population : scan parent PDF bytes for
|
|
204
|
+
// every signature, derive the SHA-1(/Contents hex) key, and
|
|
205
|
+
// synthesise a VRI entry referencing all certs/ocsps/crls.
|
|
206
|
+
let vriArg = args.vri;
|
|
207
|
+
if (args.autoVri && args.parentBytes instanceof Uint8Array) {
|
|
208
|
+
const sigContents = _scanSignatureContentsHex(args.parentBytes);
|
|
209
|
+
const auto = vriArg && typeof vriArg === 'object'
|
|
210
|
+
? Object.assign({}, vriArg) : {};
|
|
211
|
+
const allCertIdx = certs.map((_, i) => i);
|
|
212
|
+
const allOcspIdx = ocsps.map((_, i) => i);
|
|
213
|
+
const allCrlIdx = crls.map((_, i) => i);
|
|
214
|
+
// VRI `TU` = time at which validation data was obtained
|
|
215
|
+
// (ISO 32000-2 §12.8.4.3). PDF date string per §7.9.4.
|
|
216
|
+
// `args.vriTime` overrides the default of `new Date()` so
|
|
217
|
+
// tests can pin a deterministic value.
|
|
218
|
+
const tuDate = args.vriTime instanceof Date
|
|
219
|
+
? args.vriTime : new Date();
|
|
220
|
+
const tuPdfStr = _pdfDate(tuDate);
|
|
221
|
+
for (const hex of sigContents) {
|
|
222
|
+
// SHA-1 of the *raw signature value bytes* (the binary
|
|
223
|
+
// decoded from the /Contents hex literal), uppercase hex.
|
|
224
|
+
const sigBytes = new Uint8Array(hex.length >>> 1);
|
|
225
|
+
for (let i = 0; i < sigBytes.length; i++) {
|
|
226
|
+
sigBytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
227
|
+
}
|
|
228
|
+
const key = _toHexUpper(_sha1(sigBytes));
|
|
229
|
+
if (auto[key]) continue;
|
|
230
|
+
const entry = { tu: tuPdfStr };
|
|
231
|
+
if (allCertIdx.length) entry.certs = allCertIdx;
|
|
232
|
+
if (allOcspIdx.length) entry.ocsps = allOcspIdx;
|
|
233
|
+
if (allCrlIdx.length) entry.crls = allCrlIdx;
|
|
234
|
+
auto[key] = entry;
|
|
235
|
+
}
|
|
236
|
+
vriArg = auto;
|
|
237
|
+
}
|
|
238
|
+
let vriEntries = null;
|
|
239
|
+
if (vriArg && typeof vriArg === 'object') {
|
|
240
|
+
vriEntries = {};
|
|
241
|
+
for (const k of Object.keys(vriArg)) {
|
|
242
|
+
const v = vriArg[k] || {};
|
|
243
|
+
const e = {};
|
|
244
|
+
function _refsFrom(spec, parentNums) {
|
|
245
|
+
if (!Array.isArray(spec)) return null;
|
|
246
|
+
const refs = [];
|
|
247
|
+
for (const it of spec) {
|
|
248
|
+
if (typeof it === 'number') {
|
|
249
|
+
if (it < 0 || it >= parentNums.length) {
|
|
250
|
+
throw new ContractError('pdf/dss/vri-bad-index',
|
|
251
|
+
'VRI index out of bounds',
|
|
252
|
+
{ context: { index: it } });
|
|
253
|
+
}
|
|
254
|
+
refs.push(_refObj(parentNums[it], 0));
|
|
255
|
+
} else if (it instanceof Uint8Array) {
|
|
256
|
+
// Promote raw DER to a new stream.
|
|
257
|
+
const newNum = n++;
|
|
258
|
+
updates.push({ num: newNum, gen: 0,
|
|
259
|
+
value: _streamObj(it, 'CertVal') });
|
|
260
|
+
refs.push(_refObj(newNum, 0));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return _arrayObj(refs);
|
|
264
|
+
}
|
|
265
|
+
const c = _refsFrom(v.certs, certNums);
|
|
266
|
+
const o = _refsFrom(v.ocsps, ocspNums);
|
|
267
|
+
const r = _refsFrom(v.crls, crlNums);
|
|
268
|
+
if (c) e.Cert = c;
|
|
269
|
+
if (o) e.OCSP = o;
|
|
270
|
+
if (r) e.CRL = r;
|
|
271
|
+
if (typeof v.tu === 'string') {
|
|
272
|
+
e.TU = { type: 'string',
|
|
273
|
+
value: new TextEncoder().encode(v.tu) };
|
|
274
|
+
}
|
|
275
|
+
if (v.ts instanceof Uint8Array) {
|
|
276
|
+
const newNum = n++;
|
|
277
|
+
updates.push({ num: newNum, gen: 0,
|
|
278
|
+
value: _streamObj(v.ts, 'TS') });
|
|
279
|
+
e.TS = _refObj(newNum, 0);
|
|
280
|
+
}
|
|
281
|
+
vriEntries[k] = _dictObj(e);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const dssEntries = { Type: _nameObj('DSS') };
|
|
286
|
+
if (certNums.length) {
|
|
287
|
+
dssEntries.Certs = _arrayObj(certNums.map(num => _refObj(num, 0)));
|
|
288
|
+
}
|
|
289
|
+
if (ocspNums.length) {
|
|
290
|
+
dssEntries.OCSPs = _arrayObj(ocspNums.map(num => _refObj(num, 0)));
|
|
291
|
+
}
|
|
292
|
+
if (crlNums.length) {
|
|
293
|
+
dssEntries.CRLs = _arrayObj(crlNums.map(num => _refObj(num, 0)));
|
|
294
|
+
}
|
|
295
|
+
if (vriEntries) {
|
|
296
|
+
dssEntries.VRI = _dictObj(vriEntries);
|
|
297
|
+
}
|
|
298
|
+
const dssNum = n++;
|
|
299
|
+
updates.push({ num: dssNum, gen: 0, value: _dictObj(dssEntries) });
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
updates,
|
|
303
|
+
dssNum,
|
|
304
|
+
lastNum: n - 1,
|
|
305
|
+
certNums, ocspNums, crlNums
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
buildDss,
|
|
311
|
+
// Exposed for test wiring + helpers.
|
|
312
|
+
_refObj, _intObj, _nameObj, _strHexObj,
|
|
313
|
+
_arrayObj, _dictObj, _streamObj,
|
|
314
|
+
_sha1, _toHexUpper, _scanSignatureContentsHex, _pdfDate
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
};
|