diff --git a/pdf/core/crypt.go b/pdf/core/crypt.go index 4f441093..b17887c0 100644 --- a/pdf/core/crypt.go +++ b/pdf/core/crypt.go @@ -10,10 +10,10 @@ import ( "crypto/rand" "errors" "fmt" - "math" "time" "github.com/unidoc/unidoc/common" + "github.com/unidoc/unidoc/pdf/core/security" ) type Version struct { @@ -28,11 +28,11 @@ type EncryptInfo struct { } // PdfCryptNewEncrypt makes the document crypt handler based on a specified crypt filter. -func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessPermissions) (*PdfCrypt, *EncryptInfo, error) { +func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm security.Permissions) (*PdfCrypt, *EncryptInfo, error) { crypter := &PdfCrypt{ encryptedObjects: make(map[PdfObject]bool), cryptFilters: make(cryptFilters), - encryptStd: stdEncryptDict{ + encryptStd: security.StdEncryptDict{ P: perm, EncryptMetadata: true, }, @@ -83,7 +83,7 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessP return nil, nil, err } // encode parameters generated by the Standard security handler - crypter.encryptStd.EncodeTo(ed) + encodeEncryptStd(&crypter.encryptStd, ed) if crypter.encrypt.V >= 4 { if err := crypter.saveCryptFilters(ed); err != nil { return nil, nil, err @@ -97,12 +97,115 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessP }, nil } +// encodeEncryptStd encodes fields of standard security handler to an Encrypt dictionary. +func encodeEncryptStd(d *security.StdEncryptDict, ed *PdfObjectDictionary) { + ed.Set("R", MakeInteger(int64(d.R))) + ed.Set("P", MakeInteger(int64(d.P))) + + ed.Set("O", MakeStringFromBytes(d.O)) + ed.Set("U", MakeStringFromBytes(d.U)) + if d.R >= 5 { + ed.Set("OE", MakeStringFromBytes(d.OE)) + ed.Set("UE", MakeStringFromBytes(d.UE)) + ed.Set("EncryptMetadata", MakeBool(d.EncryptMetadata)) + if d.R > 5 { + ed.Set("Perms", MakeStringFromBytes(d.Perms)) + } + } +} + +// decodeEncryptStd decodes fields of standard security handler from an Encrypt dictionary. +func decodeEncryptStd(d *security.StdEncryptDict, ed *PdfObjectDictionary) error { + // TODO(dennwc): this code is too verbose; maybe use reflection to populate fields and validate afterwards? + R, ok := ed.Get("R").(*PdfObjectInteger) + if !ok { + return errors.New("encrypt dictionary missing R") + } + // TODO(dennwc): according to spec, R should be validated according to V value + if *R < 2 || *R > 6 { + return fmt.Errorf("invalid R (%d)", *R) + } + d.R = int(*R) + + O, ok := ed.GetString("O") + if !ok { + return errors.New("encrypt dictionary missing O") + } + if d.R == 5 || d.R == 6 { + // the spec says =48 bytes, but Acrobat pads them out longer + if len(O) < 48 { + return fmt.Errorf("Length(O) < 48 (%d)", len(O)) + } + } else if len(O) != 32 { + return fmt.Errorf("Length(O) != 32 (%d)", len(O)) + } + d.O = []byte(O) + + U, ok := ed.GetString("U") + if !ok { + return errors.New("encrypt dictionary missing U") + } + if d.R == 5 || d.R == 6 { + // the spec says =48 bytes, but Acrobat pads them out longer + if len(U) < 48 { + return fmt.Errorf("Length(U) < 48 (%d)", len(U)) + } + } else if len(U) != 32 { + // Strictly this does not cause an error. + // If O is OK and others then can still read the file. + common.Log.Debug("Warning: Length(U) != 32 (%d)", len(U)) + //return crypter, errors.New("Length(U) != 32") + } + d.U = []byte(U) + + if d.R >= 5 { + OE, ok := ed.GetString("OE") + if !ok { + return errors.New("encrypt dictionary missing OE") + } else if len(OE) != 32 { + return fmt.Errorf("Length(OE) != 32 (%d)", len(OE)) + } + d.OE = []byte(OE) + + UE, ok := ed.GetString("UE") + if !ok { + return errors.New("encrypt dictionary missing UE") + } else if len(UE) != 32 { + return fmt.Errorf("Length(UE) != 32 (%d)", len(UE)) + } + d.UE = []byte(UE) + } + + P, ok := ed.Get("P").(*PdfObjectInteger) + if !ok { + return errors.New("encrypt dictionary missing permissions attr") + } + d.P = security.Permissions(*P) + + if d.R == 6 { + Perms, ok := ed.GetString("Perms") + if !ok { + return errors.New("encrypt dictionary missing Perms") + } else if len(Perms) != 16 { + return fmt.Errorf("Length(Perms) != 16 (%d)", len(Perms)) + } + d.Perms = []byte(Perms) + } + + if em, ok := ed.Get("EncryptMetadata").(*PdfObjectBool); ok { + d.EncryptMetadata = bool(*em) + } else { + d.EncryptMetadata = true // True by default. + } + return nil +} + // PdfCrypt provides PDF encryption/decryption support. // The PDF standard supports encryption of strings and streams (Section 7.6). // TODO (v3): Consider unexporting. type PdfCrypt struct { encrypt encryptDict - encryptStd stdEncryptDict + encryptStd security.StdEncryptDict id0 string encryptionKey []byte @@ -117,7 +220,6 @@ type PdfCrypt struct { parser *PdfParser decryptedObjNum map[int]struct{} - ivAESZero []byte // a zero buffer used as an initialization vector for AES } func (crypt *PdfCrypt) newEncyptDict() *PdfObjectDictionary { @@ -181,35 +283,6 @@ type encryptDict struct { EFF string // The filter that shall be used when decrypting embedded file streams. } -// AccessPermissions is a bitmask of access permissions for a PDF file. -type AccessPermissions uint32 - -const ( - // PermOwner grants all permissions. - PermOwner = AccessPermissions(math.MaxUint32) - - PermPrinting = AccessPermissions(1 << 2) // bit 3 - PermModify = AccessPermissions(1 << 3) // bit 4 - PermExtractGraphics = AccessPermissions(1 << 4) // bit 5 - PermAnnotate = AccessPermissions(1 << 5) // bit 6 - // PermFillForms allow form filling, if annotation is disabled? If annotation enabled, is not looked at. - PermFillForms = AccessPermissions(1 << 8) // bit 9 - PermDisabilityExtract = AccessPermissions(1 << 9) // bit 10 // TODO: not clear what this means! - // PermRotateInsert allows rotating, editing page order. - PermRotateInsert = AccessPermissions(1 << 10) // bit 11 - // PermFullPrintQuality limits print quality (lowres), assuming Printing bit is set. - PermFullPrintQuality = AccessPermissions(1 << 11) // bit 12 -) - -// Allowed checks if a set of permissions can be granted. -func (p AccessPermissions) Allowed(p2 AccessPermissions) bool { - return p&p2 == p2 -} - -const padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF" + - "\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C" + - "\xA9\xFE\x64\x53\x69\x7A" - // StandardCryptFilter is a default name for a standard crypt filter. const StandardCryptFilter = "StdCF" @@ -405,7 +478,7 @@ func PdfCryptNewDecrypt(parser *PdfParser, ed, trailer *PdfObjectDictionary) (*P } // decode Standard security handler parameters - if err := crypter.encryptStd.DecodeFrom(ed); err != nil { + if err := decodeEncryptStd(&crypter.encryptStd, ed); err != nil { return crypter, err } @@ -428,18 +501,15 @@ func PdfCryptNewDecrypt(parser *PdfParser, ed, trailer *PdfObjectDictionary) (*P } // GetAccessPermissions returns the PDF access permissions as an AccessPermissions object. -func (crypt *PdfCrypt) GetAccessPermissions() AccessPermissions { +func (crypt *PdfCrypt) GetAccessPermissions() security.Permissions { return crypt.encryptStd.P } -func (crypt *PdfCrypt) securityHandler() stdSecurityHandler { +func (crypt *PdfCrypt) securityHandler() security.StdHandler { if crypt.encryptStd.R >= 5 { - return stdHandlerR6{} - } - return stdHandlerR4{ - ID0: crypt.id0, - Length: crypt.encrypt.Length, + return security.NewHandlerR6() } + return security.NewHandlerR4(crypt.id0, crypt.encrypt.Length) } // Check whether the specified password can be used to decrypt the document. @@ -464,7 +534,7 @@ func (crypt *PdfCrypt) authenticate(password []byte) (bool, error) { // The bool flag indicates that the user can access and can view the file. // The AccessPermissions shows what access the user has for editing etc. // An error is returned if there was a problem performing the authentication. -func (crypt *PdfCrypt) checkAccessRights(password []byte) (bool, AccessPermissions, error) { +func (crypt *PdfCrypt) checkAccessRights(password []byte) (bool, security.Permissions, error) { h := crypt.securityHandler() // TODO(dennwc): it computes an encryption key as well; if necessary, define a new interface method to optimize this fkey, perm, err := h.Authenticate(&crypt.encryptStd, password) diff --git a/pdf/core/crypt_handlers.go b/pdf/core/crypt_handlers.go deleted file mode 100644 index aace2e83..00000000 --- a/pdf/core/crypt_handlers.go +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is subject to the terms and conditions defined in - * file 'LICENSE.md', which is part of this source code package. - */ - -package core - -import ( - "errors" - "fmt" - - "github.com/unidoc/unidoc/common" -) - -type stdSecurityHandler interface { - // GenerateParams uses owner and user passwords to set encryption parameters and generate an encryption key. - // It assumes that R, P and EncryptMetadata are already set. - GenerateParams(d *stdEncryptDict, ownerPass, userPass []byte) ([]byte, error) - - // Authenticate uses encryption dictionary parameters and the password to calculate - // the document encryption key. It also returns permissions that should be granted to a user. - // In case of failed authentication, it returns empty key and zero permissions with no error. - Authenticate(d *stdEncryptDict, pass []byte) ([]byte, AccessPermissions, error) -} - -// stdEncryptDict is a set of additional fields used in standard encryption dictionary. -type stdEncryptDict struct { - R int // (Required) A number specifying which revision of the standard security handler shall be used. - - P AccessPermissions - EncryptMetadata bool // Indicates whether the document-level metadata stream shall be encrypted. - - // set by security handlers: - - O, U []byte - OE, UE []byte // R=6 - Perms []byte // An encrypted copy of P (16 bytes). Used to verify permissions. R=6 -} - -func (d *stdEncryptDict) EncodeTo(ed *PdfObjectDictionary) { - ed.Set("R", MakeInteger(int64(d.R))) - ed.Set("P", MakeInteger(int64(d.P))) - - ed.Set("O", MakeStringFromBytes(d.O)) - ed.Set("U", MakeStringFromBytes(d.U)) - if d.R >= 5 { - ed.Set("OE", MakeStringFromBytes(d.OE)) - ed.Set("UE", MakeStringFromBytes(d.UE)) - ed.Set("EncryptMetadata", MakeBool(d.EncryptMetadata)) - if d.R > 5 { - ed.Set("Perms", MakeStringFromBytes(d.Perms)) - } - } -} - -func (d *stdEncryptDict) DecodeFrom(ed *PdfObjectDictionary) error { - // TODO(dennwc): this code is too verbose; maybe use reflection to populate fields and validate afterwards? - R, ok := ed.Get("R").(*PdfObjectInteger) - if !ok { - return errors.New("encrypt dictionary missing R") - } - // TODO(dennwc): according to spec, R should be validated according to V value - if *R < 2 || *R > 6 { - return fmt.Errorf("invalid R (%d)", *R) - } - d.R = int(*R) - - O, ok := ed.GetString("O") - if !ok { - return errors.New("encrypt dictionary missing O") - } - if d.R == 5 || d.R == 6 { - // the spec says =48 bytes, but Acrobat pads them out longer - if len(O) < 48 { - return fmt.Errorf("Length(O) < 48 (%d)", len(O)) - } - } else if len(O) != 32 { - return fmt.Errorf("Length(O) != 32 (%d)", len(O)) - } - d.O = []byte(O) - - U, ok := ed.GetString("U") - if !ok { - return errors.New("encrypt dictionary missing U") - } - if d.R == 5 || d.R == 6 { - // the spec says =48 bytes, but Acrobat pads them out longer - if len(U) < 48 { - return fmt.Errorf("Length(U) < 48 (%d)", len(U)) - } - } else if len(U) != 32 { - // Strictly this does not cause an error. - // If O is OK and others then can still read the file. - common.Log.Debug("Warning: Length(U) != 32 (%d)", len(U)) - //return crypter, errors.New("Length(U) != 32") - } - d.U = []byte(U) - - if d.R >= 5 { - OE, ok := ed.GetString("OE") - if !ok { - return errors.New("encrypt dictionary missing OE") - } else if len(OE) != 32 { - return fmt.Errorf("Length(OE) != 32 (%d)", len(OE)) - } - d.OE = []byte(OE) - - UE, ok := ed.GetString("UE") - if !ok { - return errors.New("encrypt dictionary missing UE") - } else if len(UE) != 32 { - return fmt.Errorf("Length(UE) != 32 (%d)", len(UE)) - } - d.UE = []byte(UE) - } - - P, ok := ed.Get("P").(*PdfObjectInteger) - if !ok { - return errors.New("encrypt dictionary missing permissions attr") - } - d.P = AccessPermissions(*P) - - if d.R == 6 { - Perms, ok := ed.GetString("Perms") - if !ok { - return errors.New("encrypt dictionary missing Perms") - } else if len(Perms) != 16 { - return fmt.Errorf("Length(Perms) != 16 (%d)", len(Perms)) - } - d.Perms = []byte(Perms) - } - - if em, ok := ed.Get("EncryptMetadata").(*PdfObjectBool); ok { - d.EncryptMetadata = bool(*em) - } else { - d.EncryptMetadata = true // True by default. - } - return nil -} diff --git a/pdf/core/crypt_test.go b/pdf/core/crypt_test.go index 8c81c0bd..6cb56a62 100644 --- a/pdf/core/crypt_test.go +++ b/pdf/core/crypt_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/unidoc/unidoc/common" + "github.com/unidoc/unidoc/pdf/core/security" ) func init() { @@ -24,7 +25,7 @@ func TestDecryption1(t *testing.T) { V: 2, Length: 128, }, - encryptStd: stdEncryptDict{ + encryptStd: security.StdEncryptDict{ R: 3, P: 0xfffff0c0, EncryptMetadata: true, diff --git a/pdf/core/parser.go b/pdf/core/parser.go index 359139d2..9cfea000 100644 --- a/pdf/core/parser.go +++ b/pdf/core/parser.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/unidoc/unidoc/common" + "github.com/unidoc/unidoc/pdf/core/security" ) // Regular Expressions for parsing and identifying object signatures. @@ -1641,11 +1642,11 @@ func (parser *PdfParser) Decrypt(password []byte) (bool, error) { // The bool flag indicates that the user can access and view the file. // The AccessPermissions shows what access the user has for editing etc. // An error is returned if there was a problem performing the authentication. -func (parser *PdfParser) CheckAccessRights(password []byte) (bool, AccessPermissions, error) { +func (parser *PdfParser) CheckAccessRights(password []byte) (bool, security.Permissions, error) { // Also build the encryption/decryption key. if parser.crypter == nil { // If the crypter is not set, the file is not encrypted and we can assume full access permissions. - return true, PermOwner, nil + return true, security.PermOwner, nil } return parser.crypter.checkAccessRights(password) } diff --git a/pdf/core/ecb.go b/pdf/core/security/ecb.go similarity index 98% rename from pdf/core/ecb.go rename to pdf/core/security/ecb.go index b96cdfa3..20aeebfd 100644 --- a/pdf/core/ecb.go +++ b/pdf/core/security/ecb.go @@ -3,7 +3,7 @@ * file 'LICENSE.md', which is part of this source code package. */ -package core +package security import "crypto/cipher" diff --git a/pdf/core/security/handlers.go b/pdf/core/security/handlers.go new file mode 100644 index 00000000..a72e6f2f --- /dev/null +++ b/pdf/core/security/handlers.go @@ -0,0 +1,32 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.md', which is part of this source code package. + */ + +package security + +// StdHandler is an interface for standard security handlers. +type StdHandler interface { + // GenerateParams uses owner and user passwords to set encryption parameters and generate an encryption key. + // It assumes that R, P and EncryptMetadata are already set. + GenerateParams(d *StdEncryptDict, ownerPass, userPass []byte) ([]byte, error) + + // Authenticate uses encryption dictionary parameters and the password to calculate + // the document encryption key. It also returns permissions that should be granted to a user. + // In case of failed authentication, it returns empty key and zero permissions with no error. + Authenticate(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error) +} + +// StdEncryptDict is a set of additional fields used in standard encryption dictionary. +type StdEncryptDict struct { + R int // (Required) A number specifying which revision of the standard security handler shall be used. + + P Permissions + EncryptMetadata bool // Indicates whether the document-level metadata stream shall be encrypted. + + // set by security handlers: + + O, U []byte + OE, UE []byte // R=6 + Perms []byte // An encrypted copy of P (16 bytes). Used to verify permissions. R=6 +} diff --git a/pdf/core/security/permissions.go b/pdf/core/security/permissions.go new file mode 100644 index 00000000..9b1ed981 --- /dev/null +++ b/pdf/core/security/permissions.go @@ -0,0 +1,28 @@ +package security + +import "math" + +// Permissions is a bitmask of access permissions for a PDF file. +type Permissions uint32 + +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 + // 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! + // PermRotateInsert allows rotating, editing page order. + PermRotateInsert = Permissions(1 << 10) // bit 11 + // PermFullPrintQuality limits print quality (lowres), assuming Printing bit is set. + PermFullPrintQuality = Permissions(1 << 11) // bit 12 +) + +// Allowed checks if a set of permissions can be granted. +func (p Permissions) Allowed(p2 Permissions) bool { + return p&p2 == p2 +} diff --git a/pdf/core/crypt_handlers_r4.go b/pdf/core/security/standard_r4.go similarity index 91% rename from pdf/core/crypt_handlers_r4.go rename to pdf/core/security/standard_r4.go index 4445d385..dda15662 100644 --- a/pdf/core/crypt_handlers_r4.go +++ b/pdf/core/security/standard_r4.go @@ -3,7 +3,7 @@ * file 'LICENSE.md', which is part of this source code package. */ -package core +package security import ( "bytes" @@ -15,7 +15,16 @@ import ( "github.com/unidoc/unidoc/common" ) -var _ stdSecurityHandler = stdHandlerR4{} +var _ StdHandler = stdHandlerR4{} + +const padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF" + + "\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C" + + "\xA9\xFE\x64\x53\x69\x7A" + +// NewHandlerR4 creates a new standard security handler for R<=4. +func NewHandlerR4(id0 string, length int) StdHandler { + return stdHandlerR4{ID0: id0, Length: length} +} type stdHandlerR4 struct { Length int @@ -40,7 +49,7 @@ func (sh stdHandlerR4) paddedPass(pass []byte) []byte { } // alg2 computes an encryption key. -func (sh stdHandlerR4) alg2(d *stdEncryptDict, pass []byte) []byte { +func (sh stdHandlerR4) alg2(d *StdEncryptDict, pass []byte) []byte { common.Log.Trace("alg2") key := sh.paddedPass(pass) @@ -212,7 +221,7 @@ func (sh stdHandlerR4) alg5(ekey []byte, upass []byte) ([]byte, error) { // alg6 authenticates the user password and returns the document encryption key. // It returns an nil key in case authentication failed. -func (sh stdHandlerR4) alg6(d *stdEncryptDict, upass []byte) ([]byte, error) { +func (sh stdHandlerR4) alg6(d *StdEncryptDict, upass []byte) ([]byte, error) { var ( uo []byte err error @@ -252,7 +261,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. -func (sh stdHandlerR4) alg7(d *stdEncryptDict, opass []byte) ([]byte, error) { +func (sh stdHandlerR4) alg7(d *StdEncryptDict, opass []byte) ([]byte, error) { encKey := sh.alg3Key(d.R, opass) decrypted := make([]byte, len(d.O)) @@ -289,7 +298,7 @@ func (sh stdHandlerR4) alg7(d *stdEncryptDict, opass []byte) ([]byte, error) { return ekey, nil } -func (sh stdHandlerR4) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([]byte, error) { +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) if err != nil { @@ -311,7 +320,7 @@ func (sh stdHandlerR4) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([ return ekey, nil } -func (sh stdHandlerR4) Authenticate(d *stdEncryptDict, pass []byte) ([]byte, AccessPermissions, error) { +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. // (user pass needs to be known or empty). diff --git a/pdf/core/crypt_handlers_r4_test.go b/pdf/core/security/standard_r4_test.go similarity index 98% rename from pdf/core/crypt_handlers_r4_test.go rename to pdf/core/security/standard_r4_test.go index 53a10806..441b7dd7 100644 --- a/pdf/core/crypt_handlers_r4_test.go +++ b/pdf/core/security/standard_r4_test.go @@ -3,7 +3,7 @@ * file 'LICENSE.md', which is part of this source code package. */ -package core +package security import ( "github.com/unidoc/unidoc/common" @@ -53,7 +53,7 @@ func TestAlg2(t *testing.T) { 0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}), Length: 128, } - d := &stdEncryptDict{ + d := &StdEncryptDict{ R: 3, P: 0xfffff0c0, EncryptMetadata: true, @@ -110,7 +110,7 @@ func TestAlg5(t *testing.T) { 0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}), Length: 128, } - d := &stdEncryptDict{ + d := &StdEncryptDict{ R: 3, P: 0xfffff0c0, EncryptMetadata: true, diff --git a/pdf/core/crypt_handlers_r6.go b/pdf/core/security/standard_r6.go similarity index 90% rename from pdf/core/crypt_handlers_r6.go rename to pdf/core/security/standard_r6.go index 915c3e26..f16bade0 100644 --- a/pdf/core/crypt_handlers_r6.go +++ b/pdf/core/security/standard_r6.go @@ -3,7 +3,7 @@ * file 'LICENSE.md', which is part of this source code package. */ -package core +package security import ( "bytes" @@ -19,13 +19,19 @@ import ( "math" ) -var _ stdSecurityHandler = stdHandlerR6{} +var _ StdHandler = stdHandlerR6{} +// NewHandlerR6 creates a new standard security handler for R=5 and R=6. +func NewHandlerR6() StdHandler { + return stdHandlerR6{} +} + +// stdHandlerR6 is an implementation of standard security handler with R=5 and R=6. type stdHandlerR6 struct{} // alg2a retrieves the encryption key from an encrypted document (R >= 5). // 7.6.4.3.2 Algorithm 2.A (page 83) -func (sh stdHandlerR6) alg2a(d *stdEncryptDict, pass []byte) ([]byte, AccessPermissions, error) { +func (sh stdHandlerR6) alg2a(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error) { // O & U: 32 byte hash + 8 byte Validation Salt + 8 byte Key Salt // step a: Unicode normalization @@ -46,7 +52,7 @@ func (sh stdHandlerR6) alg2a(d *stdEncryptDict, pass []byte) ([]byte, AccessPerm ekey []byte // encrypted file key ukey []byte // user key; set only when using owner's password ) - var perm AccessPermissions + var perm Permissions if len(h) != 0 { // owner password valid perm = PermOwner @@ -217,7 +223,7 @@ func (sh stdHandlerR6) alg2b(R int, data, pwd, userKey []byte) []byte { // alg8 computes the encryption dictionary's U (user password) and UE (user encryption) values (R>=5). // 7.6.4.4.6 Algorithm 8 (page 86) -func (sh stdHandlerR6) alg8(d *stdEncryptDict, ekey []byte, upass []byte) error { +func (sh stdHandlerR6) alg8(d *StdEncryptDict, ekey []byte, upass []byte) error { // step a: compute U (user password) var rbuf [16]byte if _, err := io.ReadFull(rand.Reader, rbuf[:]); err != nil { @@ -263,7 +269,7 @@ func (sh stdHandlerR6) alg8(d *stdEncryptDict, ekey []byte, upass []byte) error // alg9 computes the encryption dictionary's O (owner password) and OE (owner encryption) values (R>=5). // 7.6.4.4.7 Algorithm 9 (page 86) -func (sh stdHandlerR6) alg9(d *stdEncryptDict, ekey []byte, opass []byte) error { +func (sh stdHandlerR6) alg9(d *StdEncryptDict, ekey []byte, opass []byte) error { // step a: compute O (owner password) var rbuf [16]byte if _, err := io.ReadFull(rand.Reader, rbuf[:]); err != nil { @@ -312,7 +318,7 @@ func (sh stdHandlerR6) alg9(d *stdEncryptDict, ekey []byte, opass []byte) error // alg10 computes the encryption dictionary's Perms (permissions) value (R=6). // 7.6.4.4.8 Algorithm 10 (page 87) -func (sh stdHandlerR6) alg10(d *stdEncryptDict, ekey []byte) error { +func (sh stdHandlerR6) alg10(d *StdEncryptDict, ekey []byte) error { // step a: extend permissions to 64 bits perms := uint64(uint32(d.P)) | (math.MaxUint32 << 32) @@ -352,7 +358,7 @@ func (sh stdHandlerR6) alg10(d *stdEncryptDict, ekey []byte) error { } // alg11 authenticates the user password (R >= 5) and returns the hash. -func (sh stdHandlerR6) alg11(d *stdEncryptDict, upass []byte) ([]byte, error) { +func (sh stdHandlerR6) alg11(d *StdEncryptDict, upass []byte) ([]byte, error) { str := make([]byte, len(upass)+8) i := copy(str, upass) i += copy(str[i:], d.U[32:40]) // user Validation Salt @@ -367,7 +373,7 @@ func (sh stdHandlerR6) alg11(d *stdEncryptDict, upass []byte) ([]byte, error) { // alg12 authenticates the owner password (R >= 5) and returns the hash. // 7.6.4.4.10 Algorithm 12 (page 87) -func (sh stdHandlerR6) alg12(d *stdEncryptDict, opass []byte) ([]byte, error) { +func (sh stdHandlerR6) alg12(d *StdEncryptDict, opass []byte) ([]byte, error) { str := make([]byte, len(opass)+8+48) i := copy(str, opass) i += copy(str[i:], d.O[32:40]) // owner Validation Salt @@ -383,7 +389,7 @@ func (sh stdHandlerR6) alg12(d *stdEncryptDict, opass []byte) ([]byte, error) { // alg13 validates user permissions (P+EncryptMetadata vs Perms) for R=6. // 7.6.4.4.11 Algorithm 13 (page 87) -func (sh stdHandlerR6) alg13(d *stdEncryptDict, fkey []byte) error { +func (sh stdHandlerR6) alg13(d *StdEncryptDict, fkey []byte) error { perms := make([]byte, 16) copy(perms, d.Perms[:16]) @@ -398,7 +404,7 @@ func (sh stdHandlerR6) alg13(d *stdEncryptDict, fkey []byte) error { if !bytes.Equal(perms[9:12], []byte("adb")) { return errors.New("decoded permissions are invalid") } - p := AccessPermissions(binary.LittleEndian.Uint32(perms[0:4])) + p := Permissions(binary.LittleEndian.Uint32(perms[0:4])) if p != d.P { return errors.New("permissions validation failed") } @@ -419,7 +425,7 @@ func (sh stdHandlerR6) alg13(d *stdEncryptDict, fkey []byte) error { // generateR6 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. -func (sh stdHandlerR6) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([]byte, error) { +func (sh stdHandlerR6) GenerateParams(d *StdEncryptDict, opass, upass []byte) ([]byte, error) { ekey := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, ekey); err != nil { return nil, err @@ -455,6 +461,6 @@ func (sh stdHandlerR6) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([ return ekey, nil } -func (sh stdHandlerR6) Authenticate(d *stdEncryptDict, pass []byte) ([]byte, AccessPermissions, error) { +func (sh stdHandlerR6) Authenticate(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error) { return sh.alg2a(d, pass) } diff --git a/pdf/core/crypt_handlers_r6_test.go b/pdf/core/security/standard_r6_test.go similarity index 98% rename from pdf/core/crypt_handlers_r6_test.go rename to pdf/core/security/standard_r6_test.go index 16e46638..4f94d186 100644 --- a/pdf/core/crypt_handlers_r6_test.go +++ b/pdf/core/security/standard_r6_test.go @@ -1,4 +1,4 @@ -package core +package security import ( "bytes" @@ -60,7 +60,7 @@ func TestStdHandlerR6(t *testing.T) { c := c t.Run(c.Name, func(t *testing.T) { sh := stdHandlerR6{} // V=5 - d := &stdEncryptDict{ + d := &StdEncryptDict{ R: R, P: perms, EncryptMetadata: c.EncMeta, } diff --git a/pdf/model/reader.go b/pdf/model/reader.go index 58672544..d1eba160 100644 --- a/pdf/model/reader.go +++ b/pdf/model/reader.go @@ -13,6 +13,7 @@ import ( "github.com/unidoc/unidoc/common" . "github.com/unidoc/unidoc/pdf/core" + "github.com/unidoc/unidoc/pdf/core/security" ) // PdfReader represents a PDF file reader. It is a frontend to the lower level parsing mechanism and provides @@ -110,7 +111,7 @@ func (this *PdfReader) Decrypt(password []byte) (bool, error) { // The bool flag indicates that the user can access and view the file. // The AccessPermissions shows what access the user has for editing etc. // An error is returned if there was a problem performing the authentication. -func (this *PdfReader) CheckAccessRights(password []byte) (bool, AccessPermissions, error) { +func (this *PdfReader) CheckAccessRights(password []byte) (bool, security.Permissions, error) { return this.parser.CheckAccessRights(password) } diff --git a/pdf/model/writer.go b/pdf/model/writer.go index 6cefc924..47a02e9b 100644 --- a/pdf/model/writer.go +++ b/pdf/model/writer.go @@ -20,6 +20,7 @@ import ( "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/model/fonts" ) @@ -648,7 +649,7 @@ func (this *PdfWriter) updateObjectNumbers() { // EncryptOptions represents encryption options for an output PDF. type EncryptOptions struct { - Permissions AccessPermissions + Permissions security.Permissions Algorithm EncryptionAlgorithm } @@ -670,7 +671,7 @@ func (this *PdfWriter) Encrypt(userPass, ownerPass []byte, options *EncryptOptio if options != nil { algo = options.Algorithm } - perm := PermOwner + perm := security.PermOwner if options != nil { perm = options.Permissions }