aboutsummaryrefslogtreecommitdiff
path: root/pdf
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2020-09-04 17:16:42 +0200
committerPřemysl Eric Janouch <p@janouch.name>2020-09-04 18:30:09 +0200
commit2d08100b58b6c7e06f124aef3e2761bcdaeac85b (patch)
tree13c09c71e8b203e88e40a150b1327ce404283bda /pdf
parent1224d9be47e5235f39556d9118c9893ee6e68a4c (diff)
downloadpdf-simple-sign-2d08100b58b6c7e06f124aef3e2761bcdaeac85b.tar.gz
pdf-simple-sign-2d08100b58b6c7e06f124aef3e2761bcdaeac85b.tar.xz
pdf-simple-sign-2d08100b58b6c7e06f124aef3e2761bcdaeac85b.zip
Avoid downgrading the document's PDF version
Diffstat (limited to 'pdf')
-rw-r--r--pdf/pdf.go40
1 files changed, 31 insertions, 9 deletions
diff --git a/pdf/pdf.go b/pdf/pdf.go
index d9e4098..a6f1ae8 100644
--- a/pdf/pdf.go
+++ b/pdf/pdf.go
@@ -670,7 +670,7 @@ func (u *Updater) loadXref(lex *Lexer, loadedEntries map[uint]struct{}) error {
// -----------------------------------------------------------------------------
-var haystackRE = regexp.MustCompile(`(?s:.*)\sstartxref\s+(\d+)\s+%%EOF`)
+var trailerRE = regexp.MustCompile(`(?s:.*)\sstartxref\s+(\d+)\s+%%EOF`)
// NewUpdater initializes an Updater, building the cross-reference table and
// preparing a new trailer dictionary.
@@ -685,7 +685,7 @@ func NewUpdater(document []byte) (*Updater, error) {
haystack = haystack[len(haystack)-1024:]
}
- m := haystackRE.FindSubmatch(haystack)
+ m := trailerRE.FindSubmatch(haystack)
if m == nil {
return nil, errors.New("cannot find startxref")
}
@@ -739,6 +739,31 @@ func NewUpdater(document []byte) (*Updater, error) {
return u, nil
}
+var versionRE = regexp.MustCompile(
+ `(?:^|[\r\n])%(?:!PS-Adobe-\d\.\d )?PDF-(\d)\.(\d)[\r\n]`)
+
+// Version extracts the claimed PDF version as a positive decimal number,
+// e.g. 17 for PDF 1.7. Returns zero on failure.
+func (u *Updater) Version(root *Object) int {
+ if version, ok := root.Dict["Version"]; ok && version.Kind == Name {
+ if v := version.String; len(v) == 3 && v[1] == '.' &&
+ v[0] >= '0' && v[0] <= '9' && v[2] >= '0' && v[2] <= '9' {
+ return int(v[0]-'0')*10 + int(v[2]-'0')
+ }
+ }
+
+ // We only need to look for the comment roughly within
+ // the first kibibyte of the document.
+ haystack := u.Document
+ if len(haystack) > 1024 {
+ haystack = haystack[:1024]
+ }
+ if m := versionRE.FindSubmatch(haystack); m != nil {
+ return int(m[1][0]-'0')*10 + int(m[2][0]-'0')
+ }
+ return 0
+}
+
// Get retrieves an object by its number and generation--may return
// Nil or End with an error.
//
@@ -1094,9 +1119,6 @@ func FillInSignature(document []byte, signOff, signLen int,
// employ cross-reference streams from PDF 1.5, or at least constitutes
// a hybrid-reference file. The results with PDF 2.0 (2017) are currently
// unknown as the standard costs money.
-//
-// Carelessly assumes that the version of the original document is at most
-// PDF 1.6.
func Sign(document []byte,
key crypto.PrivateKey, certs []*x509.Certificate) ([]byte, error) {
pdf, err := NewUpdater(document)
@@ -1195,10 +1217,10 @@ func Sign(document []byte,
})
// Upgrade the document version for SHA-256 etc.
- // XXX: Assuming that it's not newer than 1.6 already--while Cairo can't
- // currently use a newer version that 1.5, it's not a bad idea to use
- // cairo_pdf_surface_restrict_to_version().
- root.Dict["Version"] = NewName("1.6")
+ if pdf.Version(&root) < 16 {
+ root.Dict["Version"] = NewName("1.6")
+ }
+
pdf.Update(rootRef.N, func(buf BytesWriter) {
buf.WriteString(root.Serialize())
})