mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-07-30 10:57:21 +02:00
Merge pull request #21652 from Snuffleupagus/signatures-async-helpers
Update the signatures-helpers to actually handle `MissingDataException`s
This commit is contained in:
commit
f704f2ef02
@ -1996,7 +1996,7 @@ class PDFDocument {
|
|||||||
return shadow(this, "fieldObjects", promise);
|
return shadow(this, "fieldObjects", promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
#collectSignatureFields(fields, out, visitedRefs) {
|
async #collectSignatureFields(fields, out, visitedRefs) {
|
||||||
if (!Array.isArray(fields)) {
|
if (!Array.isArray(fields)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2007,14 +2007,18 @@ class PDFDocument {
|
|||||||
}
|
}
|
||||||
visitedRefs.put(fieldRef);
|
visitedRefs.put(fieldRef);
|
||||||
}
|
}
|
||||||
const field = this.xref.fetchIfRef(fieldRef);
|
const field = await this.xref.fetchIfRefAsync(fieldRef);
|
||||||
if (!(field instanceof Dict)) {
|
if (!(field instanceof Dict)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (isName(field.get("FT"), "Sig")) {
|
if (isName(await field.getAsync("FT"), "Sig")) {
|
||||||
const sigDict = this.xref.fetchIfRef(field.get("V"));
|
const sigDict = await field.getAsync("V");
|
||||||
if (sigDict instanceof Dict) {
|
if (sigDict instanceof Dict) {
|
||||||
const parsed = this.#parseSignatureDict(field, sigDict, fieldRef);
|
const parsed = await this.#parseSignatureDict(
|
||||||
|
field,
|
||||||
|
sigDict,
|
||||||
|
fieldRef
|
||||||
|
);
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
out.push(parsed);
|
out.push(parsed);
|
||||||
}
|
}
|
||||||
@ -2023,7 +2027,11 @@ class PDFDocument {
|
|||||||
if (field.has("Kids")) {
|
if (field.has("Kids")) {
|
||||||
// A terminal field can have Widget annotations as children, so its
|
// A terminal field can have Widget annotations as children, so its
|
||||||
// own signature must be collected before walking the field tree.
|
// own signature must be collected before walking the field tree.
|
||||||
this.#collectSignatureFields(field.get("Kids"), out, visitedRefs);
|
await this.#collectSignatureFields(
|
||||||
|
await field.getAsync("Kids"),
|
||||||
|
out,
|
||||||
|
visitedRefs
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2070,8 +2078,8 @@ class PDFDocument {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#parseSignatureDict(field, sigDict, fieldRef) {
|
async #parseSignatureDict(field, sigDict, fieldRef) {
|
||||||
const byteRange = sigDict.get("ByteRange");
|
const byteRange = await sigDict.getAsync("ByteRange");
|
||||||
if (
|
if (
|
||||||
!Array.isArray(byteRange) ||
|
!Array.isArray(byteRange) ||
|
||||||
byteRange.length !== 4 ||
|
byteRange.length !== 4 ||
|
||||||
@ -2079,31 +2087,13 @@ class PDFDocument {
|
|||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const contents = sigDict.get("Contents");
|
|
||||||
if (typeof contents !== "string" || contents.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filterName = sigDict.get("Filter");
|
|
||||||
const filter = filterName instanceof Name ? filterName.name : null;
|
|
||||||
const subFilterName = sigDict.get("SubFilter");
|
|
||||||
const subFilter = subFilterName instanceof Name ? subFilterName.name : null;
|
|
||||||
|
|
||||||
let signatureType = null;
|
|
||||||
if (subFilter === "adbe.pkcs7.detached") {
|
|
||||||
signatureType = 0;
|
|
||||||
} else if (subFilter === "adbe.pkcs7.sha1") {
|
|
||||||
signatureType = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice the two ByteRange byte spans out of the underlying PDF stream.
|
// Slice the two ByteRange byte spans out of the underlying PDF stream.
|
||||||
// ByteRange = [a, b, c, d] means signed bytes are [a..a+b] and [c..c+d];
|
// ByteRange = [a, b, c, d] means signed bytes are [a..a+b] and [c..c+d];
|
||||||
// the gap covers the /Contents hex blob itself.
|
// the gap covers the /Contents hex blob itself.
|
||||||
const [a, b, c, d] = byteRange;
|
const [a, b, c, d] = byteRange;
|
||||||
const stream = this.stream;
|
|
||||||
// `/ByteRange` offsets are absolute, so compare against `stream.end`
|
// `/ByteRange` offsets are absolute, so compare against `stream.end`
|
||||||
// (raw buffer end), not `stream.length` (post-`moveStart` payload).
|
// (raw buffer end), not `stream.length` (post-`moveStart` payload).
|
||||||
const fileLength = stream.end || 0;
|
const fileLength = this.stream.end || 0;
|
||||||
// Reject signatures whose /ByteRange is structurally implausible: it
|
// Reject signatures whose /ByteRange is structurally implausible: it
|
||||||
// must start at the file head, define a non-empty first span, leave
|
// must start at the file head, define a non-empty first span, leave
|
||||||
// room for the /Contents blob between the two spans, and fit within
|
// room for the /Contents blob between the two spans, and fit within
|
||||||
@ -2118,22 +2108,46 @@ class PDFDocument {
|
|||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const pkcs7 = stringToBytes(contents);
|
|
||||||
|
|
||||||
const t = field.get("T");
|
const contents = await sigDict.getAsync("Contents");
|
||||||
const fieldName = typeof t === "string" ? stringToPDFString(t) : "";
|
if (typeof contents !== "string" || contents.length === 0) {
|
||||||
const name = sigDict.get("Name");
|
return null;
|
||||||
const reason = sigDict.get("Reason");
|
}
|
||||||
const location = sigDict.get("Location");
|
|
||||||
const contactInfo = sigDict.get("ContactInfo");
|
|
||||||
const m = sigDict.get("M");
|
|
||||||
|
|
||||||
|
const [
|
||||||
|
filterName,
|
||||||
|
subFilterName,
|
||||||
|
t,
|
||||||
|
name,
|
||||||
|
reason,
|
||||||
|
location,
|
||||||
|
contactInfo,
|
||||||
|
m,
|
||||||
|
] = await Promise.all([
|
||||||
|
sigDict.getAsync("Filter"),
|
||||||
|
sigDict.getAsync("SubFilter"),
|
||||||
|
field.getAsync("T"),
|
||||||
|
sigDict.getAsync("Name"),
|
||||||
|
sigDict.getAsync("Reason"),
|
||||||
|
sigDict.getAsync("Location"),
|
||||||
|
sigDict.getAsync("ContactInfo"),
|
||||||
|
sigDict.getAsync("M"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const filter = filterName instanceof Name ? filterName.name : null,
|
||||||
|
subFilter = subFilterName instanceof Name ? subFilterName.name : null;
|
||||||
|
|
||||||
|
let signatureType = null;
|
||||||
|
if (subFilter === "adbe.pkcs7.detached") {
|
||||||
|
signatureType = 0;
|
||||||
|
} else if (subFilter === "adbe.pkcs7.sha1") {
|
||||||
|
signatureType = 1;
|
||||||
|
}
|
||||||
const refKey = fieldRef instanceof Ref ? fieldRef.toString() : "inline";
|
const refKey = fieldRef instanceof Ref ? fieldRef.toString() : "inline";
|
||||||
const id = `${refKey}:${a}-${b}-${c}-${d}`;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id: `${refKey}:${a}-${b}-${c}-${d}`,
|
||||||
fieldName,
|
fieldName: typeof t === "string" ? stringToPDFString(t) : "",
|
||||||
signerName: typeof name === "string" ? stringToPDFString(name) : null,
|
signerName: typeof name === "string" ? stringToPDFString(name) : null,
|
||||||
reason: typeof reason === "string" ? stringToPDFString(reason) : null,
|
reason: typeof reason === "string" ? stringToPDFString(reason) : null,
|
||||||
location:
|
location:
|
||||||
@ -2145,7 +2159,7 @@ class PDFDocument {
|
|||||||
subFilter,
|
subFilter,
|
||||||
signatureType,
|
signatureType,
|
||||||
byteRange,
|
byteRange,
|
||||||
pkcs7,
|
pkcs7: stringToBytes(contents),
|
||||||
revisionIndex: 0,
|
revisionIndex: 0,
|
||||||
parentId: null,
|
parentId: null,
|
||||||
};
|
};
|
||||||
@ -2165,7 +2179,7 @@ class PDFDocument {
|
|||||||
const fields = annotationGlobals.acroForm.get("Fields");
|
const fields = annotationGlobals.acroForm.get("Fields");
|
||||||
|
|
||||||
const collected = [];
|
const collected = [];
|
||||||
this.#collectSignatureFields(fields, collected, new RefSet());
|
await this.#collectSignatureFields(fields, collected, new RefSet());
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
collected.map(async signature => {
|
collected.map(async signature => {
|
||||||
|
|||||||
@ -322,6 +322,10 @@ describe("document", function () {
|
|||||||
{ ref: sigRef, data: sigDict },
|
{ ref: sigRef, data: sigDict },
|
||||||
{ ref: fieldRef, data: fieldDict },
|
{ ref: fieldRef, data: fieldDict },
|
||||||
]);
|
]);
|
||||||
|
acroForm.assignXref(xref);
|
||||||
|
sigDict.assignXref(xref);
|
||||||
|
fieldDict.assignXref(xref);
|
||||||
|
|
||||||
acroForm.set("Fields", [fieldRef]);
|
acroForm.set("Fields", [fieldRef]);
|
||||||
|
|
||||||
const pdfDocument = getDocument(acroForm, xref);
|
const pdfDocument = getDocument(acroForm, xref);
|
||||||
@ -362,6 +366,10 @@ describe("document", function () {
|
|||||||
{ ref: sigRef, data: sigDict },
|
{ ref: sigRef, data: sigDict },
|
||||||
{ ref: fieldRef, data: fieldDict },
|
{ ref: fieldRef, data: fieldDict },
|
||||||
]);
|
]);
|
||||||
|
acroForm.assignXref(xref);
|
||||||
|
sigDict.assignXref(xref);
|
||||||
|
fieldDict.assignXref(xref);
|
||||||
|
|
||||||
acroForm.set("Fields", [fieldRef]);
|
acroForm.set("Fields", [fieldRef]);
|
||||||
|
|
||||||
const documentStream = new StringStream(
|
const documentStream = new StringStream(
|
||||||
@ -386,6 +394,10 @@ describe("document", function () {
|
|||||||
{ ref: sigRef, data: sigDict },
|
{ ref: sigRef, data: sigDict },
|
||||||
{ ref: fieldRef, data: fieldDict },
|
{ ref: fieldRef, data: fieldDict },
|
||||||
]);
|
]);
|
||||||
|
acroForm.assignXref(xref);
|
||||||
|
sigDict.assignXref(xref);
|
||||||
|
fieldDict.assignXref(xref);
|
||||||
|
|
||||||
acroForm.set("Fields", [fieldRef]);
|
acroForm.set("Fields", [fieldRef]);
|
||||||
|
|
||||||
const documentStream = new StringStream(
|
const documentStream = new StringStream(
|
||||||
@ -418,6 +430,11 @@ describe("document", function () {
|
|||||||
{ ref: sigFieldRef, data: sigField },
|
{ ref: sigFieldRef, data: sigField },
|
||||||
{ ref: containerRef, data: container },
|
{ ref: containerRef, data: container },
|
||||||
]);
|
]);
|
||||||
|
acroForm.assignXref(xref);
|
||||||
|
sigDict.assignXref(xref);
|
||||||
|
sigField.assignXref(xref);
|
||||||
|
container.assignXref(xref);
|
||||||
|
|
||||||
acroForm.set("Fields", [containerRef]);
|
acroForm.set("Fields", [containerRef]);
|
||||||
|
|
||||||
const pdfDocument = getDocument(acroForm, xref);
|
const pdfDocument = getDocument(acroForm, xref);
|
||||||
@ -450,6 +467,11 @@ describe("document", function () {
|
|||||||
{ ref: sigFieldRef, data: sigField },
|
{ ref: sigFieldRef, data: sigField },
|
||||||
{ ref: widgetRef, data: widget },
|
{ ref: widgetRef, data: widget },
|
||||||
]);
|
]);
|
||||||
|
acroForm.assignXref(xref);
|
||||||
|
sigDict.assignXref(xref);
|
||||||
|
sigField.assignXref(xref);
|
||||||
|
widget.assignXref(xref);
|
||||||
|
|
||||||
acroForm.set("Fields", [sigFieldRef]);
|
acroForm.set("Fields", [sigFieldRef]);
|
||||||
|
|
||||||
const pdfDocument = getDocument(acroForm, xref);
|
const pdfDocument = getDocument(acroForm, xref);
|
||||||
@ -472,6 +494,10 @@ describe("document", function () {
|
|||||||
{ ref: sigRef, data: sigDict },
|
{ ref: sigRef, data: sigDict },
|
||||||
{ ref: fieldRef, data: fieldDict },
|
{ ref: fieldRef, data: fieldDict },
|
||||||
]);
|
]);
|
||||||
|
acroForm.assignXref(xref);
|
||||||
|
sigDict.assignXref(xref);
|
||||||
|
fieldDict.assignXref(xref);
|
||||||
|
|
||||||
acroForm.set("Fields", [fieldRef]);
|
acroForm.set("Fields", [fieldRef]);
|
||||||
|
|
||||||
const pdfDocument = getDocument(acroForm, xref);
|
const pdfDocument = getDocument(acroForm, xref);
|
||||||
@ -498,18 +524,21 @@ describe("document", function () {
|
|||||||
name: "Inner",
|
name: "Inner",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const outerField = makeSigField({ T: "outer", sigRef: outerSigRef });
|
||||||
|
const innerField = makeSigField({ T: "inner", sigRef: innerSigRef });
|
||||||
|
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
{ ref: outerSigRef, data: outerSig },
|
{ ref: outerSigRef, data: outerSig },
|
||||||
{
|
{ ref: outerFieldRef, data: outerField },
|
||||||
ref: outerFieldRef,
|
|
||||||
data: makeSigField({ T: "outer", sigRef: outerSigRef }),
|
|
||||||
},
|
|
||||||
{ ref: innerSigRef, data: innerSig },
|
{ ref: innerSigRef, data: innerSig },
|
||||||
{
|
{ ref: innerFieldRef, data: innerField },
|
||||||
ref: innerFieldRef,
|
|
||||||
data: makeSigField({ T: "inner", sigRef: innerSigRef }),
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
|
acroForm.assignXref(xref);
|
||||||
|
outerSig.assignXref(xref);
|
||||||
|
innerSig.assignXref(xref);
|
||||||
|
outerField.assignXref(xref);
|
||||||
|
innerField.assignXref(xref);
|
||||||
|
|
||||||
acroForm.set("Fields", [outerFieldRef, innerFieldRef]);
|
acroForm.set("Fields", [outerFieldRef, innerFieldRef]);
|
||||||
|
|
||||||
const pdfDocument = getDocument(acroForm, xref);
|
const pdfDocument = getDocument(acroForm, xref);
|
||||||
@ -535,14 +564,18 @@ describe("document", function () {
|
|||||||
byteRange: [0, 10, 20, 30],
|
byteRange: [0, 10, 20, 30],
|
||||||
subFilter,
|
subFilter,
|
||||||
});
|
});
|
||||||
|
const sigField = makeSigField({ T: "sig", sigRef });
|
||||||
|
|
||||||
const xref = new XRefMock([
|
const xref = new XRefMock([
|
||||||
{ ref: sigRef, data: sigDict },
|
{ ref: sigRef, data: sigDict },
|
||||||
{
|
{ ref: fieldRef, data: sigField },
|
||||||
ref: fieldRef,
|
|
||||||
data: makeSigField({ T: "sig", sigRef }),
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
|
acroForm.assignXref(xref);
|
||||||
|
sigDict.assignXref(xref);
|
||||||
|
sigField.assignXref(xref);
|
||||||
|
|
||||||
acroForm.set("Fields", [fieldRef]);
|
acroForm.set("Fields", [fieldRef]);
|
||||||
|
|
||||||
const pdfDocument = getDocument(acroForm, xref);
|
const pdfDocument = getDocument(acroForm, xref);
|
||||||
const [sig] = await pdfDocument.signatures;
|
const [sig] = await pdfDocument.signatures;
|
||||||
return sig.signatureType;
|
return sig.signatureType;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user