Skip to content

Commit f704f2e

Browse files
Merge pull request #21652 from Snuffleupagus/signatures-async-helpers
Update the signatures-helpers to actually handle `MissingDataException`s
2 parents 01ba4c4 + c9eaf13 commit f704f2e

2 files changed

Lines changed: 99 additions & 52 deletions

File tree

src/core/document.js

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,7 +1996,7 @@ class PDFDocument {
19961996
return shadow(this, "fieldObjects", promise);
19971997
}
19981998

1999-
#collectSignatureFields(fields, out, visitedRefs) {
1999+
async #collectSignatureFields(fields, out, visitedRefs) {
20002000
if (!Array.isArray(fields)) {
20012001
return;
20022002
}
@@ -2007,14 +2007,18 @@ class PDFDocument {
20072007
}
20082008
visitedRefs.put(fieldRef);
20092009
}
2010-
const field = this.xref.fetchIfRef(fieldRef);
2010+
const field = await this.xref.fetchIfRefAsync(fieldRef);
20112011
if (!(field instanceof Dict)) {
20122012
continue;
20132013
}
2014-
if (isName(field.get("FT"), "Sig")) {
2015-
const sigDict = this.xref.fetchIfRef(field.get("V"));
2014+
if (isName(await field.getAsync("FT"), "Sig")) {
2015+
const sigDict = await field.getAsync("V");
20162016
if (sigDict instanceof Dict) {
2017-
const parsed = this.#parseSignatureDict(field, sigDict, fieldRef);
2017+
const parsed = await this.#parseSignatureDict(
2018+
field,
2019+
sigDict,
2020+
fieldRef
2021+
);
20182022
if (parsed) {
20192023
out.push(parsed);
20202024
}
@@ -2023,7 +2027,11 @@ class PDFDocument {
20232027
if (field.has("Kids")) {
20242028
// A terminal field can have Widget annotations as children, so its
20252029
// own signature must be collected before walking the field tree.
2026-
this.#collectSignatureFields(field.get("Kids"), out, visitedRefs);
2030+
await this.#collectSignatureFields(
2031+
await field.getAsync("Kids"),
2032+
out,
2033+
visitedRefs
2034+
);
20272035
}
20282036
}
20292037
}
@@ -2070,40 +2078,22 @@ class PDFDocument {
20702078
return true;
20712079
}
20722080

2073-
#parseSignatureDict(field, sigDict, fieldRef) {
2074-
const byteRange = sigDict.get("ByteRange");
2081+
async #parseSignatureDict(field, sigDict, fieldRef) {
2082+
const byteRange = await sigDict.getAsync("ByteRange");
20752083
if (
20762084
!Array.isArray(byteRange) ||
20772085
byteRange.length !== 4 ||
20782086
byteRange.some(n => !Number.isInteger(n) || n < 0)
20792087
) {
20802088
return null;
20812089
}
2082-
const contents = sigDict.get("Contents");
2083-
if (typeof contents !== "string" || contents.length === 0) {
2084-
return null;
2085-
}
2086-
2087-
const filterName = sigDict.get("Filter");
2088-
const filter = filterName instanceof Name ? filterName.name : null;
2089-
const subFilterName = sigDict.get("SubFilter");
2090-
const subFilter = subFilterName instanceof Name ? subFilterName.name : null;
2091-
2092-
let signatureType = null;
2093-
if (subFilter === "adbe.pkcs7.detached") {
2094-
signatureType = 0;
2095-
} else if (subFilter === "adbe.pkcs7.sha1") {
2096-
signatureType = 1;
2097-
}
2098-
20992090
// Slice the two ByteRange byte spans out of the underlying PDF stream.
21002091
// ByteRange = [a, b, c, d] means signed bytes are [a..a+b] and [c..c+d];
21012092
// the gap covers the /Contents hex blob itself.
21022093
const [a, b, c, d] = byteRange;
2103-
const stream = this.stream;
21042094
// `/ByteRange` offsets are absolute, so compare against `stream.end`
21052095
// (raw buffer end), not `stream.length` (post-`moveStart` payload).
2106-
const fileLength = stream.end || 0;
2096+
const fileLength = this.stream.end || 0;
21072097
// Reject signatures whose /ByteRange is structurally implausible: it
21082098
// must start at the file head, define a non-empty first span, leave
21092099
// room for the /Contents blob between the two spans, and fit within
@@ -2118,22 +2108,46 @@ class PDFDocument {
21182108
) {
21192109
return null;
21202110
}
2121-
const pkcs7 = stringToBytes(contents);
21222111

