From 68d20968d3648a2f6583a57bf76ff3e84b430609 Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Wed, 3 Oct 2018 07:59:15 +0300 Subject: [PATCH] core: move encryption dictionary encoder and decoder to it's own type --- pdf/core/crypt.go | 117 ++-------------------------------- pdf/core/crypt_handlers.go | 126 +++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 111 deletions(-) diff --git a/pdf/core/crypt.go b/pdf/core/crypt.go index 68466392..4f441093 100644 --- a/pdf/core/crypt.go +++ b/pdf/core/crypt.go @@ -50,7 +50,7 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessP case cryptFilterAESV3: vers.Major, vers.Minor = 2, 0 crypter.encrypt.V = 5 - crypter.encryptStd.R = 6 // TODO(dennwc): a way to set R=5? + crypter.encryptStd.R = 6 } if cf != nil { crypter.encrypt.Length = cf.KeyLength() * 8 @@ -82,20 +82,8 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessP if err != nil { return nil, nil, err } - // Generate encryption parameters - if crypter.encryptStd.R < 5 { - ed.Set("O", MakeString(string(crypter.encryptStd.O))) - ed.Set("U", MakeString(string(crypter.encryptStd.U))) - } else { // R >= 5 - ed.Set("O", MakeString(string(crypter.encryptStd.O))) - ed.Set("U", MakeString(string(crypter.encryptStd.U))) - ed.Set("OE", MakeString(string(crypter.encryptStd.OE))) - ed.Set("UE", MakeString(string(crypter.encryptStd.UE))) - ed.Set("EncryptMetadata", MakeBool(crypter.encryptStd.EncryptMetadata)) - if crypter.encryptStd.R > 5 { - ed.Set("Perms", MakeString(string(crypter.encryptStd.Perms))) - } - } + // encode parameters generated by the Standard security handler + crypter.encryptStd.EncodeTo(ed) if crypter.encrypt.V >= 4 { if err := crypter.saveCryptFilters(ed); err != nil { return nil, nil, err @@ -138,8 +126,6 @@ func (crypt *PdfCrypt) newEncyptDict() *PdfObjectDictionary { ed.Set("Filter", MakeName("Standard")) ed.Set("V", MakeInteger(int64(crypt.encrypt.V))) ed.Set("Length", MakeInteger(int64(crypt.encrypt.Length))) - ed.Set("P", MakeInteger(int64(crypt.encryptStd.P))) - ed.Set("R", MakeInteger(int64(crypt.encryptStd.R))) return ed } @@ -195,17 +181,6 @@ type encryptDict struct { EFF string // The filter that shall be used when decrypting embedded file streams. } -// 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. - O, U []byte - OE, UE []byte // R=6 - - P AccessPermissions - Perms []byte // An encrypted copy of P (16 bytes). Used to verify permissions. R=6 - EncryptMetadata bool // Indicates whether the document-level metadata stream shall be encrypted. -} - // AccessPermissions is a bitmask of access permissions for a PDF file. type AccessPermissions uint32 @@ -429,89 +404,9 @@ func PdfCryptNewDecrypt(parser *PdfParser, ed, trailer *PdfObjectDictionary) (*P } } - R, ok := ed.Get("R").(*PdfObjectInteger) - if !ok { - return crypter, 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 crypter, fmt.Errorf("Invalid R (%d)", *R) - } - crypter.encryptStd.R = int(*R) - - O, ok := ed.Get("O").(*PdfObjectString) - if !ok { - return crypter, errors.New("Encrypt dictionary missing O") - } - if crypter.encryptStd.R == 5 || crypter.encryptStd.R == 6 { - // the spec says =48 bytes, but Acrobat pads them out longer - if len(O.Str()) < 48 { - return crypter, fmt.Errorf("Length(O) < 48 (%d)", len(O.Str())) - } - } else if len(O.Str()) != 32 { - return crypter, fmt.Errorf("Length(O) != 32 (%d)", len(O.Str())) - } - crypter.encryptStd.O = O.Bytes() - - U, ok := ed.Get("U").(*PdfObjectString) - if !ok { - return crypter, errors.New("Encrypt dictionary missing U") - } - if crypter.encryptStd.R == 5 || crypter.encryptStd.R == 6 { - // the spec says =48 bytes, but Acrobat pads them out longer - if len(U.Str()) < 48 { - return crypter, fmt.Errorf("Length(U) < 48 (%d)", len(U.Str())) - } - } else if len(U.Str()) != 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.Str())) - //return crypter, errors.New("Length(U) != 32") - } - crypter.encryptStd.U = U.Bytes() - - if crypter.encryptStd.R >= 5 { - OE, ok := ed.Get("OE").(*PdfObjectString) - if !ok { - return crypter, errors.New("Encrypt dictionary missing OE") - } - if len(OE.Str()) != 32 { - return crypter, fmt.Errorf("Length(OE) != 32 (%d)", len(OE.Str())) - } - crypter.encryptStd.OE = OE.Bytes() - - UE, ok := ed.Get("UE").(*PdfObjectString) - if !ok { - return crypter, errors.New("Encrypt dictionary missing UE") - } - if len(UE.Str()) != 32 { - return crypter, fmt.Errorf("Length(UE) != 32 (%d)", len(UE.Str())) - } - crypter.encryptStd.UE = UE.Bytes() - } - - P, ok := ed.Get("P").(*PdfObjectInteger) - if !ok { - return crypter, errors.New("Encrypt dictionary missing permissions attr") - } - crypter.encryptStd.P = AccessPermissions(*P) - - if crypter.encryptStd.R == 6 { - Perms, ok := ed.Get("Perms").(*PdfObjectString) - if !ok { - return crypter, errors.New("Encrypt dictionary missing Perms") - } - if len(Perms.Str()) != 16 { - return crypter, fmt.Errorf("Length(Perms) != 16 (%d)", len(Perms.Str())) - } - crypter.encryptStd.Perms = Perms.Bytes() - } - - em, ok := ed.Get("EncryptMetadata").(*PdfObjectBool) - if ok { - crypter.encryptStd.EncryptMetadata = bool(*em) - } else { - crypter.encryptStd.EncryptMetadata = true // True by default. + // decode Standard security handler parameters + if err := crypter.encryptStd.DecodeFrom(ed); err != nil { + return crypter, err } // Default: empty ID. diff --git a/pdf/core/crypt_handlers.go b/pdf/core/crypt_handlers.go index ca587b7c..8c46b467 100644 --- a/pdf/core/crypt_handlers.go +++ b/pdf/core/crypt_handlers.go @@ -5,6 +5,13 @@ 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. @@ -15,3 +22,122 @@ type stdSecurityHandler interface { // 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.Get("O").(*PdfObjectString) + 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.Str()) < 48 { + return fmt.Errorf("Length(O) < 48 (%d)", len(O.Str())) + } + } else if len(O.Str()) != 32 { + return fmt.Errorf("Length(O) != 32 (%d)", len(O.Str())) + } + d.O = O.Bytes() + + U, ok := ed.Get("U").(*PdfObjectString) + 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.Str()) < 48 { + return fmt.Errorf("Length(U) < 48 (%d)", len(U.Str())) + } + } else if len(U.Str()) != 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.Str())) + //return crypter, errors.New("Length(U) != 32") + } + d.U = U.Bytes() + + if d.R >= 5 { + OE, ok := ed.Get("OE").(*PdfObjectString) + if !ok { + return errors.New("Encrypt dictionary missing OE") + } + if len(OE.Str()) != 32 { + return fmt.Errorf("Length(OE) != 32 (%d)", len(OE.Str())) + } + d.OE = OE.Bytes() + + UE, ok := ed.Get("UE").(*PdfObjectString) + if !ok { + return errors.New("Encrypt dictionary missing UE") + } + if len(UE.Str()) != 32 { + return fmt.Errorf("Length(UE) != 32 (%d)", len(UE.Str())) + } + d.UE = UE.Bytes() + } + + 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.Get("Perms").(*PdfObjectString) + if !ok { + return errors.New("Encrypt dictionary missing Perms") + } + if len(Perms.Str()) != 16 { + return fmt.Errorf("Length(Perms) != 16 (%d)", len(Perms.Str())) + } + d.Perms = Perms.Bytes() + } + + em, ok := ed.Get("EncryptMetadata").(*PdfObjectBool) + if ok { + d.EncryptMetadata = bool(*em) + } else { + d.EncryptMetadata = true // True by default. + } + return nil +}