From 84284c88ec91dfcf6d531975e8e8362806b7d6da Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Mon, 8 Oct 2018 01:04:56 +0300 Subject: [PATCH] security: add missing license headers, more documentation --- pdf/core/crypt.go | 12 +++++-- pdf/core/security/auth.go | 11 ++++-- pdf/core/security/crypt/filter_aesv2.go | 12 +++++++ pdf/core/security/crypt/filter_aesv3.go | 14 +++++++- pdf/core/security/crypt/filter_v2.go | 15 ++++++++ pdf/core/security/crypt/filters.go | 47 ++++++++++++++----------- pdf/core/security/permissions.go | 26 +++++++++----- pdf/core/security/standard_r4.go | 9 ++++- pdf/core/security/standard_r6.go | 11 +++--- pdf/core/security/standard_r6_test.go | 5 +++ pdf/model/writer.go | 3 +- 11 files changed, 123 insertions(+), 42 deletions(-) diff --git a/pdf/core/crypt.go b/pdf/core/crypt.go index 929afa88..840761eb 100644 --- a/pdf/core/crypt.go +++ b/pdf/core/crypt.go @@ -17,14 +17,20 @@ import ( crypto "github.com/unidoc/unidoc/pdf/core/security/crypt" ) +// Version represents a version of a PDF standard. type Version struct { Major int Minor int } +// EncryptInfo contains an information generated by the document encrypter. type EncryptInfo struct { + // Version is minimal PDF version that supports specified encryption algorithm. Version - Encrypt *PdfObjectDictionary + // Encrypt is an encryption dictionary that contains all necessary parameters. + // It should be stored in all copies of the document trailer. + Encrypt *PdfObjectDictionary + // ID0 and ID1 are IDs used in the trailer. Older algorithms such as RC4 uses them for encryption. ID0, ID1 string } @@ -57,7 +63,7 @@ func PdfCryptNewEncrypt(cf crypto.Filter, userPass, ownerPass []byte, perm secur crypter.streamFilter = defaultFilter crypter.stringFilter = defaultFilter } - ed := crypter.newEncyptDict() + ed := crypter.newEncryptDict() // Prepare the ID object for the trailer. hashcode := md5.Sum([]byte(time.Now().Format(time.RFC850))) @@ -244,7 +250,7 @@ func decodeCryptFilter(cf *crypto.FilterDict, d *PdfObjectDictionary) error { return nil } -func (crypt *PdfCrypt) newEncyptDict() *PdfObjectDictionary { +func (crypt *PdfCrypt) newEncryptDict() *PdfObjectDictionary { // Generate the encryption dictionary. ed := MakeDict() ed.Set("Filter", MakeName("Standard")) diff --git a/pdf/core/security/auth.go b/pdf/core/security/auth.go index 1e6cd28d..c199c410 100644 --- a/pdf/core/security/auth.go +++ b/pdf/core/security/auth.go @@ -1,9 +1,16 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.md', which is part of this source code package. + */ + package security // AuthEvent is an event type that triggers authentication. type AuthEvent string const ( - EventDocOpen = AuthEvent("DocOpen") // document open - EventEFOpen = AuthEvent("EFOpen") // embedded file open + // EventDocOpen is an event triggered when opening the document. + EventDocOpen = AuthEvent("DocOpen") + // EventEFOpen is an event triggered when accessing an embedded file. + EventEFOpen = AuthEvent("EFOpen") ) diff --git a/pdf/core/security/crypt/filter_aesv2.go b/pdf/core/security/crypt/filter_aesv2.go index 409f9eac..dc6b95d7 100644 --- a/pdf/core/security/crypt/filter_aesv2.go +++ b/pdf/core/security/crypt/filter_aesv2.go @@ -1,3 +1,8 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.md', which is part of this source code package. + */ + package crypt import "fmt" @@ -22,28 +27,35 @@ func newFilterAESV2(d FilterDict) (Filter, error) { return filterAESV2{}, nil } +var _ Filter = filterAESV2{} + // filterAESV2 is an AES-based filter (128 bit key, PDF 1.6) type filterAESV2 struct { filterAES } +// PDFVersion implements Filter interface. func (filterAESV2) PDFVersion() [2]int { return [2]int{1, 5} } +// HandlerVersion implements Filter interface. func (filterAESV2) HandlerVersion() (V, R int) { V, R = 4, 4 return } +// Name implements Filter interface. func (filterAESV2) Name() string { return "AESV2" } +// KeyLength implements Filter interface. func (filterAESV2) KeyLength() int { return 128 / 8 } +// MakeKey implements Filter interface. func (filterAESV2) MakeKey(objNum, genNum uint32, ekey []byte) ([]byte, error) { return makeKeyV2(objNum, genNum, ekey, true) } diff --git a/pdf/core/security/crypt/filter_aesv3.go b/pdf/core/security/crypt/filter_aesv3.go index 9ca7eb26..5af52b39 100644 --- a/pdf/core/security/crypt/filter_aesv3.go +++ b/pdf/core/security/crypt/filter_aesv3.go @@ -1,3 +1,8 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.md', which is part of this source code package. + */ + package crypt import ( @@ -146,28 +151,35 @@ func (filterAES) DecryptBytes(buf []byte, okey []byte) ([]byte, error) { return buf, nil } +var _ Filter = filterAESV3{} + // filterAESV3 is an AES-based filter (256 bit key, PDF 2.0) type filterAESV3 struct { filterAES } +// PDFVersion implements Filter interface. func (filterAESV3) PDFVersion() [2]int { return [2]int{2, 0} } +// HandlerVersion implements Filter interface. func (filterAESV3) HandlerVersion() (V, R int) { V, R = 5, 6 return } +// Name implements Filter interface. func (filterAESV3) Name() string { return "AESV3" } +// KeyLength implements Filter interface. func (filterAESV3) KeyLength() int { return 256 / 8 } +// MakeKey implements Filter interface. func (filterAESV3) MakeKey(_, _ uint32, ekey []byte) ([]byte, error) { - return ekey, nil + return ekey, nil // document encryption key == object encryption key } diff --git a/pdf/core/security/crypt/filter_v2.go b/pdf/core/security/crypt/filter_v2.go index d1eb5794..e0a7100c 100644 --- a/pdf/core/security/crypt/filter_v2.go +++ b/pdf/core/security/crypt/filter_v2.go @@ -1,3 +1,8 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.md', which is part of this source code package. + */ + package crypt import ( @@ -21,6 +26,7 @@ func NewFilterV2(length int) Filter { return f } +// newFilterV2 creates a RC4-based filter from a Filter dictionary. func newFilterV2(d FilterDict) (Filter, error) { if d.Length%8 != 0 { return nil, fmt.Errorf("Crypt filter length not multiple of 8 (%d)", d.Length) @@ -74,32 +80,40 @@ func makeKeyV2(objNum, genNum uint32, ekey []byte, isAES bool) ([]byte, error) { return hashb, nil } +var _ Filter = filterV2{} + // filterV2 is a RC4-based filter type filterV2 struct { length int } +// PDFVersion implements Filter interface. func (f filterV2) PDFVersion() [2]int { return [2]int{} // TODO(dennwc): unspecified; check what it should be } +// HandlerVersion implements Filter interface. func (f filterV2) HandlerVersion() (V, R int) { V, R = 2, 3 return } +// Name implements Filter interface. func (filterV2) Name() string { return "V2" } +// KeyLength implements Filter interface. func (f filterV2) KeyLength() int { return f.length } +// MakeKey implements Filter interface. func (f filterV2) MakeKey(objNum, genNum uint32, ekey []byte) ([]byte, error) { return makeKeyV2(objNum, genNum, ekey, false) } +// EncryptBytes implements Filter interface. func (filterV2) EncryptBytes(buf []byte, okey []byte) ([]byte, error) { // Standard RC4 algorithm. ciph, err := rc4.NewCipher(okey) @@ -112,6 +126,7 @@ func (filterV2) EncryptBytes(buf []byte, okey []byte) ([]byte, error) { return buf, nil } +// DecryptBytes implements Filter interface. func (filterV2) DecryptBytes(buf []byte, okey []byte) ([]byte, error) { // Standard RC4 algorithm. ciph, err := rc4.NewCipher(okey) diff --git a/pdf/core/security/crypt/filters.go b/pdf/core/security/crypt/filters.go index 77ba0fd4..3f275710 100644 --- a/pdf/core/security/crypt/filters.go +++ b/pdf/core/security/crypt/filters.go @@ -1,3 +1,8 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.md', which is part of this source code package. + */ + package crypt import ( @@ -13,6 +18,27 @@ var ( // filterFunc is used to construct crypt filters from CryptFilter dictionary type filterFunc func(d FilterDict) (Filter, error) +// Filter is a common interface for crypt filter methods. +type Filter interface { + // Name returns a name of the filter that should be used in CFM field of Encrypt dictionary. + Name() string + // KeyLength returns a length of the encryption key in bytes. + KeyLength() int + // PDFVersion reports the minimal version of PDF document that introduced this filter. + PDFVersion() [2]int + // HandlerVersion reports V and R parameters that should be used for this filter. + HandlerVersion() (V, R int) + // MakeKey generates a object encryption key based on file encryption key and object numbers. + // Used only for legacy filters - AESV3 doesn't change the key for each object. + MakeKey(objNum, genNum uint32, fkey []byte) ([]byte, error) + // EncryptBytes encrypts a buffer using object encryption key, as returned by MakeKey. + // Implementation may reuse a buffer and encrypt data in-place. + EncryptBytes(p []byte, okey []byte) ([]byte, error) + // DecryptBytes decrypts a buffer using object encryption key, as returned by MakeKey. + // Implementation may reuse a buffer and decrypt data in-place. + DecryptBytes(p []byte, okey []byte) ([]byte, error) +} + // NewFilter creates CryptFilter from a corresponding dictionary. func NewFilter(d FilterDict) (Filter, error) { fnc, err := getFilter(d.CFM) @@ -56,27 +82,6 @@ func getFilter(name string) (filterFunc, error) { return f, nil } -// Filter is a common interface for crypt filter methods. -type Filter interface { - // Name returns a name of the filter that should be used in CFM field of Encrypt dictionary. - Name() string - // KeyLength returns a length of the encryption key in bytes. - KeyLength() int - // PDFVersion reports the minimal version of PDF document that introduced this filter. - PDFVersion() [2]int - // HandlerVersion reports V and R parameters that should be used for this filter. - HandlerVersion() (V, R int) - // MakeKey generates a object encryption key based on file encryption key and object numbers. - // Used only for legacy filters - AESV3 doesn't change the key for each object. - MakeKey(objNum, genNum uint32, fkey []byte) ([]byte, error) - // EncryptBytes encrypts a buffer using object encryption key, as returned by MakeKey. - // Implementation may reuse a buffer and encrypt data in-place. - EncryptBytes(p []byte, okey []byte) ([]byte, error) - // DecryptBytes decrypts a buffer using object encryption key, as returned by MakeKey. - // Implementation may reuse a buffer and decrypt data in-place. - DecryptBytes(p []byte, okey []byte) ([]byte, error) -} - type filterIdentity struct{} func (filterIdentity) PDFVersion() [2]int { diff --git a/pdf/core/security/permissions.go b/pdf/core/security/permissions.go index 9b1ed981..5bb569a0 100644 --- a/pdf/core/security/permissions.go +++ b/pdf/core/security/permissions.go @@ -1,3 +1,8 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.md', which is part of this source code package. + */ + package security import "math" @@ -9,17 +14,22 @@ const ( // PermOwner grants all permissions. PermOwner = Permissions(math.MaxUint32) - PermPrinting = Permissions(1 << 2) // bit 3 - PermModify = Permissions(1 << 3) // bit 4 - PermExtractGraphics = Permissions(1 << 4) // bit 5 - PermAnnotate = Permissions(1 << 5) // bit 6 + // PermPrinting allows printing the document with a low quality. + PermPrinting = Permissions(1 << 2) + // PermModify allows to modify the document. + PermModify = Permissions(1 << 3) + // PermExtractGraphics allows to extract graphics from the document. + PermExtractGraphics = Permissions(1 << 4) + // PermAnnotate allows annotating the document. + PermAnnotate = Permissions(1 << 5) // PermFillForms allow form filling, if annotation is disabled? If annotation enabled, is not looked at. - PermFillForms = Permissions(1 << 8) // bit 9 - PermDisabilityExtract = Permissions(1 << 9) // bit 10 // TODO: not clear what this means! + PermFillForms = Permissions(1 << 8) + // PermDisabilityExtract allows to extract graphics in accessibility mode. + PermDisabilityExtract = Permissions(1 << 9) // PermRotateInsert allows rotating, editing page order. - PermRotateInsert = Permissions(1 << 10) // bit 11 + PermRotateInsert = Permissions(1 << 10) // PermFullPrintQuality limits print quality (lowres), assuming Printing bit is set. - PermFullPrintQuality = Permissions(1 << 11) // bit 12 + PermFullPrintQuality = Permissions(1 << 11) ) // Allowed checks if a set of permissions can be granted. diff --git a/pdf/core/security/standard_r4.go b/pdf/core/security/standard_r4.go index dda15662..c4d655c6 100644 --- a/pdf/core/security/standard_r4.go +++ b/pdf/core/security/standard_r4.go @@ -26,6 +26,10 @@ func NewHandlerR4(id0 string, length int) StdHandler { return stdHandlerR4{ID0: id0, Length: length} } +// stdHandlerR4 is a standard security handler for R<=4. +// It uses RC4 and MD5 to generate encryption parameters. +// This legacy handler also requires Length parameter from +// Encrypt dictionary and ID0 from the trailer. type stdHandlerR4 struct { Length int ID0 string @@ -260,7 +264,7 @@ func (sh stdHandlerR4) alg6(d *StdEncryptDict, upass []byte) ([]byte, error) { } // alg7 authenticates the owner password and returns the document encryption key. -//// It returns an nil key in case authentication failed. +// It returns an nil key in case authentication failed. func (sh stdHandlerR4) alg7(d *StdEncryptDict, opass []byte) ([]byte, error) { encKey := sh.alg3Key(d.R, opass) @@ -298,6 +302,8 @@ func (sh stdHandlerR4) alg7(d *StdEncryptDict, opass []byte) ([]byte, error) { return ekey, nil } +// GenerateParams generates and sets O and U parameters for the encryption dictionary. +// It expects R, P and EncryptMetadata fields to be set. func (sh stdHandlerR4) GenerateParams(d *StdEncryptDict, opass, upass []byte) ([]byte, error) { // Make the O and U objects. O, err := sh.alg3(d.R, upass, opass) @@ -320,6 +326,7 @@ func (sh stdHandlerR4) GenerateParams(d *StdEncryptDict, opass, upass []byte) ([ return ekey, nil } +// Authenticate implements StdHandler interface. func (sh stdHandlerR4) Authenticate(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error) { // Try owner password. // May not be necessary if only want to get all contents. diff --git a/pdf/core/security/standard_r6.go b/pdf/core/security/standard_r6.go index f16bade0..3ead14c9 100644 --- a/pdf/core/security/standard_r6.go +++ b/pdf/core/security/standard_r6.go @@ -27,6 +27,7 @@ func NewHandlerR6() StdHandler { } // stdHandlerR6 is an implementation of standard security handler with R=5 and R=6. +// Both revisions are expected to be used with AES encryption filters. type stdHandlerR6 struct{} // alg2a retrieves the encryption key from an encrypted document (R >= 5). @@ -115,10 +116,10 @@ func (sh stdHandlerR6) alg2a(d *StdEncryptDict, pass []byte) ([]byte, Permission return fkey, perm, nil } -// alg2b_R5 computes a hash for R=5, used in a deprecated extension. +// alg2bR5 computes a hash for R=5, used in a deprecated extension. // It's used the same way as a hash described in Algorithm 2.B, but it doesn't use the original password // and the user key to calculate the hash. -func alg2b_R5(data []byte) []byte { +func alg2bR5(data []byte) []byte { h := sha256.New() h.Write(data) return h.Sum(nil) @@ -216,7 +217,7 @@ func alg2b(data, pwd, userKey []byte) []byte { // alg2b computes a hash for R=5 and R=6. func (sh stdHandlerR6) alg2b(R int, data, pwd, userKey []byte) []byte { if R == 5 { - return alg2b_R5(data) + return alg2bR5(data) } return alg2b(data, pwd, userKey) } @@ -422,9 +423,10 @@ func (sh stdHandlerR6) alg13(d *StdEncryptDict, fkey []byte) error { return nil } -// generateR6 is the algorithm opposite to alg2a (R>=5). +// GenerateParams is the algorithm opposite to alg2a (R>=5). // It generates U,O,UE,OE,Perms fields using AESv3 encryption. // There is no algorithm number assigned to this function in the spec. +// It expects R, P and EncryptMetadata fields to be set. func (sh stdHandlerR6) GenerateParams(d *StdEncryptDict, opass, upass []byte) ([]byte, error) { ekey := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, ekey); err != nil { @@ -461,6 +463,7 @@ func (sh stdHandlerR6) GenerateParams(d *StdEncryptDict, opass, upass []byte) ([ return ekey, nil } +// Authenticate implements StdHandler interface. func (sh stdHandlerR6) Authenticate(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error) { return sh.alg2a(d, pass) } diff --git a/pdf/core/security/standard_r6_test.go b/pdf/core/security/standard_r6_test.go index 4f94d186..1e230c69 100644 --- a/pdf/core/security/standard_r6_test.go +++ b/pdf/core/security/standard_r6_test.go @@ -1,3 +1,8 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.md', which is part of this source code package. + */ + package security import ( diff --git a/pdf/model/writer.go b/pdf/model/writer.go index cf7624e8..627b8c3b 100644 --- a/pdf/model/writer.go +++ b/pdf/model/writer.go @@ -17,12 +17,11 @@ import ( "io" "strings" - "github.com/unidoc/unidoc/pdf/core/security/crypt" - "github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common/license" . "github.com/unidoc/unidoc/pdf/core" "github.com/unidoc/unidoc/pdf/core/security" + "github.com/unidoc/unidoc/pdf/core/security/crypt" "github.com/unidoc/unidoc/pdf/model/fonts" )