From 679656fb8d44bff5fe748b87d4bf29ea1cf7e0e3 Mon Sep 17 00:00:00 2001 From: Jeroen Bobbeldijk Date: Sat, 23 Sep 2017 16:37:39 +0200 Subject: [PATCH] Changed file to writer/reader --- sign.go | 2 +- sign/pdfbyterange.go | 17 ++++++------ sign/pdfsignature.go | 19 +++++++++---- sign/pdftrailer.go | 6 ++-- sign/pdfvisualsignature.go | 1 - sign/pdfxref.go | 42 +++++++++++++--------------- sign/sign.go | 43 +++++++++++++++------------- sign/sign_test.go | 57 +++++++++++++++++++++++++++++++++----- verify/verify.go | 4 +-- 9 files changed, 121 insertions(+), 70 deletions(-) diff --git a/sign.go b/sign.go index 22e8931..ecb9a39 100644 --- a/sign.go +++ b/sign.go @@ -130,7 +130,7 @@ func main() { Certificate: cert, CertificateChains: certificate_chains, TSA: sign.TSA{ - URL: "http://aatl-timestamp.globalsign.com/tsa/aohfewat2389535fnasgnlg5m23", + URL: "http://aatl-timestamp.globalsign.com/tsa/aohfewat2389535fnasgnlg5m23", }, RevocationData: revocation.InfoArchival{}, RevocationFunction: sign.DefaultEmbedRevocationStatusFunction, diff --git a/sign/pdfbyterange.go b/sign/pdfbyterange.go index 9a5e9f0..2df939d 100644 --- a/sign/pdfbyterange.go +++ b/sign/pdfbyterange.go @@ -6,11 +6,8 @@ import ( ) func (context *SignContext) updateByteRange() error { - // Get current filesize. Easier than what should be the current size. - // @todo: find out of this is safe. - output_file_stat, _ := context.OutputFile.Stat() - - output_file_size := output_file_stat.Size() + context.OutputBuffer.Seek(0, 0) + output_file_size := int64(context.OutputBuffer.Buff.Len()) // Calculate ByteRange values to replace them. context.ByteRangeValues = make([]int64, 4) @@ -32,13 +29,17 @@ func (context *SignContext) updateByteRange() error { // Make sure our ByteRange string didn't shrink in length. new_byte_range += strings.Repeat(" ", len(signatureByteRangePlaceholder)-len(new_byte_range)) - // Seek to ByteRange position in file. - context.OutputFile.Seek(context.ByteRangeStartByte, 0) + context.OutputBuffer.Seek(0, 0) + file_content := context.OutputBuffer.Buff.Bytes() + + context.OutputBuffer.Write(file_content[:context.ByteRangeStartByte]) // Write new ByteRange. - if _, err := context.OutputFile.Write([]byte(new_byte_range)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(new_byte_range)); err != nil { return err } + context.OutputBuffer.Write(file_content[context.ByteRangeStartByte+int64(len(new_byte_range)):]) + return nil } diff --git a/sign/pdfsignature.go b/sign/pdfsignature.go index 2e28f22..e7a072e 100644 --- a/sign/pdfsignature.go +++ b/sign/pdfsignature.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "errors" "fmt" - "io" "io/ioutil" "net/http" "strconv" @@ -121,12 +120,10 @@ func (context *SignContext) fetchRevocationData() error { } func (context *SignContext) createSignature() ([]byte, error) { + context.OutputBuffer.Seek(0, 0) // Sadly we can't efficiently sign a file, we need to read all the bytes we want to sign. - context.OutputFile.Seek(0, 0) - sign_buf := bytes.NewBuffer(nil) - io.Copy(sign_buf, context.OutputFile) - file_content := sign_buf.Bytes() + file_content := context.OutputBuffer.Buff.Bytes() // Collect the parts to sign. sign_content := make([]byte, 0) @@ -266,7 +263,17 @@ func (context *SignContext) replaceSignature() error { return errors.New("Signature is too big to fit in reserved space.") } - context.OutputFile.WriteAt(dst, context.ByteRangeValues[0]+context.ByteRangeValues[1]+1) + context.OutputBuffer.Seek(0, 0) + file_content := context.OutputBuffer.Buff.Bytes() + + context.OutputBuffer.Write(file_content[:(context.ByteRangeValues[0] + context.ByteRangeValues[1] + 1)]) + + // Write new ByteRange. + if _, err := context.OutputBuffer.Write([]byte(dst)); err != nil { + return err + } + + context.OutputBuffer.Write(file_content[(context.ByteRangeValues[0]+context.ByteRangeValues[1]+1)+int64(len(dst)):]) return nil } diff --git a/sign/pdftrailer.go b/sign/pdftrailer.go index c5c5dc4..eb93ac0 100644 --- a/sign/pdftrailer.go +++ b/sign/pdftrailer.go @@ -35,18 +35,18 @@ func (context *SignContext) writeTrailer() error { trailer_string = strings.Replace(trailer_string, info_string, new_info, -1) // Write the new trailer. - if _, err := context.OutputFile.Write([]byte(trailer_string)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(trailer_string)); err != nil { return err } } // Write the new xref start position. - if _, err := context.OutputFile.Write([]byte("\nstartxref\n" + strconv.FormatInt(context.NewXrefStart, 10) + "\n")); err != nil { + if _, err := context.OutputBuffer.Write([]byte("\nstartxref\n" + strconv.FormatInt(context.NewXrefStart, 10) + "\n")); err != nil { return err } // Write PDF ending. - if _, err := context.OutputFile.Write([]byte("%%EOF")); err != nil { + if _, err := context.OutputBuffer.Write([]byte("%%EOF")); err != nil { return err } diff --git a/sign/pdfvisualsignature.go b/sign/pdfvisualsignature.go index 2043120..3261cb1 100644 --- a/sign/pdfvisualsignature.go +++ b/sign/pdfvisualsignature.go @@ -20,7 +20,6 @@ func (context *SignContext) createVisualSignature() (visual_signature string, er } } - rootPtr := root.GetPtr() context.CatalogData.RootString = strconv.Itoa(int(rootPtr.GetID())) + " " + strconv.Itoa(int(rootPtr.GetGen())) + " R" diff --git a/sign/pdfxref.go b/sign/pdfxref.go index 2addc2e..96d411e 100644 --- a/sign/pdfxref.go +++ b/sign/pdfxref.go @@ -1,12 +1,12 @@ package sign import ( + "bytes" + "compress/zlib" + "encoding/binary" + "encoding/hex" "errors" "strconv" - "encoding/hex" - "compress/zlib" - "bytes" - "encoding/binary" ) func (context *SignContext) writeXref() error { @@ -31,12 +31,12 @@ func (context *SignContext) writeXrefTable() error { xref_size := "xref\n0 " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount, 10) new_xref_size := "xref\n0 " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+4, 10) - if _, err := context.OutputFile.Write([]byte(new_xref_size)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(new_xref_size)); err != nil { return err } // Write the old xref table to the output pdf. - if err := writePartFromSourceFileToTargetFile(context.InputFile, context.OutputFile, context.PDFReader.XrefInformation.StartPos+int64(len(xref_size)), context.PDFReader.XrefInformation.Length-int64(len(xref_size))); err != nil { + if err := writePartFromSourceFileToTargetFile(context.InputFile, context.OutputBuffer, context.PDFReader.XrefInformation.StartPos+int64(len(xref_size)), context.PDFReader.XrefInformation.Length-int64(len(xref_size))); err != nil { return err } @@ -45,7 +45,7 @@ func (context *SignContext) writeXrefTable() error { visual_signature_xref_line := leftPad(visual_signature_object_start_position, "0", 10-len(visual_signature_object_start_position)) + " 00000 n \n" // Write the new catalog xref line. - if _, err := context.OutputFile.Write([]byte(visual_signature_xref_line)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(visual_signature_xref_line)); err != nil { return err } @@ -54,7 +54,7 @@ func (context *SignContext) writeXrefTable() error { catalog_xref_line := leftPad(catalog_object_start_position, "0", 10-len(catalog_object_start_position)) + " 00000 n \n" // Write the new catalog xref line. - if _, err := context.OutputFile.Write([]byte(catalog_xref_line)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(catalog_xref_line)); err != nil { return err } @@ -63,7 +63,7 @@ func (context *SignContext) writeXrefTable() error { info_xref_line := leftPad(info_object_start_position, "0", 10-len(info_object_start_position)) + " 00000 n \n" // Write the new signature xref line. - if _, err := context.OutputFile.Write([]byte(info_xref_line)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(info_xref_line)); err != nil { return err } @@ -72,7 +72,7 @@ func (context *SignContext) writeXrefTable() error { signature_xref_line := leftPad(signature_object_start_position, "0", 10-len(signature_object_start_position)) + " 00000 n \n" // Write the new signature xref line. - if _, err := context.OutputFile.Write([]byte(signature_xref_line)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(signature_xref_line)); err != nil { return err } @@ -107,7 +107,6 @@ func (context *SignContext) writeXrefStream() error { } } - new_info := "Info " + strconv.FormatInt(int64(context.InfoData.ObjectId), 10) + " 0 R" new_root := "Root " + strconv.FormatInt(int64(context.CatalogData.ObjectId), 10) + " 0 R" @@ -116,21 +115,21 @@ func (context *SignContext) writeXrefStream() error { id0 := hex.EncodeToString([]byte(id.Index(0).RawString())) id1 := hex.EncodeToString([]byte(id.Index(0).RawString())) - new_xref := strconv.Itoa(int(context.SignData.ObjectId + 1)) + " 0 obj\n" - new_xref += "<< /Type /XRef /Length " + strconv.Itoa(len(streamBytes)) + " /Filter /FlateDecode /DecodeParms << /Columns 5 /Predictor 12 >> /W [ 1 3 1 ] /Prev " + strconv.FormatInt(context.PDFReader.XrefInformation.StartPos, 10) + " /Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+5, 10) + " /Index [ " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount, 10) + " 5 ] /" + new_info + " /" + new_root + " /ID [<" + id0 + "><" + id1 + ">] >>\n" - if _, err := context.OutputFile.Write([]byte(new_xref)); err != nil { + new_xref := strconv.Itoa(int(context.SignData.ObjectId+1)) + " 0 obj\n" + new_xref += "<< /Type /XRef /Length " + strconv.Itoa(len(streamBytes)) + " /Filter /FlateDecode /DecodeParms << /Columns 5 /Predictor 12 >> /W [ 1 3 1 ] /Prev " + strconv.FormatInt(context.PDFReader.XrefInformation.StartPos, 10) + " /Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+5, 10) + " /Index [ " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount, 10) + " 5 ] /" + new_info + " /" + new_root + " /ID [<" + id0 + "><" + id1 + ">] >>\n" + if _, err := context.OutputBuffer.Write([]byte(new_xref)); err != nil { return err } - if _, err := context.OutputFile.Write([]byte("stream\n")); err != nil { + if _, err := context.OutputBuffer.Write([]byte("stream\n")); err != nil { return err } - if _, err := context.OutputFile.Write(streamBytes); err != nil { + if _, err := context.OutputBuffer.Write(streamBytes); err != nil { return err } - if _, err := context.OutputFile.Write([]byte("\nendstream\n")); err != nil { + if _, err := context.OutputBuffer.Write([]byte("\nendstream\n")); err != nil { return err } @@ -138,9 +137,9 @@ func (context *SignContext) writeXrefStream() error { } func writeXrefStreamLine(b *bytes.Buffer, xreftype byte, offset int, gen byte) { - b.WriteByte(xreftype); - b.Write(encodeInt(offset)); - b.WriteByte(gen); + b.WriteByte(xreftype) + b.Write(encodeInt(offset)) + b.WriteByte(gen) } func encodeInt(i int) []byte { @@ -178,7 +177,6 @@ func EncodePNGSUBBytes(columns int, data []byte) ([]byte, error) { return b.Bytes(), nil } - func EncodePNGUPBytes(columns int, data []byte) ([]byte, error) { rowCount := len(data) / columns if len(data)%columns != 0 { @@ -215,4 +213,4 @@ func EncodePNGUPBytes(columns int, data []byte) ([]byte, error) { w.Close() return b.Bytes(), nil -} \ No newline at end of file +} diff --git a/sign/sign.go b/sign/sign.go index 0deb1de..96b118a 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -9,6 +9,7 @@ import ( "bitbucket.org/digitorus/pdf" "bitbucket.org/digitorus/pdfsign/revocation" + "github.com/mattetti/filebuffer" ) type CatalogData struct { @@ -62,8 +63,9 @@ type SignDataSignatureInfo struct { type SignContext struct { Filesize int64 - InputFile *os.File - OutputFile *os.File + InputFile io.ReadSeeker + OutputFile io.Writer + OutputBuffer *filebuffer.Buffer SignData SignData CatalogData CatalogData VisualSignData VisualSignData @@ -100,14 +102,18 @@ func SignFile(input string, output string, sign_data SignData) error { return err } + return Sign(input_file, output_file, rdr, size, sign_data) +} + +func Sign(input io.ReadSeeker, output io.Writer, rdr *pdf.Reader, size int64, sign_data SignData) error { sign_data.ObjectId = uint32(rdr.XrefInformation.ItemCount) + 3 // We do size+1 because we insert a newline. context := SignContext{ Filesize: size + 1, PDFReader: rdr, - InputFile: input_file, - OutputFile: output_file, + InputFile: input, + OutputFile: output, VisualSignData: VisualSignData{ ObjectId: uint32(rdr.XrefInformation.ItemCount), }, @@ -120,7 +126,7 @@ func SignFile(input string, output string, sign_data SignData) error { SignData: sign_data, } - err = context.SignPDF() + err := context.SignPDF() if err != nil { return err } @@ -129,18 +135,15 @@ func SignFile(input string, output string, sign_data SignData) error { } func (context *SignContext) SignPDF() error { - // Copy old file into new file. - if _, err := io.Copy(context.OutputFile, context.InputFile); err != nil { - return err - } + context.OutputBuffer = filebuffer.New([]byte{}) - err := context.OutputFile.Sync() - if err != nil { + // Copy old file into new file. + if _, err := io.Copy(context.OutputBuffer, context.InputFile); err != nil { return err } // File always needs an empty line after %%EOF. - if _, err := context.OutputFile.Write([]byte("\n")); err != nil { + if _, err := context.OutputBuffer.Write([]byte("\n")); err != nil { return err } @@ -165,7 +168,7 @@ func (context *SignContext) SignPDF() error { context.VisualSignData.Length = int64(len(visual_signature)) // Write the new catalog object. - if _, err := context.OutputFile.Write([]byte(visual_signature)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(visual_signature)); err != nil { return err } @@ -177,7 +180,7 @@ func (context *SignContext) SignPDF() error { context.CatalogData.Length = int64(len(catalog)) // Write the new catalog object. - if _, err := context.OutputFile.Write([]byte(catalog)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(catalog)); err != nil { return err } @@ -192,7 +195,7 @@ func (context *SignContext) SignPDF() error { context.InfoData.Length = int64(len(info)) // Write the new catalog object. - if _, err := context.OutputFile.Write([]byte(info)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(info)); err != nil { return err } @@ -206,7 +209,7 @@ func (context *SignContext) SignPDF() error { context.SignatureContentsStartByte = signature_contents_start_byte // Write the new signature object. - if _, err := context.OutputFile.Write([]byte(signature_object)); err != nil { + if _, err := context.OutputBuffer.Write([]byte(signature_object)); err != nil { return err } @@ -229,10 +232,10 @@ func (context *SignContext) SignPDF() error { return err } - err = context.OutputFile.Sync() - if err != nil { - return err - } + context.OutputBuffer.Seek(0, 0) + file_content := context.OutputBuffer.Buff.Bytes() + + context.OutputFile.Write(file_content) return nil } diff --git a/sign/sign_test.go b/sign/sign_test.go index a6c0f2f..aaa5f59 100644 --- a/sign/sign_test.go +++ b/sign/sign_test.go @@ -1,6 +1,8 @@ package sign import ( + "bufio" + "bytes" "crypto/x509" "encoding/pem" "fmt" @@ -131,7 +133,30 @@ func TestSignPDF(t *testing.T) { fmt.Printf("Signing file %s\n", f.Name()) - err = SignFile("../testfiles/"+f.Name(), "../testfiles/"+f.Name()+".tmp", SignData{ + input_file, err := os.Open("../testfiles/" + f.Name()) + if err != nil { + t.Errorf("%s: %s", f.Name(), err.Error()) + return + } + defer input_file.Close() + + var buffer bytes.Buffer + output_file := bufio.NewWriter(&buffer) + + finfo, err := input_file.Stat() + if err != nil { + t.Errorf("%s: %s", f.Name(), err.Error()) + return + } + size := finfo.Size() + + rdr, err := pdf.NewReader(input_file, size) + if err != nil { + t.Errorf("%s: %s", f.Name(), err.Error()) + return + } + + err = Sign(input_file, output_file, rdr, size, SignData{ Signature: SignDataSignature{ Info: SignDataSignatureInfo{ Name: "Jeroen Bobbeldijk", @@ -153,9 +178,6 @@ func TestSignPDF(t *testing.T) { RevocationFunction: DefaultEmbedRevocationStatusFunction, }) - // Cleanup old files. - defer os.Remove("../testfiles/"+f.Name()+".tmp") - if err != nil { t.Errorf("%s: %s", f.Name(), err.Error()) return @@ -191,7 +213,30 @@ func BenchmarkSignPDF(b *testing.B) { certificate_chains := make([][]*x509.Certificate, 0) for n := 0; n < b.N; n++ { - err := SignFile("../testfiles/testfile20.pdf", "../testfiles/testfile20.pdf.tmp", SignData{ + input_file, err := os.Open("../testfiles/testfile20.pdf") + if err != nil { + b.Errorf("%s: %s", "testfile20.pdf", err.Error()) + return + } + defer input_file.Close() + + var buffer bytes.Buffer + output_file := bufio.NewWriter(&buffer) + + finfo, err := input_file.Stat() + if err != nil { + b.Errorf("%s: %s", "testfile20.pdf", err.Error()) + return + } + size := finfo.Size() + + rdr, err := pdf.NewReader(input_file, size) + if err != nil { + b.Errorf("%s: %s", "testfile20.pdf", err.Error()) + return + } + + err = Sign(input_file, output_file, rdr, size, SignData{ Signature: SignDataSignature{ Info: SignDataSignatureInfo{ Name: "Jeroen Bobbeldijk", @@ -213,8 +258,6 @@ func BenchmarkSignPDF(b *testing.B) { RevocationFunction: DefaultEmbedRevocationStatusFunction, }) - os.Remove("../testfiles/testfile20.pdf.tmp") - if err != nil { b.Errorf("%s: %s", "testfile20.pdf", err.Error()) return diff --git a/verify/verify.go b/verify/verify.go index 06b7e89..8e4fd12 100644 --- a/verify/verify.go +++ b/verify/verify.go @@ -12,11 +12,11 @@ import ( "bitbucket.org/digitorus/pdf" "bitbucket.org/digitorus/pdfsign/revocation" + "crypto" "github.com/digitorus/pkcs7" "github.com/digitorus/timestamp" - "log" "golang.org/x/crypto/ocsp" - "crypto" + "log" ) type Response struct {