2018-08-14 17:29:19 +00:00
|
|
|
/*
|
|
|
|
* This file is subject to the terms and conditions defined in
|
|
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package model
|
|
|
|
|
2018-08-16 17:39:51 +00:00
|
|
|
import (
|
2019-01-14 12:39:19 +03:00
|
|
|
"bytes"
|
|
|
|
|
2018-08-16 17:39:51 +00:00
|
|
|
"github.com/unidoc/unidoc/common"
|
|
|
|
"github.com/unidoc/unidoc/pdf/core"
|
|
|
|
)
|
2018-08-14 17:29:19 +00:00
|
|
|
|
2019-01-18 01:19:24 +00:00
|
|
|
var _ core.PdfObject = &pdfSignDictionary{}
|
|
|
|
|
2019-01-17 22:45:10 +00:00
|
|
|
// pdfSignDictionary is used as a wrapper around PdfSignature for digital checksum calculation
|
|
|
|
// and population of /Contents and /ByteRange.
|
2019-01-18 01:19:24 +00:00
|
|
|
// Implements interface core.PdfObject.
|
2018-12-19 18:36:15 +03:00
|
|
|
type pdfSignDictionary struct {
|
|
|
|
*core.PdfObjectDictionary
|
|
|
|
handler *SignatureHandler
|
|
|
|
signature *PdfSignature
|
|
|
|
fileOffset int64
|
|
|
|
contentsOffsetStart int
|
|
|
|
contentsOffsetEnd int
|
|
|
|
byteRangeOffsetStart int
|
|
|
|
byteRangeOffsetEnd int
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSubFilter returns SubFilter value or empty string.
|
|
|
|
func (d *pdfSignDictionary) GetSubFilter() string {
|
|
|
|
obj := d.Get("SubFilter")
|
|
|
|
if obj == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
if sf, found := core.GetNameVal(obj); found {
|
|
|
|
return sf
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteString outputs the object as it is to be written to file.
|
|
|
|
func (d *pdfSignDictionary) WriteString() string {
|
|
|
|
d.contentsOffsetStart = 0
|
|
|
|
d.contentsOffsetEnd = 0
|
|
|
|
d.byteRangeOffsetStart = 0
|
|
|
|
d.byteRangeOffsetEnd = 0
|
2019-01-14 12:39:19 +03:00
|
|
|
out := bytes.NewBuffer(nil)
|
|
|
|
out.WriteString("<<")
|
2018-12-19 18:36:15 +03:00
|
|
|
for _, k := range d.Keys() {
|
|
|
|
v := d.Get(k)
|
|
|
|
switch k {
|
|
|
|
case "ByteRange":
|
2019-01-14 12:39:19 +03:00
|
|
|
out.WriteString(k.WriteString())
|
|
|
|
out.WriteString(" ")
|
|
|
|
d.byteRangeOffsetStart = out.Len()
|
|
|
|
out.WriteString(v.WriteString())
|
2019-01-17 22:45:10 +00:00
|
|
|
out.WriteString(" ")
|
|
|
|
d.byteRangeOffsetEnd = out.Len() - 1
|
2018-12-19 18:36:15 +03:00
|
|
|
case "Contents":
|
2019-01-14 12:39:19 +03:00
|
|
|
out.WriteString(k.WriteString())
|
|
|
|
out.WriteString(" ")
|
|
|
|
d.contentsOffsetStart = out.Len()
|
|
|
|
out.WriteString(v.WriteString())
|
2019-01-17 22:45:10 +00:00
|
|
|
out.WriteString(" ")
|
|
|
|
d.contentsOffsetEnd = out.Len() - 1
|
2018-12-19 18:36:15 +03:00
|
|
|
default:
|
2019-01-14 12:39:19 +03:00
|
|
|
out.WriteString(k.WriteString())
|
|
|
|
out.WriteString(" ")
|
|
|
|
out.WriteString(v.WriteString())
|
2018-12-19 18:36:15 +03:00
|
|
|
}
|
|
|
|
}
|
2019-01-14 12:39:19 +03:00
|
|
|
out.WriteString(">>")
|
|
|
|
return out.String()
|
2018-12-19 18:36:15 +03:00
|
|
|
}
|
|
|
|
|
2018-08-14 17:29:19 +00:00
|
|
|
// PdfSignature represents a PDF signature dictionary and is used for signing via form signature fields.
|
2018-08-15 11:33:48 +00:00
|
|
|
// (Section 12.8, Table 252 - Entries in a signature dictionary p. 475 in PDF32000_2008).
|
2018-08-14 17:29:19 +00:00
|
|
|
type PdfSignature struct {
|
2018-12-19 18:36:15 +03:00
|
|
|
Handler SignatureHandler
|
2018-08-14 17:29:19 +00:00
|
|
|
container *core.PdfIndirectObject
|
2018-08-16 17:39:51 +00:00
|
|
|
// Type: Sig/DocTimeStamp
|
2018-10-23 12:03:47 +00:00
|
|
|
Type *core.PdfObjectName
|
|
|
|
Filter *core.PdfObjectName
|
|
|
|
SubFilter *core.PdfObjectName
|
|
|
|
Contents *core.PdfObjectString
|
|
|
|
Cert core.PdfObject
|
|
|
|
ByteRange *core.PdfObjectArray
|
|
|
|
Reference *core.PdfObjectArray
|
|
|
|
Changes *core.PdfObjectArray
|
|
|
|
Name *core.PdfObjectString
|
|
|
|
M *core.PdfObjectString
|
|
|
|
Location *core.PdfObjectString
|
|
|
|
Reason *core.PdfObjectString
|
|
|
|
ContactInfo *core.PdfObjectString
|
|
|
|
R *core.PdfObjectInteger
|
|
|
|
V *core.PdfObjectInteger
|
|
|
|
PropBuild *core.PdfObjectDictionary
|
|
|
|
PropAuthTime *core.PdfObjectInteger
|
|
|
|
PropAuthType *core.PdfObjectName
|
2018-08-14 17:29:19 +00:00
|
|
|
}
|
|
|
|
|
2018-12-19 18:36:15 +03:00
|
|
|
// NewPdfSignature creates a new PdfSignature object.
|
|
|
|
func NewPdfSignature() *PdfSignature {
|
|
|
|
sig := &PdfSignature{}
|
2019-01-17 22:45:10 +00:00
|
|
|
sigDict := &pdfSignDictionary{
|
|
|
|
PdfObjectDictionary: core.MakeDict(),
|
|
|
|
handler: &sig.Handler,
|
|
|
|
signature: sig,
|
|
|
|
}
|
2018-12-19 18:36:15 +03:00
|
|
|
sig.container = core.MakeIndirectObject(sigDict)
|
|
|
|
return sig
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:29:19 +00:00
|
|
|
// PdfSignatureReference represents a signature reference dictionary.
|
|
|
|
// (Table 253 - p. 477 in PDF32000_2008).
|
|
|
|
type PdfSignatureReference struct {
|
|
|
|
// Type: SigRef
|
|
|
|
TransformMethod *core.PdfObjectName
|
|
|
|
TransformParams *core.PdfObjectDictionary
|
|
|
|
Data core.PdfObject
|
|
|
|
DigestMethod *core.PdfObjectName
|
|
|
|
}
|
|
|
|
|
2018-08-16 17:39:51 +00:00
|
|
|
// GetContainingPdfObject implements interface PdfModel.
|
|
|
|
func (sig *PdfSignature) GetContainingPdfObject() core.PdfObject {
|
|
|
|
return sig.container
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:29:19 +00:00
|
|
|
// ToPdfObject implements interface PdfModel.
|
|
|
|
func (sig *PdfSignature) ToPdfObject() core.PdfObject {
|
|
|
|
container := sig.container
|
2018-12-19 18:36:15 +03:00
|
|
|
|
|
|
|
var dict *core.PdfObjectDictionary
|
|
|
|
if sigDict, ok := container.PdfObject.(*pdfSignDictionary); ok {
|
|
|
|
dict = sigDict.PdfObjectDictionary
|
|
|
|
} else {
|
|
|
|
dict = container.PdfObject.(*core.PdfObjectDictionary)
|
|
|
|
}
|
2018-08-14 17:29:19 +00:00
|
|
|
|
2018-08-16 17:39:51 +00:00
|
|
|
dict.Set("Type", sig.Type)
|
2019-02-12 19:18:39 +02:00
|
|
|
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)
|
2018-08-14 17:29:19 +00:00
|
|
|
|
2019-01-17 22:45:10 +00:00
|
|
|
// NOTE: ByteRange and Contents need to be updated dynamically.
|
2019-02-12 19:18:39 +02:00
|
|
|
// TODO: Currently dynamic update is only in the appender, need to support
|
|
|
|
// in the PdfWriter too for the initial signature on document creation.
|
2018-08-14 17:29:19 +00:00
|
|
|
return container
|
|
|
|
}
|
|
|
|
|
2018-08-16 17:39:51 +00:00
|
|
|
// newPdfSignatureFromIndirect loads a PdfSignature from an indirect object containing the signature dictionary.
|
|
|
|
func (r *PdfReader) newPdfSignatureFromIndirect(container *core.PdfIndirectObject) (*PdfSignature, error) {
|
|
|
|
dict, ok := container.PdfObject.(*core.PdfObjectDictionary)
|
|
|
|
if !ok {
|
|
|
|
common.Log.Debug("ERROR: Signature container not containing a dictionary")
|
|
|
|
return nil, ErrTypeCheck
|
|
|
|
}
|
|
|
|
|
|
|
|
// If PdfSignature already processed and cached, return the cached entry.
|
|
|
|
if sig, cached := r.modelManager.GetModelFromPrimitive(container).(*PdfSignature); cached {
|
|
|
|
return sig, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
sig := &PdfSignature{}
|
|
|
|
sig.container = container
|
|
|
|
|
|
|
|
sig.Type, ok = core.GetName(dict.Get("Type"))
|
|
|
|
if !ok {
|
|
|
|
common.Log.Error("ERROR: Signature Type attribute invalid or missing")
|
|
|
|
return nil, ErrInvalidAttribute
|
|
|
|
}
|
|
|
|
|
|
|
|
sig.Filter, ok = core.GetName(dict.Get("Filter"))
|
|
|
|
if !ok {
|
|
|
|
common.Log.Error("ERROR: Signature Filter attribute invalid or missing")
|
|
|
|
return nil, ErrInvalidAttribute
|
|
|
|
}
|
|
|
|
|
|
|
|
sig.SubFilter, _ = core.GetName(dict.Get("SubFilter"))
|
|
|
|
|
|
|
|
sig.Contents, ok = core.GetString(dict.Get("Contents"))
|
|
|
|
if !ok {
|
|
|
|
common.Log.Error("ERROR: Signature contents missing")
|
|
|
|
return nil, ErrInvalidAttribute
|
|
|
|
}
|
|
|
|
|
|
|
|
sig.Cert = dict.Get("Cert")
|
|
|
|
sig.ByteRange, _ = core.GetArray(dict.Get("ByteRange"))
|
|
|
|
sig.Reference, _ = core.GetArray(dict.Get("Reference"))
|
|
|
|
sig.Changes, _ = core.GetArray(dict.Get("Changes"))
|
|
|
|
sig.Name, _ = core.GetString(dict.Get("Name"))
|
|
|
|
sig.M, _ = core.GetString(dict.Get("M"))
|
|
|
|
sig.Location, _ = core.GetString(dict.Get("Location"))
|
|
|
|
sig.Reason, _ = core.GetString(dict.Get("Reason"))
|
|
|
|
sig.ContactInfo, _ = core.GetString(dict.Get("ContactInfo"))
|
|
|
|
sig.R, _ = core.GetInt(dict.Get("R"))
|
|
|
|
sig.V, _ = core.GetInt(dict.Get("V"))
|
2018-10-23 12:03:47 +00:00
|
|
|
sig.PropBuild, _ = core.GetDict(dict.Get("Prop_Build"))
|
|
|
|
sig.PropAuthTime, _ = core.GetInt(dict.Get("Prop_AuthTime"))
|
|
|
|
sig.PropAuthType, _ = core.GetName(dict.Get("Prop_AuthType"))
|
2018-08-16 17:39:51 +00:00
|
|
|
|
|
|
|
return sig, nil
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:29:19 +00:00
|
|
|
// PdfSignatureField represents a form field that contains a digital signature.
|
2018-08-16 17:39:51 +00:00
|
|
|
// (12.7.4.5 - Signature Fields p. 454 in PDF32000_2008).
|
2018-08-14 17:29:19 +00:00
|
|
|
//
|
|
|
|
// The signature form field serves two primary purposes. 1. Define the form field that will provide the
|
|
|
|
// visual signing properties for display but may also hold information needed when the actual signing
|
|
|
|
// takes place such as signature method. This carries information from the author of the document to the
|
|
|
|
// software that later does signing.
|
|
|
|
//
|
|
|
|
// Filling in (signing) the signature field entails updating at least the V entry and usually the AP entry of the
|
|
|
|
// associated widget annotation. (Exporting a signature field exports the T, V, AP entries)
|
|
|
|
//
|
|
|
|
// The annotation rectangle (Rect) in such a dictionary shall give the position of the field on its page. Signature
|
|
|
|
// fields that are not intended to be visible shall have an annotation rectangle that has zero height and width. PDF
|
|
|
|
// processors shall treat such signatures as not visible. PDF processors shall also treat signatures as not
|
|
|
|
// visible if either the Hidden bit or the NoView bit of the F entry is true
|
|
|
|
//
|
|
|
|
// The location of a signature within a document can have a bearing on its legal meaning. For this reason,
|
|
|
|
// signature fields shall never refer to more than one annotation.
|
|
|
|
type PdfSignatureField struct {
|
|
|
|
container *core.PdfIndirectObject
|
|
|
|
|
2019-01-17 22:45:10 +00:00
|
|
|
V *PdfSignature
|
|
|
|
Lock *core.PdfIndirectObject
|
|
|
|
SV *core.PdfIndirectObject
|
2018-12-19 18:36:15 +03:00
|
|
|
Kids *core.PdfObjectArray
|
2018-08-14 17:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewPdfSignatureField prepares a PdfSignatureField from a PdfSignature.
|
|
|
|
func NewPdfSignatureField(signature *PdfSignature) *PdfSignatureField {
|
2018-10-23 11:43:02 +00:00
|
|
|
return &PdfSignatureField{
|
|
|
|
V: signature,
|
|
|
|
container: core.MakeIndirectObject(core.MakeDict()),
|
|
|
|
}
|
2018-08-14 17:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ToPdfObject implements interface PdfModel.
|
|
|
|
func (sf *PdfSignatureField) ToPdfObject() core.PdfObject {
|
|
|
|
container := sf.container
|
|
|
|
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
|
|
|
|
2019-02-12 19:49:37 +02:00
|
|
|
// Set fields.
|
|
|
|
if sf.V != nil {
|
|
|
|
dict.Set("V", sf.V.ToPdfObject())
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:29:19 +00:00
|
|
|
dict.Set("FT", core.MakeName("Sig"))
|
2019-02-12 19:18:39 +02:00
|
|
|
dict.SetIfNotNil("Lock", sf.Lock)
|
|
|
|
dict.SetIfNotNil("SV", sf.SV)
|
|
|
|
dict.SetIfNotNil("Kids", sf.Kids)
|
2018-08-14 17:29:19 +00:00
|
|
|
// Other standard fields...
|
|
|
|
|
|
|
|
return container
|
|
|
|
}
|
|
|
|
|
|
|
|
// PdfSignatureFieldLock represents signature field lock dictionary.
|
|
|
|
// (Table 233 - p. 455 in PDF32000_2008).
|
|
|
|
type PdfSignatureFieldLock struct {
|
|
|
|
Type core.PdfObject
|
|
|
|
Action *core.PdfObjectName
|
|
|
|
Fields *core.PdfObjectArray
|
|
|
|
P *core.PdfObjectInteger
|
|
|
|
}
|
|
|
|
|
|
|
|
// PdfSignatureFieldSeed represents signature field seed value dictionary.
|
|
|
|
// (Table 234 - p. 455 in PDF32000_2008).
|
|
|
|
type PdfSignatureFieldSeed struct {
|
2018-12-19 18:36:15 +03:00
|
|
|
container *core.PdfIndirectObject
|
2019-01-17 22:45:10 +00:00
|
|
|
|
2018-08-14 17:29:19 +00:00
|
|
|
Ff *core.PdfObjectInteger
|
|
|
|
Filter *core.PdfObjectName
|
|
|
|
SubFilter *core.PdfObjectArray
|
|
|
|
DigestMethod *core.PdfObjectArray
|
|
|
|
V *core.PdfObjectInteger
|
|
|
|
Cert core.PdfObject
|
|
|
|
Reasons *core.PdfObjectArray
|
|
|
|
MDP *core.PdfObjectDictionary
|
|
|
|
TimeStamp *core.PdfObjectDictionary
|
|
|
|
LegalAttestation *core.PdfObjectArray
|
|
|
|
AddRevInfo *core.PdfObjectBool
|
|
|
|
LockDocument *core.PdfObjectName
|
|
|
|
AppearanceFilter *core.PdfObjectString
|
|
|
|
}
|
|
|
|
|
2018-12-19 18:36:15 +03:00
|
|
|
func (pss *PdfSignatureFieldSeed) ToPdfObject() core.PdfObject {
|
|
|
|
container := pss.container
|
|
|
|
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
|
|
|
|
2019-02-12 19:18:39 +02:00
|
|
|
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)
|
|
|
|
|
2018-12-19 18:36:15 +03:00
|
|
|
return container
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:29:19 +00:00
|
|
|
// PdfCertificateSeed represents certificate seed value dictionary.
|
|
|
|
// (Table 235 - p. 457 in PDF32000_2008).
|
|
|
|
type PdfCertificateSeed struct {
|
2018-12-19 18:36:15 +03:00
|
|
|
container *core.PdfIndirectObject
|
2018-08-14 17:29:19 +00:00
|
|
|
// Type
|
|
|
|
Ff *core.PdfObjectInteger
|
|
|
|
Subject *core.PdfObjectArray
|
|
|
|
SignaturePolicyOID *core.PdfObjectString
|
|
|
|
SignaturePolicyHashValue *core.PdfObjectString
|
|
|
|
SignaturePolicyHashAlgorithm *core.PdfObjectName
|
|
|
|
SignaturePolicyCommitmentType *core.PdfObjectArray
|
|
|
|
SubjectDN *core.PdfObjectArray
|
|
|
|
KeyUsage *core.PdfObjectArray
|
|
|
|
Issuer *core.PdfObjectArray
|
|
|
|
OID *core.PdfObjectArray
|
|
|
|
URL *core.PdfObjectString
|
|
|
|
URLType *core.PdfObjectName
|
|
|
|
}
|
2018-12-19 18:36:15 +03:00
|
|
|
|
|
|
|
func (pcs *PdfCertificateSeed) ToPdfObject() core.PdfObject {
|
|
|
|
container := pcs.container
|
|
|
|
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
2019-02-12 19:18:39 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2018-12-19 18:36:15 +03:00
|
|
|
return container
|
|
|
|
}
|