2123-
const t = field.get("T");
2124-
const fieldName = typeof t === "string" ? stringToPDFString(t) : "";
2125-
const name = sigDict.get("Name");
2126-
const reason = sigDict.get("Reason");
2127-
const location = sigDict.get("Location");
2128-
const contactInfo = sigDict.get("ContactInfo");
2129-
const m = sigDict.get("M");
2112+
const contents = await sigDict.getAsync("Contents");
2113+
if (typeof contents !== "string" || contents.length === 0) {
2114+
return null;
2115+
}
2116+
2117+
const [
2118+
filterName,
2119+
subFilterName,
2120+
t,
2121+
name,
2122+
reason,
2123+
location,
2124+
contactInfo,
2125+
m,
2126+
] = await Promise.all([
2127+
sigDict.getAsync("Filter"),
2128+
sigDict.getAsync("SubFilter"),
2129+
field.getAsync("T"),
2130+
sigDict.getAsync("Name"),
2131+
sigDict.getAsync("Reason"),
2132+
sigDict.getAsync("Location"),
2133+
sigDict.getAsync("ContactInfo"),
2134+
sigDict.getAsync("M"),
2135+
]);
2136+
2137+
const filter = filterName instanceof Name ? filterName.name : null,
2138+
subFilter = subFilterName instanceof Name ? subFilterName.name : null;
21302139

2140+
let signatureType = null;
2141+
if (subFilter === "adbe.pkcs7.detached") {
2142+
signatureType = 0;
2143+
} else if (subFilter === "adbe.pkcs7.sha1") {
2144+
signatureType = 1;
2145+
}
21312146
const refKey = fieldRef instanceof Ref ? fieldRef.toString() : "inline";
2132-
const id = `${refKey}:${a}-${b}-${c}-${d}`;
21332147

