/* * 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 ( "crypto/md5" "crypto/rand" "errors" "fmt" "time" "github.com/unidoc/unipdf/v3/common" "github.com/unidoc/unipdf/v3/core/security" crypto "github.com/unidoc/unipdf/v3/core/security/crypt" ) // EncryptInfo contains an information generated by the document encrypter. type EncryptInfo struct { // Version is minimal PDF version that supports specified encryption algorithm. Version // 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 } // PdfCryptNewEncrypt makes the document crypt handler based on a specified crypt filter. func PdfCryptNewEncrypt(cf crypto.Filter, userPass, ownerPass []byte, perm security.Permissions) (*PdfCrypt, *EncryptInfo, error) { crypter := &PdfCrypt{ encryptedObjects: make(map[PdfObject]bool), cryptFilters: make(cryptFilters), encryptStd: security.StdEncryptDict{ P: perm, EncryptMetadata: true, }, } var vers Version if cf != nil { v := cf.PDFVersion() vers.Major, vers.Minor = v[0], v[1] V, R := cf.HandlerVersion() crypter.encrypt.V = V crypter.encryptStd.R = R crypter.encrypt.Length = cf.KeyLength() * 8 } const ( defaultFilter = stdCryptFilter ) crypter.cryptFilters[defaultFilter] = cf if crypter.encrypt.V >= 4 { crypter.streamFilter = defaultFilter crypter.stringFilter = defaultFilter } ed := crypter.newEncryptDict() // Prepare the ID object for the trailer. hashcode := md5.Sum([]byte(time.Now().Format(time.RFC850))) id0 := string(hashcode[:]) b := make([]byte, 100) rand.Read(b) hashcode = md5.Sum(b) id1 := string(hashcode[:]) common.Log.Trace("Random b: % x", b) common.Log.Trace("Gen Id 0: % x", id0) crypter.id0 = string(id0) err := crypter.generateParams(userPass, ownerPass) if err != nil { return nil, nil, err } // encode parameters generated by the Standard security handler encodeEncryptStd(&crypter.encryptStd, ed) if crypter.encrypt.V >= 4 { if err := crypter.saveCryptFilters(ed); err != nil { return nil, nil, err } } return crypter, &EncryptInfo{ Version: vers, Encrypt: ed, ID0: id0, ID1: id1, }, nil } // PdfCrypt provides PDF encryption/decryption support. // The PDF standard supports encryption of strings and streams (Section 7.6). type PdfCrypt struct { encrypt encryptDict encryptStd security.StdEncryptDict id0 string encryptionKey []byte decryptedObjects map[PdfObject]bool encryptedObjects map[PdfObject]bool authenticated bool // Crypt filters (V4). cryptFilters cryptFilters streamFilter string stringFilter string parser *PdfParser decryptedObjNum map[int]struct{} } // 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 } func decodeCryptFilter(cf *crypto.FilterDict, d *PdfObjectDictionary) error { // If Type present, should be CryptFilter. if typename, ok := d.Get("Type").(*PdfObjectName); ok { if string(*typename) != "CryptFilter" { return fmt.Errorf("CF dict type != CryptFilter (%s)", typename) } } // Method. name, ok := d.Get("CFM").(*PdfObjectName) if !ok { return fmt.Errorf("unsupported crypt filter (None)") } cf.CFM = string(*name) // Auth event if event, ok := d.Get("AuthEvent").(*PdfObjectName); ok { cf.AuthEvent = security.AuthEvent(*event) } else { cf.AuthEvent = security.EventDocOpen } if length, ok := d.Get("Length").(*PdfObjectInteger); ok { cf.Length = int(*length) } return nil } func (crypt *PdfCrypt) newEncryptDict() *PdfObjectDictionary { // Generate the encryption dictionary. ed := MakeDict() ed.Set("Filter", MakeName("Standard")) ed.Set("V", MakeInteger(int64(crypt.encrypt.V))) ed.Set("Length", MakeInteger(int64(crypt.encrypt.Length))) return ed } // String returns a descriptive information string about the encryption method used. func (crypt *PdfCrypt) String() string { if crypt == nil { return "" } // TODO(dennwc): define a String method on CF str := crypt.encrypt.Filter + " - " if crypt.encrypt.V == 0 { str += "Undocumented algorithm" } else if crypt.encrypt.V == 1 { // RC4 or AES (bits: 40) str += "RC4: 40 bits" } else if crypt.encrypt.V == 2 { str += fmt.Sprintf("RC4: %d bits", crypt.encrypt.Length) } else if crypt.encrypt.V == 3 { str += "Unpublished algorithm" } else if crypt.encrypt.V >= 4 { // Look at CF, StmF, StrF str += fmt.Sprintf("Stream filter: %s - String filter: %s", crypt.streamFilter, crypt.stringFilter) str += "; Crypt filters:" for name, cf := range crypt.cryptFilters { str += fmt.Sprintf(" - %s: %s (%d)", name, cf.Name(), cf.KeyLength()) } } perms := crypt.GetAccessPermissions() str += fmt.Sprintf(" - %#v", perms) return str } // encryptDict is a set of field common to all encryption dictionaries. type encryptDict struct { Filter string // (Required) The name of the preferred security handler for this document. V int // (Required) A code specifying the algorithm to be used in encrypting and decrypting the document. SubFilter string // Completely specifies the format and interpretation of the encryption dictionary. Length int // The length of the encryption key, in bits. StmF string // The filter that shall be used by default when decrypting streams. StrF string // The filter that shall be used when decrypting all strings in the document. EFF string // The filter that shall be used when decrypting embedded file streams. CF map[string]crypto.FilterDict // Crypt filters dictionary. } // stdCryptFilter is a default name for a standard crypt filter. const stdCryptFilter = "StdCF" func newCryptFiltersV2(length int) cryptFilters { return cryptFilters{ stdCryptFilter: crypto.NewFilterV2(length), } } // cryptFilters is a map of crypt filter name and underlying CryptFilter info. type cryptFilters map[string]crypto.Filter // loadCryptFilters loads crypt filter information from the encryption dictionary (V>=4). func (crypt *PdfCrypt) loadCryptFilters(ed *PdfObjectDictionary) error { crypt.cryptFilters = cryptFilters{} obj := ed.Get("CF") obj = TraceToDirectObject(obj) // TODO: may need to resolve reference... if ref, isRef := obj.(*PdfObjectReference); isRef { o, err := crypt.parser.LookupByReference(*ref) if err != nil { common.Log.Debug("Error looking up CF reference") return err } obj = TraceToDirectObject(o) } cf, ok := obj.(*PdfObjectDictionary) if !ok { common.Log.Debug("Invalid CF, type: %T", obj) return errors.New("invalid CF") } for _, name := range cf.Keys() { v := cf.Get(name) if ref, isRef := v.(*PdfObjectReference); isRef { o, err := crypt.parser.LookupByReference(*ref) if err != nil { common.Log.Debug("Error lookup up dictionary reference") return err } v = TraceToDirectObject(o) } dict, ok := v.(*PdfObjectDictionary) if !ok { return fmt.Errorf("invalid dict in CF (name %s) - not a dictionary but %T", name, v) } if name == "Identity" { common.Log.Debug("ERROR - Cannot overwrite the identity filter - Trying next") continue } var cfd crypto.FilterDict if err := decodeCryptFilter(&cfd, dict); err != nil { return err } cf, err := crypto.NewFilter(cfd) if err != nil { return err } crypt.cryptFilters[string(name)] = cf } // Cannot be overwritten. crypt.cryptFilters["Identity"] = crypto.NewIdentity() // StrF strings filter. crypt.stringFilter = "Identity" if strf, ok := ed.Get("StrF").(*PdfObjectName); ok { if _, exists := crypt.cryptFilters[string(*strf)]; !exists { return fmt.Errorf("crypt filter for StrF not specified in CF dictionary (%s)", *strf) } crypt.stringFilter = string(*strf) } // StmF streams filter. crypt.streamFilter = "Identity" if stmf, ok := ed.Get("StmF").(*PdfObjectName); ok { if _, exists := crypt.cryptFilters[string(*stmf)]; !exists { return fmt.Errorf("crypt filter for StmF not specified in CF dictionary (%s)", *stmf) } crypt.streamFilter = string(*stmf) } return nil } func encodeCryptFilter(cf crypto.Filter, event security.AuthEvent) *PdfObjectDictionary { if event == "" { event = security.EventDocOpen } v := MakeDict() v.Set("Type", MakeName("CryptFilter")) // optional v.Set("AuthEvent", MakeName(string(event))) v.Set("CFM", MakeName(cf.Name())) v.Set("Length", MakeInteger(int64(cf.KeyLength()))) return v } // saveCryptFilters saves crypt filter information to the encryption dictionary (V>=4). func (crypt *PdfCrypt) saveCryptFilters(ed *PdfObjectDictionary) error { if crypt.encrypt.V < 4 { return errors.New("can only be used with V>=4") } cf := MakeDict() ed.Set("CF", cf) for name, filter := range crypt.cryptFilters { if name == "Identity" { continue } v := encodeCryptFilter(filter, "") cf.Set(PdfObjectName(name), v) } ed.Set("StrF", MakeName(crypt.stringFilter)) ed.Set("StmF", MakeName(crypt.streamFilter)) return nil } // PdfCryptNewDecrypt makes the document crypt handler based on the encryption dictionary // and trailer dictionary. Returns an error on failure to process. func PdfCryptNewDecrypt(parser *PdfParser, ed, trailer *PdfObjectDictionary) (*PdfCrypt, error) { crypter := &PdfCrypt{ authenticated: false, decryptedObjects: make(map[PdfObject]bool), encryptedObjects: make(map[PdfObject]bool), decryptedObjNum: make(map[int]struct{}), parser: parser, } filter, ok := ed.Get("Filter").(*PdfObjectName) if !ok { common.Log.Debug("ERROR Crypt dictionary missing required Filter field!") return crypter, errors.New("required crypt field Filter missing") } if *filter != "Standard" { common.Log.Debug("ERROR Unsupported filter (%s)", *filter) return crypter, errors.New("unsupported Filter") } crypter.encrypt.Filter = string(*filter) if subfilter, ok := ed.Get("SubFilter").(*PdfObjectString); ok { crypter.encrypt.SubFilter = subfilter.Str() common.Log.Debug("Using subfilter %s", subfilter) } if L, ok := ed.Get("Length").(*PdfObjectInteger); ok { if (*L % 8) != 0 { common.Log.Debug("ERROR Invalid encryption length") return crypter, errors.New("invalid encryption length") } crypter.encrypt.Length = int(*L) } else { crypter.encrypt.Length = 40 } crypter.encrypt.V = 0 if v, ok := ed.Get("V").(*PdfObjectInteger); ok { V := int(*v) crypter.encrypt.V = V if V >= 1 && V <= 2 { // Default algorithm is V2. crypter.cryptFilters = newCryptFiltersV2(crypter.encrypt.Length) } else if V >= 4 && V <= 5 { if err := crypter.loadCryptFilters(ed); err != nil { return crypter, err } } else { common.Log.Debug("ERROR Unsupported encryption algo V = %d", V) return crypter, errors.New("unsupported algorithm") } } // decode Standard security handler parameters if err := decodeEncryptStd(&crypter.encryptStd, ed); err != nil { return crypter, err } // Default: empty ID. // Strictly, if file is encrypted, the ID should always be specified // but clearly not everyone is following the specification. id0 := "" if idArray, ok := trailer.Get("ID").(*PdfObjectArray); ok && idArray.Len() >= 1 { id0obj, ok := GetString(idArray.Get(0)) if !ok { return crypter, errors.New("invalid trailer ID") } id0 = id0obj.Str() } else { common.Log.Debug("Trailer ID array missing or invalid!") } crypter.id0 = id0 return crypter, nil } // GetAccessPermissions returns the PDF access permissions as an AccessPermissions object. func (crypt *PdfCrypt) GetAccessPermissions() security.Permissions { return crypt.encryptStd.P } func (crypt *PdfCrypt) securityHandler() security.StdHandler { if crypt.encryptStd.R >= 5 { return security.NewHandlerR6() } return security.NewHandlerR4(crypt.id0, crypt.encrypt.Length) } // Check whether the specified password can be used to decrypt the document. // Also build the encryption/decryption key. func (crypt *PdfCrypt) authenticate(password []byte) (bool, error) { crypt.authenticated = false h := crypt.securityHandler() fkey, perm, err := h.Authenticate(&crypt.encryptStd, password) if err != nil { return false, err } else if perm == 0 || len(fkey) == 0 { return false, nil } crypt.authenticated = true crypt.encryptionKey = fkey return true, nil } // Check access rights and permissions for a specified password. If either user/owner password is specified, // full rights are granted, otherwise the access rights are specified by the Permissions flag. // // 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, 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) if err != nil { return false, 0, err } else if perm == 0 || len(fkey) == 0 { return false, 0, nil } return true, perm, nil } // Generates a key for encrypting a specific object based on the // object and generation number, as well as the document encryption key. func (crypt *PdfCrypt) makeKey(filter string, objNum, genNum uint32, ekey []byte) ([]byte, error) { f, ok := crypt.cryptFilters[filter] if !ok { return nil, fmt.Errorf("unknown crypt filter (%s)", filter) } return f.MakeKey(objNum, genNum, ekey) } // encryptDictKeys list all required field for "Encrypt" dictionary. // It is used as a fingerprint to detect old copies of this dictionary. var encryptDictKeys = []PdfObjectName{ "V", "R", "O", "U", "P", } // Check if object has already been processed. func (crypt *PdfCrypt) isDecrypted(obj PdfObject) bool { _, ok := crypt.decryptedObjects[obj] if ok { common.Log.Trace("Already decrypted") return true } switch obj := obj.(type) { case *PdfObjectStream: if crypt.encryptStd.R != 5 { if name, ok := obj.Get("Type").(*PdfObjectName); ok && *name == "XRef" { return true // Cross-reference streams should not be encrypted } } case *PdfIndirectObject: if _, ok = crypt.decryptedObjNum[int(obj.ObjectNumber)]; ok { return true } switch obj := obj.PdfObject.(type) { case *PdfObjectDictionary: // detect old copies of "Encrypt" dictionary // TODO: find a better way to do it ok := true for _, key := range encryptDictKeys { if obj.Get(key) == nil { ok = false break } } if ok { return true } } } common.Log.Trace("Not decrypted yet") return false } // Decrypt a buffer with a selected crypt filter. func (crypt *PdfCrypt) decryptBytes(buf []byte, filter string, okey []byte) ([]byte, error) { common.Log.Trace("Decrypt bytes") f, ok := crypt.cryptFilters[filter] if !ok { return nil, fmt.Errorf("unknown crypt filter (%s)", filter) } return f.DecryptBytes(buf, okey) } // Decrypt an object with specified key. For numbered objects, // the key argument is not used and a new one is generated based // on the object and generation number. // Traverses through all the subobjects (recursive). // // Does not look up references.. That should be done prior to calling. func (crypt *PdfCrypt) Decrypt(obj PdfObject, parentObjNum, parentGenNum int64) error { if crypt.isDecrypted(obj) { return nil } switch obj := obj.(type) { case *PdfIndirectObject: crypt.decryptedObjects[obj] = true common.Log.Trace("Decrypting indirect %d %d obj!", obj.ObjectNumber, obj.GenerationNumber) objNum := obj.ObjectNumber genNum := obj.GenerationNumber err := crypt.Decrypt(obj.PdfObject, objNum, genNum) if err != nil { return err } return nil case *PdfObjectStream: // Mark as decrypted first to avoid recursive issues. crypt.decryptedObjects[obj] = true dict := obj.PdfObjectDictionary if crypt.encryptStd.R != 5 { if s, ok := dict.Get("Type").(*PdfObjectName); ok && *s == "XRef" { return nil // Cross-reference streams should not be encrypted } } objNum := obj.ObjectNumber genNum := obj.GenerationNumber common.Log.Trace("Decrypting stream %d %d !", objNum, genNum) // TODO: Check for crypt filter (V4). // The Crypt filter shall be the first filter in the Filter array entry. streamFilter := stdCryptFilter // Default RC4. if crypt.encrypt.V >= 4 { streamFilter = crypt.streamFilter common.Log.Trace("this.streamFilter = %s", crypt.streamFilter) if filters, ok := dict.Get("Filter").(*PdfObjectArray); ok { // Crypt filter can only be the first entry. if firstFilter, ok := GetName(filters.Get(0)); ok { if *firstFilter == "Crypt" { // Crypt filter overriding the default. // Default option is Identity. streamFilter = "Identity" // Check if valid crypt filter specified in the decode params. if decodeParams, ok := dict.Get("DecodeParms").(*PdfObjectDictionary); ok { if filterName, ok := decodeParams.Get("Name").(*PdfObjectName); ok { if _, ok := crypt.cryptFilters[string(*filterName)]; ok { common.Log.Trace("Using stream filter %s", *filterName) streamFilter = string(*filterName) } } } } } } common.Log.Trace("with %s filter", streamFilter) if streamFilter == "Identity" { // Identity: pass unchanged. return nil } } err := crypt.Decrypt(dict, objNum, genNum) if err != nil { return err } okey, err := crypt.makeKey(streamFilter, uint32(objNum), uint32(genNum), crypt.encryptionKey) if err != nil { return err } obj.Stream, err = crypt.decryptBytes(obj.Stream, streamFilter, okey) if err != nil { return err } // Update the length based on the decrypted stream. dict.Set("Length", MakeInteger(int64(len(obj.Stream)))) return nil case *PdfObjectString: common.Log.Trace("Decrypting string!") stringFilter := stdCryptFilter if crypt.encrypt.V >= 4 { // Currently only support Identity / RC4. common.Log.Trace("with %s filter", crypt.stringFilter) if crypt.stringFilter == "Identity" { // Identity: pass unchanged: No action. return nil } stringFilter = crypt.stringFilter } key, err := crypt.makeKey(stringFilter, uint32(parentObjNum), uint32(parentGenNum), crypt.encryptionKey) if err != nil { return err } // Overwrite the encrypted with decrypted string. str := obj.Str() decrypted := make([]byte, len(str)) for i := 0; i < len(str); i++ { decrypted[i] = str[i] } common.Log.Trace("Decrypt string: %s : % x", decrypted, decrypted) decrypted, err = crypt.decryptBytes(decrypted, stringFilter, key) if err != nil { return err } obj.val = string(decrypted) return nil case *PdfObjectArray: for _, o := range obj.Elements() { err := crypt.Decrypt(o, parentObjNum, parentGenNum) if err != nil { return err } } return nil case *PdfObjectDictionary: isSig := false if t := obj.Get("Type"); t != nil { typeStr, ok := t.(*PdfObjectName) if ok && *typeStr == "Sig" { isSig = true } } for _, keyidx := range obj.Keys() { o := obj.Get(keyidx) // How can we avoid this check, i.e. implement a more smart // traversal system? if isSig && string(keyidx) == "Contents" { // Leave the Contents of a Signature dictionary. continue } if string(keyidx) != "Parent" && string(keyidx) != "Prev" && string(keyidx) != "Last" { // Check not needed? err := crypt.Decrypt(o, parentObjNum, parentGenNum) if err != nil { return err } } } return nil } return nil } // Check if object has already been processed. func (crypt *PdfCrypt) isEncrypted(obj PdfObject) bool { _, ok := crypt.encryptedObjects[obj] if ok { common.Log.Trace("Already encrypted") return true } common.Log.Trace("Not encrypted yet") return false } // Encrypt a buffer with the specified crypt filter and key. func (crypt *PdfCrypt) encryptBytes(buf []byte, filter string, okey []byte) ([]byte, error) { common.Log.Trace("Encrypt bytes") f, ok := crypt.cryptFilters[filter] if !ok { return nil, fmt.Errorf("unknown crypt filter (%s)", filter) } return f.EncryptBytes(buf, okey) } // Encrypt an object with specified key. For numbered objects, // the key argument is not used and a new one is generated based // on the object and generation number. // Traverses through all the subobjects (recursive). // // Does not look up references.. That should be done prior to calling. func (crypt *PdfCrypt) Encrypt(obj PdfObject, parentObjNum, parentGenNum int64) error { if crypt.isEncrypted(obj) { return nil } switch obj := obj.(type) { case *PdfIndirectObject: crypt.encryptedObjects[obj] = true common.Log.Trace("Encrypting indirect %d %d obj!", obj.ObjectNumber, obj.GenerationNumber) objNum := obj.ObjectNumber genNum := obj.GenerationNumber err := crypt.Encrypt(obj.PdfObject, objNum, genNum) if err != nil { return err } return nil case *PdfObjectStream: crypt.encryptedObjects[obj] = true dict := obj.PdfObjectDictionary if s, ok := dict.Get("Type").(*PdfObjectName); ok && *s == "XRef" { return nil // Cross-reference streams should not be encrypted } objNum := obj.ObjectNumber genNum := obj.GenerationNumber common.Log.Trace("Encrypting stream %d %d !", objNum, genNum) // TODO: Check for crypt filter (V4). // The Crypt filter shall be the first filter in the Filter array entry. streamFilter := stdCryptFilter // Default RC4. if crypt.encrypt.V >= 4 { // For now. Need to change when we add support for more than // Identity / RC4. streamFilter = crypt.streamFilter common.Log.Trace("this.streamFilter = %s", crypt.streamFilter) if filters, ok := dict.Get("Filter").(*PdfObjectArray); ok { // Crypt filter can only be the first entry. if firstFilter, ok := GetName(filters.Get(0)); ok { if *firstFilter == "Crypt" { // Crypt filter overriding the default. // Default option is Identity. streamFilter = "Identity" // Check if valid crypt filter specified in the decode params. if decodeParams, ok := dict.Get("DecodeParms").(*PdfObjectDictionary); ok { if filterName, ok := decodeParams.Get("Name").(*PdfObjectName); ok { if _, ok := crypt.cryptFilters[string(*filterName)]; ok { common.Log.Trace("Using stream filter %s", *filterName) streamFilter = string(*filterName) } } } } } } common.Log.Trace("with %s filter", streamFilter) if streamFilter == "Identity" { // Identity: pass unchanged. return nil } } err := crypt.Encrypt(obj.PdfObjectDictionary, objNum, genNum) if err != nil { return err } okey, err := crypt.makeKey(streamFilter, uint32(objNum), uint32(genNum), crypt.encryptionKey) if err != nil { return err } obj.Stream, err = crypt.encryptBytes(obj.Stream, streamFilter, okey) if err != nil { return err } // Update the length based on the encrypted stream. dict.Set("Length", MakeInteger(int64(len(obj.Stream)))) return nil case *PdfObjectString: common.Log.Trace("Encrypting string!") stringFilter := stdCryptFilter if crypt.encrypt.V >= 4 { common.Log.Trace("with %s filter", crypt.stringFilter) if crypt.stringFilter == "Identity" { // Identity: pass unchanged: No action. return nil } stringFilter = crypt.stringFilter } key, err := crypt.makeKey(stringFilter, uint32(parentObjNum), uint32(parentGenNum), crypt.encryptionKey) if err != nil { return err } str := obj.Str() encrypted := make([]byte, len(str)) for i := 0; i < len(str); i++ { encrypted[i] = str[i] } common.Log.Trace("Encrypt string: %s : % x", encrypted, encrypted) encrypted, err = crypt.encryptBytes(encrypted, stringFilter, key) if err != nil { return err } obj.val = string(encrypted) return nil case *PdfObjectArray: for _, o := range obj.Elements() { err := crypt.Encrypt(o, parentObjNum, parentGenNum) if err != nil { return err } } return nil case *PdfObjectDictionary: isSig := false if t := obj.Get("Type"); t != nil { typeStr, ok := t.(*PdfObjectName) if ok && *typeStr == "Sig" { isSig = true } } for _, keyidx := range obj.Keys() { o := obj.Get(keyidx) // How can we avoid this check, i.e. implement a more smart // traversal system? if isSig && string(keyidx) == "Contents" { // Leave the Contents of a Signature dictionary. continue } if string(keyidx) != "Parent" && string(keyidx) != "Prev" && string(keyidx) != "Last" { // Check not needed? err := crypt.Encrypt(o, parentObjNum, parentGenNum) if err != nil { return err } } } return nil } return nil } // generateParams generates encryption parameters for specified passwords. func (crypt *PdfCrypt) generateParams(upass, opass []byte) error { h := crypt.securityHandler() ekey, err := h.GenerateParams(&crypt.encryptStd, opass, upass) if err != nil { return err } crypt.encryptionKey = ekey return nil }