From 1a92cec9c4f27074683cca5087e19fb22d7c230f Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Tue, 12 Feb 2019 19:18:39 +0200 Subject: [PATCH 1/3] Refactor signature code --- pdf/model/appender.go | 13 +-- pdf/model/signature.go | 176 +++++++++------------------------ pdf/model/signature_handler.go | 34 ++++--- pdf/model/writer.go | 8 +- 4 files changed, 78 insertions(+), 153 deletions(-) diff --git a/pdf/model/appender.go b/pdf/model/appender.go index b61b1220..397b7d8c 100644 --- a/pdf/model/appender.go +++ b/pdf/model/appender.go @@ -387,17 +387,12 @@ func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *Pdf if acroForm == nil { acroForm = NewPdfAcroForm() } + pageIndex := pageNum - 1 - var page *PdfPage - for i, p := range a.pages { - if i == pageIndex { - page = p.Duplicate() - break - } - } - if page == nil { + if pageIndex < 0 || pageIndex > len(a.pages)-1 { return nil, nil, fmt.Errorf("page %d not found", pageNum) } + page := a.pages[pageIndex].Duplicate() // TODO add more checks before set the fields acroForm.SigFlags = core.MakeInteger(3) @@ -456,7 +451,6 @@ func (a *PdfAppender) Write(w io.Writer) error { if a.written { return errors.New("appender write can only be invoked once") } - a.written = true writer := NewPdfWriter() @@ -704,6 +698,7 @@ func (a *PdfAppender) Write(w io.Writer) error { } } + a.written = true return nil } diff --git a/pdf/model/signature.go b/pdf/model/signature.go index a11fa6fe..123daad2 100644 --- a/pdf/model/signature.go +++ b/pdf/model/signature.go @@ -140,49 +140,23 @@ func (sig *PdfSignature) ToPdfObject() core.PdfObject { } dict.Set("Type", sig.Type) + dict.SetIfNotNil("Filter", sig.Filter) + dict.SetIfNotNil("SubFilter", sig.SubFilter) + dict.SetIfNotNil("Contents", sig.Contents) + dict.SetIfNotNil("Cert", sig.Cert) + dict.SetIfNotNil("ByteRange", sig.ByteRange) + dict.SetIfNotNil("Reference", sig.Reference) + dict.SetIfNotNil("Changes", sig.Changes) + dict.SetIfNotNil("Name", sig.Name) + dict.SetIfNotNil("M", sig.M) + dict.SetIfNotNil("Reason", sig.Reason) + dict.SetIfNotNil("ContactInfo", sig.ContactInfo) + dict.SetIfNotNil("ByteRange", sig.ByteRange) + dict.SetIfNotNil("Contents", sig.Contents) - if sig.Filter != nil { - dict.Set("Filter", sig.Filter) - } - if sig.SubFilter != nil { - dict.Set("SubFilter", sig.SubFilter) - } - if sig.Contents != nil { - dict.Set("Contents", sig.Contents) - } - if sig.Cert != nil { - dict.Set("Cert", sig.Cert) - } - if sig.ByteRange != nil { - dict.Set("ByteRange", sig.ByteRange) - } - if sig.Reference != nil { - dict.Set("Reference", sig.Reference) - } - if sig.Changes != nil { - dict.Set("Changes", sig.Changes) - } - if sig.Name != nil { - dict.Set("Name", sig.Name) - } - if sig.M != nil { - dict.Set("M", sig.M) - } - if sig.Reason != nil { - dict.Set("Reason", sig.Reason) - } - if sig.ContactInfo != nil { - dict.Set("ContactInfo", sig.ContactInfo) - } - if sig.ByteRange != nil { - dict.Set("ByteRange", sig.ByteRange) - } - if sig.Contents != nil { - dict.Set("Contents", sig.Contents) - } // NOTE: ByteRange and Contents need to be updated dynamically. - // TODO: Currently dynamic update is only in the appender, need to support in the PdfWriter - // too for the initial signature on document creation. + // TODO: Currently dynamic update is only in the appender, need to support + // in the PdfWriter too for the initial signature on document creation. return container } @@ -281,19 +255,10 @@ func (sf *PdfSignatureField) ToPdfObject() core.PdfObject { dict := container.PdfObject.(*core.PdfObjectDictionary) dict.Set("FT", core.MakeName("Sig")) - - if sf.V != nil { - dict.Set("V", sf.V.ToPdfObject()) - } - if sf.Lock != nil { - dict.Set("Lock", sf.Lock) - } - if sf.SV != nil { - dict.Set("SV", sf.SV) - } - if sf.Kids != nil { - dict.Set("Kids", sf.Kids) - } + dict.SetIfNotNil("V", sf.V.ToPdfObject()) + dict.SetIfNotNil("Lock", sf.Lock) + dict.SetIfNotNil("SV", sf.SV) + dict.SetIfNotNil("Kids", sf.Kids) // Other standard fields... return container @@ -332,45 +297,20 @@ func (pss *PdfSignatureFieldSeed) ToPdfObject() core.PdfObject { container := pss.container dict := container.PdfObject.(*core.PdfObjectDictionary) - if pss.Ff != nil { - dict.Set("Ff", pss.Ff) - } - if pss.Filter != nil { - dict.Set("Filter", pss.Filter) - } - if pss.SubFilter != nil { - dict.Set("SubFilter", pss.SubFilter) - } - if pss.DigestMethod != nil { - dict.Set("DigestMethod", pss.DigestMethod) - } - if pss.V != nil { - dict.Set("V", pss.V) - } - if pss.Cert != nil { - dict.Set("Cert", pss.Cert) - } - if pss.Reasons != nil { - dict.Set("Reasons", pss.Reasons) - } - if pss.MDP != nil { - dict.Set("MDP", pss.MDP) - } - if pss.TimeStamp != nil { - dict.Set("TimeStamp", pss.TimeStamp) - } - if pss.LegalAttestation != nil { - dict.Set("LegalAttestation", pss.LegalAttestation) - } - if pss.AddRevInfo != nil { - dict.Set("AddRevInfo", pss.AddRevInfo) - } - if pss.LockDocument != nil { - dict.Set("LockDocument", pss.LockDocument) - } - if pss.AppearanceFilter != nil { - dict.Set("AppearanceFilter", pss.AppearanceFilter) - } + dict.SetIfNotNil("Ff", pss.Ff) + dict.SetIfNotNil("Filter", pss.Filter) + dict.SetIfNotNil("SubFilter", pss.SubFilter) + dict.SetIfNotNil("DigestMethod", pss.DigestMethod) + dict.SetIfNotNil("V", pss.V) + dict.SetIfNotNil("Cert", pss.Cert) + dict.SetIfNotNil("Reasons", pss.Reasons) + dict.SetIfNotNil("MDP", pss.MDP) + dict.SetIfNotNil("TimeStamp", pss.TimeStamp) + dict.SetIfNotNil("LegalAttestation", pss.LegalAttestation) + dict.SetIfNotNil("AddRevInfo", pss.AddRevInfo) + dict.SetIfNotNil("LockDocument", pss.LockDocument) + dict.SetIfNotNil("AppearanceFilter", pss.AppearanceFilter) + return container } @@ -396,41 +336,19 @@ type PdfCertificateSeed struct { func (pcs *PdfCertificateSeed) ToPdfObject() core.PdfObject { container := pcs.container dict := container.PdfObject.(*core.PdfObjectDictionary) - if pcs.Ff != nil { - dict.Set("Ff", pcs.Ff) - } - if pcs.Subject != nil { - dict.Set("Subject", pcs.Subject) - } - if pcs.SignaturePolicyOID != nil { - dict.Set("SignaturePolicyOID", pcs.SignaturePolicyOID) - } - if pcs.SignaturePolicyHashValue != nil { - dict.Set("SignaturePolicyHashValue", pcs.SignaturePolicyHashValue) - } - if pcs.SignaturePolicyHashAlgorithm != nil { - dict.Set("SignaturePolicyHashAlgorithm", pcs.SignaturePolicyHashAlgorithm) - } - if pcs.SignaturePolicyCommitmentType != nil { - dict.Set("SignaturePolicyCommitmentType", pcs.SignaturePolicyCommitmentType) - } - if pcs.SubjectDN != nil { - dict.Set("SubjectDN", pcs.SubjectDN) - } - if pcs.KeyUsage != nil { - dict.Set("KeyUsage", pcs.KeyUsage) - } - if pcs.Issuer != nil { - dict.Set("Issuer", pcs.Issuer) - } - if pcs.OID != nil { - dict.Set("OID", pcs.OID) - } - if pcs.URL != nil { - dict.Set("URL", pcs.URL) - } - if pcs.URLType != nil { - dict.Set("URLType", pcs.URLType) - } + + dict.SetIfNotNil("Ff", pcs.Ff) + dict.SetIfNotNil("Subject", pcs.Subject) + dict.SetIfNotNil("SignaturePolicyOID", pcs.SignaturePolicyOID) + dict.SetIfNotNil("SignaturePolicyHashValue", pcs.SignaturePolicyHashValue) + dict.SetIfNotNil("SignaturePolicyHashAlgorithm", pcs.SignaturePolicyHashAlgorithm) + dict.SetIfNotNil("SignaturePolicyCommitmentType", pcs.SignaturePolicyCommitmentType) + dict.SetIfNotNil("SubjectDN", pcs.SubjectDN) + dict.SetIfNotNil("KeyUsage", pcs.KeyUsage) + dict.SetIfNotNil("Issuer", pcs.Issuer) + dict.SetIfNotNil("OID", pcs.OID) + dict.SetIfNotNil("URL", pcs.URL) + dict.SetIfNotNil("URLType", pcs.URLType) + return container } diff --git a/pdf/model/signature_handler.go b/pdf/model/signature_handler.go index 4a15b88d..a1a2f286 100644 --- a/pdf/model/signature_handler.go +++ b/pdf/model/signature_handler.go @@ -10,6 +10,7 @@ import ( "fmt" "io" + "github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/pdf/core" ) @@ -104,35 +105,44 @@ func (r *PdfReader) ValidateSignatures(handlers []SignatureHandler) ([]Signature field *PdfField handler SignatureHandler } - var pairs []*sigFieldPair + var pairs []*sigFieldPair for _, f := range r.AcroForm.AllFields() { if f.V == nil { continue } if d, found := core.GetDict(f.V); found { if name, ok := core.GetNameVal(d.Get("Type")); ok && name == "Sig" { - ind, _ := core.GetIndirect(f.V) // TODO refactoring + ind, found := core.GetIndirect(f.V) + if !found { + common.Log.Debug("ERROR: Signature container is nil") + return nil, ErrTypeCheck + } + sig, err := r.newPdfSignatureFromIndirect(ind) if err != nil { return nil, err } - pairs = append(pairs, &sigFieldPair{sig: sig, field: f}) - } - } - } - for _, pair := range pairs { - for _, handler := range handlers { - if handler.IsApplicable(pair.sig) { - pair.handler = handler - break + // Search for an appropriate handler. + var sigHandler SignatureHandler + for _, handler := range handlers { + if handler.IsApplicable(sig) { + sigHandler = handler + break + } + } + + pairs = append(pairs, &sigFieldPair{ + sig: sig, + field: f, + handler: sigHandler, + }) } } } var results []SignatureValidationResult - for _, pair := range pairs { defaultResult := SignatureValidationResult{ IsSigned: true, diff --git a/pdf/model/writer.go b/pdf/model/writer.go index 6ee85455..7e4b653b 100644 --- a/pdf/model/writer.go +++ b/pdf/model/writer.go @@ -226,9 +226,11 @@ func copyObject(obj core.PdfObject, objectToObjectCopyMap map[core.PdfObject]cor objectToObjectCopyMap[obj] = &newObj return &newObj case *pdfSignDictionary: - newObj := &pdfSignDictionary{PdfObjectDictionary: MakeDict()} - newObj.handler = t.handler - newObj.signature = t.signature + newObj := &pdfSignDictionary{ + PdfObjectDictionary: core.MakeDict(), + handler: t.handler, + signature: t.signature, + } objectToObjectCopyMap[obj] = newObj for _, key := range t.Keys() { val := t.Get(key) From 3233ce9e9cffa4dac253acfaeb51b256f18a2f2a Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Tue, 12 Feb 2019 19:49:37 +0200 Subject: [PATCH 2/3] Further refactoring --- pdf/model/appearance.go | 1 + pdf/model/fields.go | 41 ++++++++------------- pdf/model/sighandler/sighandler_pkcs7.go | 14 +++---- pdf/model/sighandler/sighandler_rsa_sha1.go | 2 +- pdf/model/signature.go | 6 ++- 5 files changed, 29 insertions(+), 35 deletions(-) diff --git a/pdf/model/appearance.go b/pdf/model/appearance.go index 7c2411ec..853765c9 100644 --- a/pdf/model/appearance.go +++ b/pdf/model/appearance.go @@ -33,6 +33,7 @@ func (app *PdfAppearance) ToPdfObject() core.PdfObject { } app.PdfAnnotation.ToPdfObject() app.PdfField.ToPdfObject() + container := app.container d := container.PdfObject.(*core.PdfObjectDictionary) diff --git a/pdf/model/fields.go b/pdf/model/fields.go index 2d026a72..d9489055 100644 --- a/pdf/model/fields.go +++ b/pdf/model/fields.go @@ -245,46 +245,37 @@ func (f *PdfField) String() string { } // ToPdfObject sets the common field elements. -// Note: Call the more field context's ToPdfObject to set both the generic and non-generic information. +// Note: Call the more field context's ToPdfObject to set both the generic and +// non-generic information. func (f *PdfField) ToPdfObject() core.PdfObject { container := f.container d := container.PdfObject.(*core.PdfObjectDictionary) - d.SetIfNotNil("FT", f.FT) - if f.Parent != nil { - d.Set("Parent", f.Parent.GetContainingPdfObject()) - } + // Create an array of the kids (fields or widgets). kids := core.MakeArray() - if f.Kids != nil { - // Create an array of the kids (fields or widgets). - for _, child := range f.Kids { - kids.Append(child.ToPdfObject()) - } + for _, child := range f.Kids { + kids.Append(child.ToPdfObject()) + } + for _, annot := range f.Annotations { + kids.Append(annot.GetContext().ToPdfObject()) } - if f.Annotations != nil { - for _, annot := range f.Annotations { - kids.Append(annot.GetContext().ToPdfObject()) - } + // Set fields. + if f.Parent != nil { + d.SetIfNotNil("Parent", f.Parent.GetContainingPdfObject()) } - - if f.Kids != nil || f.Annotations != nil { + if kids.Len() > 0 { d.Set("Kids", kids) } + d.SetIfNotNil("FT", f.FT) d.SetIfNotNil("T", f.T) d.SetIfNotNil("TU", f.TU) d.SetIfNotNil("TM", f.TM) d.SetIfNotNil("Ff", f.Ff) - if f.V != nil { - d.Set("V", f.V) - } - if f.DV != nil { - d.Set("DV", f.DV) - } - if f.AA != nil { - d.Set("AA", f.AA) - } + d.SetIfNotNil("V", f.V) + d.SetIfNotNil("DV", f.DV) + d.SetIfNotNil("AA", f.AA) return container } diff --git a/pdf/model/sighandler/sighandler_pkcs7.go b/pdf/model/sighandler/sighandler_pkcs7.go index 3c9c6e0d..5409e57a 100644 --- a/pdf/model/sighandler/sighandler_pkcs7.go +++ b/pdf/model/sighandler/sighandler_pkcs7.go @@ -42,8 +42,8 @@ func (a *adobePKCS7Detached) InitSignature(sig *model.PdfSignature) error { sig.Handler = &handler sig.Filter = core.MakeName("Adobe.PPKLite") sig.SubFilter = core.MakeName("adbe.pkcs7.detached") - sig.Reference = nil + digest, err := handler.NewDigest(sig) if err != nil { return err @@ -73,23 +73,21 @@ func (a *adobePKCS7Detached) NewDigest(sig *model.PdfSignature) (model.Hasher, e // Validate validates PdfSignature. func (a *adobePKCS7Detached) Validate(sig *model.PdfSignature, digest model.Hasher) (model.SignatureValidationResult, error) { signed := sig.Contents.Bytes() - - buffer := digest.(*bytes.Buffer) p7, err := pkcs7.Parse(signed) if err != nil { return model.SignatureValidationResult{}, err } + + buffer := digest.(*bytes.Buffer) p7.Content = buffer.Bytes() - err = p7.Verify() - if err != nil { + if err = p7.Verify(); err != nil { return model.SignatureValidationResult{}, err } - result := model.SignatureValidationResult{ + return model.SignatureValidationResult{ IsSigned: true, IsVerified: true, - } - return result, nil + }, nil } // Sign sets the Contents fields. diff --git a/pdf/model/sighandler/sighandler_rsa_sha1.go b/pdf/model/sighandler/sighandler_rsa_sha1.go index 543cdddf..8b924947 100644 --- a/pdf/model/sighandler/sighandler_rsa_sha1.go +++ b/pdf/model/sighandler/sighandler_rsa_sha1.go @@ -45,8 +45,8 @@ func (a *adobeX509RSASHA1) InitSignature(sig *model.PdfSignature) error { //sig.Filter = core.MakeName("Adobe.PPKMS") sig.SubFilter = core.MakeName("adbe.x509.rsa_sha1") sig.Cert = core.MakeString(string(handler.certificate.Raw)) - sig.Reference = nil + digest, err := handler.NewDigest(sig) if err != nil { return err diff --git a/pdf/model/signature.go b/pdf/model/signature.go index 123daad2..ef39c82a 100644 --- a/pdf/model/signature.go +++ b/pdf/model/signature.go @@ -254,8 +254,12 @@ func (sf *PdfSignatureField) ToPdfObject() core.PdfObject { container := sf.container dict := container.PdfObject.(*core.PdfObjectDictionary) + // Set fields. + if sf.V != nil { + dict.Set("V", sf.V.ToPdfObject()) + } + dict.Set("FT", core.MakeName("Sig")) - dict.SetIfNotNil("V", sf.V.ToPdfObject()) dict.SetIfNotNil("Lock", sf.Lock) dict.SetIfNotNil("SV", sf.SV) dict.SetIfNotNil("Kids", sf.Kids) From cda855354a7401299b1a73772f3b2434929ad144 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Tue, 12 Feb 2019 19:57:42 +0200 Subject: [PATCH 3/3] Rename PdfAppearance to PdfSignatureAppearance --- pdf/model/appearance.go | 12 ++++++------ pdf/model/appender.go | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pdf/model/appearance.go b/pdf/model/appearance.go index 853765c9..99a66310 100644 --- a/pdf/model/appearance.go +++ b/pdf/model/appearance.go @@ -7,17 +7,17 @@ package model import "github.com/unidoc/unidoc/pdf/core" -// PdfAppearance contains the common attributes of an appearance form field. -type PdfAppearance struct { +// PdfSignatureAppearance contains the common attributes of an appearance form field. +type PdfSignatureAppearance struct { *PdfField *PdfAnnotationWidget // TODO(gunnsth): Signature is not really part of an appearance. Signature *PdfSignature } -// NewPdfAppearance returns an initialized annotation widget. -func NewPdfAppearance() *PdfAppearance { - app := &PdfAppearance{} +// NewPdfSignatureAppearance returns an initialized annotation widget. +func NewPdfSignatureAppearance() *PdfSignatureAppearance { + app := &PdfSignatureAppearance{} app.PdfField = NewPdfField() app.PdfAnnotationWidget = NewPdfAnnotationWidget() app.PdfField.SetContext(app) @@ -27,7 +27,7 @@ func NewPdfAppearance() *PdfAppearance { } // ToPdfObject implements interface PdfModel. -func (app *PdfAppearance) ToPdfObject() core.PdfObject { +func (app *PdfSignatureAppearance) ToPdfObject() core.PdfObject { if app.Signature != nil { app.V = app.Signature.ToPdfObject() } diff --git a/pdf/model/appender.go b/pdf/model/appender.go index 397b7d8c..6ba1e2a2 100644 --- a/pdf/model/appender.go +++ b/pdf/model/appender.go @@ -382,7 +382,7 @@ func (a *PdfAppender) ReplacePage(pageNum int, page *PdfPage) { // Sign signs a specific page with a digital signature using a specified signature handler. // Returns an Acroform and PdfAppearance that can be used to customize the signature appearance. -func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *PdfAcroForm, appearance *PdfAppearance, err error) { +func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *PdfAcroForm, appearance *PdfSignatureAppearance, err error) { acroForm = a.Reader.AcroForm if acroForm == nil { acroForm = NewPdfAcroForm() @@ -411,7 +411,7 @@ func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *Pdf } a.addNewObjects(sig.container) - appearance = NewPdfAppearance() + appearance = NewPdfSignatureAppearance() fields := append(acroForm.AllFields(), appearance.PdfField) acroForm.Fields = &fields