Make it possible to change the signature reservation

This commit is contained in:
Přemysl Eric Janouch 2020-09-04 18:33:12 +02:00
parent 2d08100b58
commit 796a9640d3
Signed by: p
GPG Key ID: A0420B94F92B9493
4 changed files with 36 additions and 17 deletions

View File

@ -9,8 +9,6 @@ the Cairo library. As such, it currently comes with some restrictions:
overwritten overwritten
* the document may not employ cross-reference streams, or must constitute * the document may not employ cross-reference streams, or must constitute
a hybrid-reference file at least 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. 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 $ ./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 Contributing and Support
------------------------ ------------------------
Use https://git.janouch.name/p/pdf-simple-sign to report bugs, request features, Use https://git.janouch.name/p/pdf-simple-sign to report bugs, request features,

View File

@ -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 // Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted. // purpose with or without fee is hereby granted.
@ -20,8 +20,9 @@ import (
"flag" "flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"janouch.name/pdf-simple-sign/pdf"
"os" "os"
"janouch.name/pdf-simple-sign/pdf"
) )
// #include <unistd.h> // #include <unistd.h>
@ -39,10 +40,13 @@ func die(status int, format string, args ...interface{}) {
} }
func usage() { 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]) "PKCS12-PATH PKCS12-PASS", os.Args[0])
} }
var reservation = flag.Int(
"r", 4096, "signature reservation as a number of bytes")
func main() { func main() {
flag.Usage = usage flag.Usage = usage
flag.Parse() flag.Parse()
@ -51,7 +55,7 @@ func main() {
} }
inputPath, outputPath := flag.Arg(0), flag.Arg(1) inputPath, outputPath := flag.Arg(0), flag.Arg(1)
pdfDocument, err := ioutil.ReadFile(inputPath) doc, err := ioutil.ReadFile(inputPath)
if err != nil { if err != nil {
die(1, "%s", err) die(1, "%s", err)
} }
@ -63,10 +67,10 @@ func main() {
if err != nil { if err != nil {
die(3, "%s", err) 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) 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) die(5, "%s", err)
} }
} }

View File

@ -40,6 +40,7 @@
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
using uint = unsigned int; using uint = unsigned int;
using ushort = unsigned short;
static std::string concatenate(const std::vector<std::string>& v, const std::string& delim) { static std::string concatenate(const std::vector<std::string>& v, const std::string& delim) {
std::string res; 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/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/pdf_reference_1-7.pdf
/// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PPKAppearances.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); pdf_updater pdf(document);
auto err = pdf.initialize(); auto err = pdf.initialize();
if (!err.empty()) 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((byterange_len = 32 /* fine for a gigabyte */), ' ');
pdf.document.append("\n /Contents <"); pdf.document.append("\n /Contents <");
sign_off = pdf.document.size(); 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("> >>"); pdf.document.append("> >>");
// We actually need to exclude the hexstring quotes from signing // 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[]) { int main(int argc, char* argv[]) {
auto invocation_name = argv[0]; auto invocation_name = argv[0];
auto usage = [=]{ 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); invocation_name);
}; };
static struct option opts[] = { static struct option opts[] = {
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"reservation", required_argument, 0, 'r'},
{nullptr, 0, 0, 0}, {nullptr, 0, 0, 0},
}; };
// Reserved space in bytes for the certificate, digest, encrypted digest, ...
long reservation = 4096;
while (1) { while (1) {
int option_index = 0; int option_index = 0;
auto c = getopt_long(argc, const_cast<char* const*>(argv), auto c = getopt_long(argc, const_cast<char* const*>(argv),
@ -961,9 +965,16 @@ int main(int argc, char* argv[]) {
if (c == -1) if (c == -1)
break; break;
char* end = nullptr;
switch (c) { switch (c) {
case 'h': usage(); break; case 'r':
default: usage(); 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)); 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()) { if (!err.empty()) {
die(2, "Error: %s", err.c_str()); die(2, "Error: %s", err.c_str());
} }

View File

@ -1115,12 +1115,14 @@ func FillInSignature(document []byte, signOff, signLen int,
// There must be at least one certificate, matching the private key. // There must be at least one certificate, matching the private key.
// The certificates must form a chain. // 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 // 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 // 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 // a hybrid-reference file. The results with PDF 2.0 (2017) are currently
// unknown as the standard costs money. // unknown as the standard costs money.
func Sign(document []byte, func Sign(document []byte, key crypto.PrivateKey, certs []*x509.Certificate,
key crypto.PrivateKey, certs []*x509.Certificate) ([]byte, error) { reservation int) ([]byte, error) {
pdf, err := NewUpdater(document) pdf, err := NewUpdater(document)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1152,7 +1154,7 @@ func Sign(document []byte,
buf.WriteString("\n /Contents <") buf.WriteString("\n /Contents <")
signOff = buf.Len() signOff = buf.Len()
signLen = 8192 // cert, digest, encrypted digest, ... signLen = reservation * 2 // cert, digest, encrypted digest, ...
buf.Write(bytes.Repeat([]byte{'0'}, signLen)) buf.Write(bytes.Repeat([]byte{'0'}, signLen))
buf.WriteString("> >>") buf.WriteString("> >>")