21342148
return {
2135-
id,
2136-
fieldName,
2149+
id: `${refKey}:${a}-${b}-${c}-${d}`,
2150+
fieldName: typeof t === "string" ? stringToPDFString(t) : "",
21372151
signerName: typeof name === "string" ? stringToPDFString(name) : null,
21382152
reason: typeof reason === "string" ? stringToPDFString(reason) : null,
21392153
location:
@@ -2145,7 +2159,7 @@ class PDFDocument {
21452159
subFilter,
21462160
signatureType,
21472161
byteRange,
2148-
pkcs7,
2162+
pkcs7: stringToBytes(contents),
21492163
revisionIndex: 0,
21502164
parentId: null,
21512165
};
@@ -2165,7 +2179,7 @@ class PDFDocument {
21652179
const fields = annotationGlobals.acroForm.get("Fields");
21662180

21672181
const collected = [];
2168-
this.#collectSignatureFields(fields, collected, new RefSet());
2182+
await this.#collectSignatureFields(fields, collected, new RefSet());
21692183

21702184
await Promise.all(
21712185
collected.map(async signature => {

test/unit/document_spec.js

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,10 @@ describe("document", function () {
322322
{ ref: sigRef, data: sigDict },
323323
{ ref: fieldRef, data: fieldDict },
324324
]);
325+
acroForm.assignXref(xref);
326+
sigDict.assignXref(xref);
327+
fieldDict.assignXref(xref);
328+
325329
acroForm.set("Fields", [fieldRef]);
326330

327331
const pdfDocument = getDocument(acroForm, xref);
@@ -362,6 +366,10 @@ describe("document", function () {
362366
{ ref: sigRef, data: sigDict },
363367
{ ref: fieldRef, data: fieldDict },
364368
]);
369+
acroForm.assignXref(xref);
370+
sigDict.assignXref(xref);
371+
fieldDict.assignXref(xref);
372+
365373
acroForm.set("Fields", [fieldRef]);
366374

367375
const documentStream = new StringStream(
@@ -386,6 +394,10 @@ describe("document", function () {
386394
{ ref: sigRef, data: sigDict },
387395
{ ref: fieldRef, data: fieldDict },
388396
]);
397+
acroForm.assignXref(xref);
398+
sigDict.assignXref(xref);
399+
fieldDict.assignXref(xref);
400+
389401
acroForm.set("Fields", [fieldRef]);
390402

391403
const documentStream = new StringStream(
@@ -418,6 +430,11 @@ describe("document", function () {
418430
{ ref: sigFieldRef, data: sigField },
419431
{ ref: containerRef, data: container },
420432
]);
433+
acroForm.assignXref(xref);
434+
sigDict.assignXref(xref);
435+
sigField.assignXref(xref);
436+
container.assignXref(xref);
437+
421438
acroForm.set("Fields", [containerRef]);
422439

423440
const pdfDocument = getDocument(acroForm, xref);
@@ -450,6 +467,11 @@ describe("document", function () {
450467
{ ref: sigFieldRef, data: sigField },
451468
{ ref: widgetRef, data: widget },
452469
]);
470+
acroForm.assignXref(xref);
471+
sigDict.assignXref(xref);
472+
sigField.assignXref(xref);
473+
widget.assignXref(xref);
474+
453475
acroForm.set("Fields", [sigFieldRef]);
454476

455477
const pdfDocument = getDocument(acroForm, xref);
@@ -472,6 +494,10 @@ describe("document", function () {
472494
{ ref: sigRef, data: sigDict },
473495
{ ref: fieldRef, data: fieldDict },
474496
]);
497+
acroForm.assignXref(xref);
498+
sigDict.assignXref(xref);
499+
fieldDict.assignXref(xref);
500+
475501
acroForm.set("Fields", [fieldRef]);
476502

477503
const pdfDocument = getDocument(acroForm, xref);
@@ -498,18 +524,21 @@ describe("document", function () {
498524
name: "Inner",
499525
});
500526

527+
const outerField = makeSigField({ T: "outer", sigRef: outerSigRef });
528+
const innerField = makeSigField({ T: "inner", sigRef: innerSigRef });
529+
501530
const xref = new XRefMock([
502531
{ ref: outerSigRef, data: outerSig },
503-
{
504-
ref: outerFieldRef,
505-
data: makeSigField({ T: "outer", sigRef: outerSigRef }),
506-
},
532+
{ ref: outerFieldRef, data: outerField },
507533
{ ref: innerSigRef, data: innerSig },
508-
{
509-
ref: innerFieldRef,
510-
data: makeSigField({ T: "inner", sigRef: innerSigRef }),
511-
},
534+
{ ref: innerFieldRef, data: innerField },
512535
]);
536+
acroForm.assignXref(xref);
537+
outerSig.assignXref(xref);
538+
innerSig.assignXref(xref);
539+
outerField.assignXref(xref);
540+
innerField.assignXref(xref);
541+
513542
acroForm.set("Fields", [outerFieldRef, innerFieldRef]);
514543

515544
const pdfDocument = getDocument(acroForm, xref);
@@ -535,14 +564,18 @@ describe("document", function () {
535564
byteRange: [0, 10, 20, 30],
536565
subFilter,
537566
});
567+
const sigField = makeSigField({ T: "sig", sigRef });
568+
538569
const xref = new XRefMock([
539570
{ ref: sigRef, data: sigDict },
540-
{
541-
ref: fieldRef,
542-
data: makeSigField({ T: "sig", sigRef }),
543-
},
571+
{ ref: fieldRef, data: sigField },
544572
]);
573+
acroForm.assignXref(xref);
574+
sigDict.assignXref(xref);
575+
sigField.assignXref(xref);
576+
545577
acroForm.set("Fields", [fieldRef]);
578+
546579
const pdfDocument = getDocument(acroForm, xref);
547580
const [sig] = await pdfDocument.signatures;
548581
return sig.signatureType;

0 commit comments

Comments
 (0)