Fetch the repository succeeded.
package core
import (
"crypto/md5"
"crypto/rand"
"errors"
"fmt"
"time"
"gitee.com/coodder/unipdf/common"
"gitee.com/coodder/unipdf/core/security"
crypto "gitee.com/coodder/unipdf/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 cfType := string(*typename); cfType != "CryptFilter" {
common.Log.Debug("Invalid CF dict type: %s (should be CryptFilter)", cfType)
}
}
// 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
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。