aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.adoc6
-rw-r--r--cmd/pdf-simple-sign/main.go16
-rw-r--r--pdf-simple-sign.cpp23
-rw-r--r--pdf/pdf.go8
4 files changed, 36 insertions, 17 deletions
diff --git a/README.adoc b/README.adoc
index 6e7acd3..595ac4b 100644
--- a/README.adoc
+++ b/README.adoc
@@ -9,8 +9,6 @@ the Cairo library. As such, it currently comes with some restrictions:
overwritten
* the document may not employ cross-reference streams, or must constitute
a hybrid-reference file at least
- * the signature may take at most 4 kilobytes as a compile-time limit,
- which should be enough space even for one intermediate certificate
The signature is attached to the first page and has no appearance.
@@ -37,6 +35,10 @@ Usage
$ ./pdf-simple-sign document.pdf document.signed.pdf KeyAndCerts.p12 password
+If the signature doesn't fit within the default reservation of 4 kibibytes,
+you might need to adjust it using the `-r` option, or throw out any unnecessary
+intermediate certificates.
+
Contributing and Support
------------------------
Use https://git.janouch.name/p/pdf-simple-sign to report bugs, request features,
diff --git a/cmd/pdf-simple-sign/main.go b/cmd/pdf-simple-sign/main.go
index 3257a66..5141a12 100644
--- a/cmd/pdf-simple-sign/main.go
+++ b/cmd/pdf-simple-sign/main.go
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2018, Přemysl Eric Janouch <p@janouch.name>
+// Copyright (c) 2018 - 2020, Přemysl Eric Janouch <p@janouch.name>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted.
@@ -20,8 +20,9 @@ import (
"flag"
"fmt"
"io/ioutil"
- "janouch.name/pdf-simple-sign/pdf"
"os"
+
+ "janouch.name/pdf-simple-sign/pdf"
)
// #include <unistd.h>
@@ -39,10 +40,13 @@ func die(status int, format string, args ...interface{}) {
}
func usage() {
- die(1, "Usage: %s [-h] INPUT-FILENAME OUTPUT-FILENAME "+
+ die(1, "Usage: %s [-h] [-r RESERVATION] INPUT-FILENAME OUTPUT-FILENAME "+
"PKCS12-PATH PKCS12-PASS", os.Args[0])
}
+var reservation = flag.Int(
+ "r", 4096, "signature reservation as a number of bytes")
+
func main() {
flag.Usage = usage
flag.Parse()
@@ -51,7 +55,7 @@ func main() {
}
inputPath, outputPath := flag.Arg(0), flag.Arg(1)
- pdfDocument, err := ioutil.ReadFile(inputPath)
+ doc, err := ioutil.ReadFile(inputPath)
if err != nil {
die(1, "%s", err)
}
@@ -63,10 +67,10 @@ func main() {
if err != nil {
die(3, "%s", err)
}
- if pdfDocument, err = pdf.Sign(pdfDocument, key, certs); err != nil {
+ if doc, err = pdf.Sign(doc, key, certs, *reservation); err != nil {
die(4, "error: %s", err)
}
- if err = ioutil.WriteFile(outputPath, pdfDocument, 0666); err != nil {
+ if err = ioutil.WriteFile(outputPath, doc, 0666); err != nil {
die(5, "%s", err)
}
}
diff --git a/pdf-simple-sign.cpp b/pdf-simple-sign.cpp
index 6d01211..34acc9f 100644
--- a/pdf-simple-sign.cpp
+++ b/pdf-simple-sign.cpp
@@ -40,6 +40,7 @@
// -------------------------------------------------------------------------------------------------
using uint = unsigned int;
+using ushort = unsigned short;
static std::string concatenate(const std::vector<std::string>& v, const std::string& delim) {
std::string res;
@@ -831,7 +832,7 @@ error:
/// https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/Acrobat_DigitalSignatures_in_PDF.pdf
/// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
/// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PPKAppearances.pdf
-static std::string pdf_sign(std::string& document) {
+static std::string pdf_sign(std::string& document, ushort reservation) {
pdf_updater pdf(document);
auto err = pdf.initialize();
if (!err.empty())
@@ -855,7 +856,7 @@ static std::string pdf_sign(std::string& document) {
pdf.document.append((byterange_len = 32 /* fine for a gigabyte */), ' ');
pdf.document.append("\n /Contents <");
sign_off = pdf.document.size();
- pdf.document.append((sign_len = 8192 /* certificate, digest, encrypted digest, ... */), '0');
+ pdf.document.append((sign_len = reservation * 2), '0');
pdf.document.append("> >>");
// We actually need to exclude the hexstring quotes from signing
@@ -945,15 +946,18 @@ static void die(int status, const char* format, ...) {
int main(int argc, char* argv[]) {
auto invocation_name = argv[0];
auto usage = [=]{
- die(1, "Usage: %s [-h] INPUT-FILENAME OUTPUT-FILENAME PKCS12-PATH PKCS12-PASS",
+ die(1, "Usage: %s [-h] [-r RESERVATION] INPUT-FILENAME OUTPUT-FILENAME PKCS12-PATH PKCS12-PASS",
invocation_name);
};
static struct option opts[] = {
{"help", no_argument, 0, 'h'},
+ {"reservation", required_argument, 0, 'r'},
{nullptr, 0, 0, 0},
};
+ // Reserved space in bytes for the certificate, digest, encrypted digest, ...
+ long reservation = 4096;
while (1) {
int option_index = 0;
auto c = getopt_long(argc, const_cast<char* const*>(argv),
@@ -961,9 +965,16 @@ int main(int argc, char* argv[]) {
if (c == -1)
break;
+ char* end = nullptr;
switch (c) {
- case 'h': usage(); break;
- default: usage();
+ case 'r':
+ errno = 0, reservation = strtol(optarg, &end, 10);
+ if (errno || *end || reservation <= 0 || reservation > USHRT_MAX)
+ die(1, "%s: must be a positive number", optarg);
+ break;
+ case 'h':
+ default:
+ usage();
}
}
@@ -990,7 +1001,7 @@ int main(int argc, char* argv[]) {
die(1, "%s: %s", input_path, strerror(errno));
}
- auto err = pdf_sign(pdf_document);
+ auto err = pdf_sign(pdf_document, ushort(reservation));
if (!err.empty()) {
die(2, "Error: %s", err.c_str());
}
diff --git a/pdf/pdf.go b/pdf/pdf.go
index a6f1ae8..f871c70 100644
--- a/pdf/pdf.go
+++ b/pdf/pdf.go
@@ -1115,12 +1115,14 @@ func FillInSignature(document []byte, signOff, signLen int,
// There must be at least one certificate, matching the private key.
// The certificates must form a chain.
//
+// A good default for the reservation is around 4096 (the value is in bytes).
+//
// The presumption here is that the document is valid and that it doesn't
// 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.
-func Sign(document []byte,
- key crypto.PrivateKey, certs []*x509.Certificate) ([]byte, error) {
+func Sign(document []byte, key crypto.PrivateKey, certs []*x509.Certificate,
+ reservation int) ([]byte, error) {
pdf, err := NewUpdater(document)
if err != nil {
return nil, err
@@ -1152,7 +1154,7 @@ func Sign(document []byte,
buf.WriteString("\n /Contents <")
signOff = buf.Len()
- signLen = 8192 // cert, digest, encrypted digest, ...
+ signLen = reservation * 2 // cert, digest, encrypted digest, ...
buf.Write(bytes.Repeat([]byte{'0'}, signLen))
buf.WriteString("> >>")