Browse Source

Make it possible to change the signature reservation

master
Přemysl Eric Janouch 8 months ago
parent
commit
796a9640d3
Signed by: p GPG Key ID: A0420B94F92B9493
  1. 6
      README.adoc
  2. 16
      cmd/pdf-simple-sign/main.go
  3. 23
      pdf-simple-sign.cpp
  4. 8
      pdf/pdf.go

6
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,

16
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)
}
}

23
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());
}

8
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("> >>")

Loading…
Cancel
Save