mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-02 22:17:06 +08:00
core: split crypt filter methods into a separate package
This commit is contained in:
parent
42df346e69
commit
7bd4ba688d
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/unidoc/unidoc/common"
|
"github.com/unidoc/unidoc/common"
|
||||||
"github.com/unidoc/unidoc/pdf/core/security"
|
"github.com/unidoc/unidoc/pdf/core/security"
|
||||||
|
crypto "github.com/unidoc/unidoc/pdf/core/security/crypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Version struct {
|
type Version struct {
|
||||||
@ -28,7 +29,7 @@ type EncryptInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PdfCryptNewEncrypt makes the document crypt handler based on a specified crypt filter.
|
// PdfCryptNewEncrypt makes the document crypt handler based on a specified crypt filter.
|
||||||
func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm security.Permissions) (*PdfCrypt, *EncryptInfo, error) {
|
func PdfCryptNewEncrypt(cf crypto.Filter, userPass, ownerPass []byte, perm security.Permissions) (*PdfCrypt, *EncryptInfo, error) {
|
||||||
crypter := &PdfCrypt{
|
crypter := &PdfCrypt{
|
||||||
encryptedObjects: make(map[PdfObject]bool),
|
encryptedObjects: make(map[PdfObject]bool),
|
||||||
cryptFilters: make(cryptFilters),
|
cryptFilters: make(cryptFilters),
|
||||||
@ -37,26 +38,19 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm securit
|
|||||||
EncryptMetadata: true,
|
EncryptMetadata: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// TODO(dennwc): define it in the CF interface
|
|
||||||
var vers Version
|
var vers Version
|
||||||
switch cf.(type) {
|
|
||||||
case cryptFilterV2:
|
|
||||||
crypter.encrypt.V = 2
|
|
||||||
crypter.encryptStd.R = 3
|
|
||||||
case cryptFilterAESV2:
|
|
||||||
vers.Major, vers.Minor = 1, 5
|
|
||||||
crypter.encrypt.V = 4
|
|
||||||
crypter.encryptStd.R = 4
|
|
||||||
case cryptFilterAESV3:
|
|
||||||
vers.Major, vers.Minor = 2, 0
|
|
||||||
crypter.encrypt.V = 5
|
|
||||||
crypter.encryptStd.R = 6
|
|
||||||
}
|
|
||||||
if cf != nil {
|
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
|
crypter.encrypt.Length = cf.KeyLength() * 8
|
||||||
}
|
}
|
||||||
const (
|
const (
|
||||||
defaultFilter = StandardCryptFilter
|
defaultFilter = stdCryptFilter
|
||||||
)
|
)
|
||||||
crypter.cryptFilters[defaultFilter] = cf
|
crypter.cryptFilters[defaultFilter] = cf
|
||||||
if crypter.encrypt.V >= 4 {
|
if crypter.encrypt.V >= 4 {
|
||||||
@ -97,6 +91,28 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm securit
|
|||||||
}, nil
|
}, 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 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.
|
// encodeEncryptStd encodes fields of standard security handler to an Encrypt dictionary.
|
||||||
func encodeEncryptStd(d *security.StdEncryptDict, ed *PdfObjectDictionary) {
|
func encodeEncryptStd(d *security.StdEncryptDict, ed *PdfObjectDictionary) {
|
||||||
ed.Set("R", MakeInteger(int64(d.R)))
|
ed.Set("R", MakeInteger(int64(d.R)))
|
||||||
@ -200,26 +216,32 @@ func decodeEncryptStd(d *security.StdEncryptDict, ed *PdfObjectDictionary) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PdfCrypt provides PDF encryption/decryption support.
|
func decodeCryptFilter(cf *crypto.FilterDict, d *PdfObjectDictionary) error {
|
||||||
// The PDF standard supports encryption of strings and streams (Section 7.6).
|
// If Type present, should be CryptFilter.
|
||||||
// TODO (v3): Consider unexporting.
|
if typename, ok := d.Get("Type").(*PdfObjectName); ok {
|
||||||
type PdfCrypt struct {
|
if string(*typename) != "CryptFilter" {
|
||||||
encrypt encryptDict
|
return fmt.Errorf("CF dict type != CryptFilter (%s)", typename)
|
||||||
encryptStd security.StdEncryptDict
|
}
|
||||||
|
}
|
||||||
|
|
||||||
id0 string
|
// Method.
|
||||||
encryptionKey []byte
|
name, ok := d.Get("CFM").(*PdfObjectName)
|
||||||
decryptedObjects map[PdfObject]bool
|
if !ok {
|
||||||
encryptedObjects map[PdfObject]bool
|
return fmt.Errorf("Unsupported crypt filter (None)")
|
||||||
authenticated bool
|
}
|
||||||
// Crypt filters (V4).
|
cf.CFM = string(*name)
|
||||||
cryptFilters cryptFilters
|
|
||||||
streamFilter string
|
|
||||||
stringFilter string
|
|
||||||
|
|
||||||
parser *PdfParser
|
// Auth event
|
||||||
|
if event, ok := d.Get("AuthEvent").(*PdfObjectName); ok {
|
||||||
|
cf.AuthEvent = security.AuthEvent(*event)
|
||||||
|
} else {
|
||||||
|
cf.AuthEvent = security.EventDocOpen
|
||||||
|
}
|
||||||
|
|
||||||
decryptedObjNum map[int]struct{}
|
if length, ok := d.Get("Length").(*PdfObjectInteger); ok {
|
||||||
|
cf.Length = int(*length)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (crypt *PdfCrypt) newEncyptDict() *PdfObjectDictionary {
|
func (crypt *PdfCrypt) newEncyptDict() *PdfObjectDictionary {
|
||||||
@ -262,65 +284,31 @@ func (crypt *PdfCrypt) String() string {
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
type authEvent string
|
|
||||||
|
|
||||||
const (
|
|
||||||
authEventDocOpen = authEvent("DocOpen")
|
|
||||||
authEventEFOpen = authEvent("EFOpen")
|
|
||||||
)
|
|
||||||
|
|
||||||
type cryptFiltersDict map[string]cryptFilterDict
|
|
||||||
|
|
||||||
// encryptDict is a set of field common to all encryption dictionaries.
|
// encryptDict is a set of field common to all encryption dictionaries.
|
||||||
type encryptDict struct {
|
type encryptDict struct {
|
||||||
Filter string // (Required) The name of the preferred security handler for this document.
|
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.
|
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.
|
SubFilter string // Completely specifies the format and interpretation of the encryption dictionary.
|
||||||
Length int // The length of the encryption key, in bits.
|
Length int // The length of the encryption key, in bits.
|
||||||
CF cryptFiltersDict // Crypt filters dictionary.
|
|
||||||
StmF string // The filter that shall be used by default when decrypting streams.
|
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.
|
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.
|
EFF string // The filter that shall be used when decrypting embedded file streams.
|
||||||
|
|
||||||
|
CF map[string]crypto.FilterDict // Crypt filters dictionary.
|
||||||
}
|
}
|
||||||
|
|
||||||
// StandardCryptFilter is a default name for a standard crypt filter.
|
// stdCryptFilter is a default name for a standard crypt filter.
|
||||||
const StandardCryptFilter = "StdCF"
|
const stdCryptFilter = "StdCF"
|
||||||
|
|
||||||
func newCryptFiltersV2(length int) cryptFilters {
|
func newCryptFiltersV2(length int) cryptFilters {
|
||||||
return cryptFilters{
|
return cryptFilters{
|
||||||
StandardCryptFilter: NewCryptFilterV2(length),
|
stdCryptFilter: crypto.NewFilterV2(length),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCryptFilterV2 creates a RC4-based filter with a specified key length (in bytes).
|
|
||||||
func NewCryptFilterV2(length int) CryptFilter {
|
|
||||||
f, err := newCryptFilterV2(cryptFilterDict{Length: length})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCryptFilterAESV2 creates an AES-based filter with a 128 bit key (AESV2).
|
|
||||||
func NewCryptFilterAESV2() CryptFilter {
|
|
||||||
f, err := newCryptFilterAESV2(cryptFilterDict{})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCryptFilterAESV3 creates an AES-based filter with a 256 bit key (AESV3).
|
|
||||||
func NewCryptFilterAESV3() CryptFilter {
|
|
||||||
f, err := newCryptFilterAESV3(cryptFilterDict{})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// cryptFilters is a map of crypt filter name and underlying CryptFilter info.
|
// cryptFilters is a map of crypt filter name and underlying CryptFilter info.
|
||||||
type cryptFilters map[string]CryptFilter
|
type cryptFilters map[string]crypto.Filter
|
||||||
|
|
||||||
// loadCryptFilters loads crypt filter information from the encryption dictionary (V>=4).
|
// loadCryptFilters loads crypt filter information from the encryption dictionary (V>=4).
|
||||||
func (crypt *PdfCrypt) loadCryptFilters(ed *PdfObjectDictionary) error {
|
func (crypt *PdfCrypt) loadCryptFilters(ed *PdfObjectDictionary) error {
|
||||||
@ -365,22 +353,18 @@ func (crypt *PdfCrypt) loadCryptFilters(ed *PdfObjectDictionary) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfd cryptFilterDict
|
var cfd crypto.FilterDict
|
||||||
if err := cfd.ReadFrom(dict); err != nil {
|
if err := decodeCryptFilter(&cfd, dict); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fnc, err := getCryptFilterMethod(cfd.CFM)
|
cf, err := crypto.NewFilter(cfd)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cf, err := fnc(cfd)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
crypt.cryptFilters[string(name)] = cf
|
crypt.cryptFilters[string(name)] = cf
|
||||||
}
|
}
|
||||||
// Cannot be overwritten.
|
// Cannot be overwritten.
|
||||||
crypt.cryptFilters["Identity"] = cryptFilteridentity{}
|
crypt.cryptFilters["Identity"] = crypto.NewIdentity()
|
||||||
|
|
||||||
// StrF strings filter.
|
// StrF strings filter.
|
||||||
crypt.stringFilter = "Identity"
|
crypt.stringFilter = "Identity"
|
||||||
@ -403,6 +387,18 @@ func (crypt *PdfCrypt) loadCryptFilters(ed *PdfObjectDictionary) error {
|
|||||||
return nil
|
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).
|
// saveCryptFilters saves crypt filter information to the encryption dictionary (V>=4).
|
||||||
func (crypt *PdfCrypt) saveCryptFilters(ed *PdfObjectDictionary) error {
|
func (crypt *PdfCrypt) saveCryptFilters(ed *PdfObjectDictionary) error {
|
||||||
if crypt.encrypt.V < 4 {
|
if crypt.encrypt.V < 4 {
|
||||||
@ -415,7 +411,7 @@ func (crypt *PdfCrypt) saveCryptFilters(ed *PdfObjectDictionary) error {
|
|||||||
if name == "Identity" {
|
if name == "Identity" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v := cryptFilterToDict(filter, "")
|
v := encodeCryptFilter(filter, "")
|
||||||
cf.Set(PdfObjectName(name), v)
|
cf.Set(PdfObjectName(name), v)
|
||||||
}
|
}
|
||||||
ed.Set("StrF", MakeName(crypt.stringFilter))
|
ed.Set("StrF", MakeName(crypt.stringFilter))
|
||||||
@ -654,7 +650,7 @@ func (crypt *PdfCrypt) Decrypt(obj PdfObject, parentObjNum, parentGenNum int64)
|
|||||||
// TODO: Check for crypt filter (V4).
|
// TODO: Check for crypt filter (V4).
|
||||||
// The Crypt filter shall be the first filter in the Filter array entry.
|
// The Crypt filter shall be the first filter in the Filter array entry.
|
||||||
|
|
||||||
streamFilter := StandardCryptFilter // Default RC4.
|
streamFilter := stdCryptFilter // Default RC4.
|
||||||
if crypt.encrypt.V >= 4 {
|
if crypt.encrypt.V >= 4 {
|
||||||
streamFilter = crypt.streamFilter
|
streamFilter = crypt.streamFilter
|
||||||
common.Log.Trace("this.streamFilter = %s", crypt.streamFilter)
|
common.Log.Trace("this.streamFilter = %s", crypt.streamFilter)
|
||||||
@ -708,7 +704,7 @@ func (crypt *PdfCrypt) Decrypt(obj PdfObject, parentObjNum, parentGenNum int64)
|
|||||||
case *PdfObjectString:
|
case *PdfObjectString:
|
||||||
common.Log.Trace("Decrypting string!")
|
common.Log.Trace("Decrypting string!")
|
||||||
|
|
||||||
stringFilter := StandardCryptFilter
|
stringFilter := stdCryptFilter
|
||||||
if crypt.encrypt.V >= 4 {
|
if crypt.encrypt.V >= 4 {
|
||||||
// Currently only support Identity / RC4.
|
// Currently only support Identity / RC4.
|
||||||
common.Log.Trace("with %s filter", crypt.stringFilter)
|
common.Log.Trace("with %s filter", crypt.stringFilter)
|
||||||
@ -837,7 +833,7 @@ func (crypt *PdfCrypt) Encrypt(obj PdfObject, parentObjNum, parentGenNum int64)
|
|||||||
// TODO: Check for crypt filter (V4).
|
// TODO: Check for crypt filter (V4).
|
||||||
// The Crypt filter shall be the first filter in the Filter array entry.
|
// The Crypt filter shall be the first filter in the Filter array entry.
|
||||||
|
|
||||||
streamFilter := StandardCryptFilter // Default RC4.
|
streamFilter := stdCryptFilter // Default RC4.
|
||||||
if crypt.encrypt.V >= 4 {
|
if crypt.encrypt.V >= 4 {
|
||||||
// For now. Need to change when we add support for more than
|
// For now. Need to change when we add support for more than
|
||||||
// Identity / RC4.
|
// Identity / RC4.
|
||||||
@ -893,7 +889,7 @@ func (crypt *PdfCrypt) Encrypt(obj PdfObject, parentObjNum, parentGenNum int64)
|
|||||||
case *PdfObjectString:
|
case *PdfObjectString:
|
||||||
common.Log.Trace("Encrypting string!")
|
common.Log.Trace("Encrypting string!")
|
||||||
|
|
||||||
stringFilter := StandardCryptFilter
|
stringFilter := stdCryptFilter
|
||||||
if crypt.encrypt.V >= 4 {
|
if crypt.encrypt.V >= 4 {
|
||||||
common.Log.Trace("with %s filter", crypt.stringFilter)
|
common.Log.Trace("with %s filter", crypt.stringFilter)
|
||||||
if crypt.stringFilter == "Identity" {
|
if crypt.stringFilter == "Identity" {
|
||||||
|
@ -1,389 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rc4"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/unidoc/unidoc/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
cryptMethods = make(map[string]cryptFilterFunc)
|
|
||||||
)
|
|
||||||
|
|
||||||
// cryptFilterDict represents information from a CryptFilter dictionary.
|
|
||||||
type cryptFilterDict struct {
|
|
||||||
CFM string // The method used, if any, by the PDF reader to decrypt data.
|
|
||||||
AuthEvent authEvent
|
|
||||||
Length int // in bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cf *cryptFilterDict) ReadFrom(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 = authEvent(*event)
|
|
||||||
} else {
|
|
||||||
cf.AuthEvent = authEventDocOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
if length, ok := d.Get("Length").(*PdfObjectInteger); ok {
|
|
||||||
cf.Length = int(*length)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cryptFilterFunc is used to construct crypt filters from CryptFilter dictionary
|
|
||||||
type cryptFilterFunc func(d cryptFilterDict) (CryptFilter, error)
|
|
||||||
|
|
||||||
// registerCryptFilterMethod registers a CFM.
|
|
||||||
func registerCryptFilterMethod(name string, fnc cryptFilterFunc) {
|
|
||||||
if _, ok := cryptMethods[name]; ok {
|
|
||||||
panic("already registered")
|
|
||||||
}
|
|
||||||
cryptMethods[name] = fnc
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCryptFilterMethod check if a CFM with a specified name is supported an returns its implementation.
|
|
||||||
func getCryptFilterMethod(name string) (cryptFilterFunc, error) {
|
|
||||||
f := cryptMethods[string(name)]
|
|
||||||
if f == nil {
|
|
||||||
return nil, fmt.Errorf("unsupported crypt filter: %q", name)
|
|
||||||
}
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Register supported crypt filter methods.
|
|
||||||
// Table 25, CFM (page 92)
|
|
||||||
registerCryptFilterMethod("V2", newCryptFilterV2)
|
|
||||||
registerCryptFilterMethod("AESV2", newCryptFilterAESV2)
|
|
||||||
registerCryptFilterMethod("AESV3", newCryptFilterAESV3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CryptFilter is a common interface for crypt filter methods.
|
|
||||||
type CryptFilter interface {
|
|
||||||
// Name returns a name of the filter that should be used in CFM field of Encrypt dictionary.
|
|
||||||
Name() string
|
|
||||||
// KeyLength returns a length of the encryption key in bytes.
|
|
||||||
KeyLength() int
|
|
||||||
// MakeKey generates a object encryption key based on file encryption key and object numbers.
|
|
||||||
// Used only for legacy filters - AESV3 doesn't change the key for each object.
|
|
||||||
MakeKey(objNum, genNum uint32, fkey []byte) ([]byte, error)
|
|
||||||
// EncryptBytes encrypts a buffer using object encryption key, as returned by MakeKey.
|
|
||||||
// Implementation may reuse a buffer and encrypt data in-place.
|
|
||||||
EncryptBytes(p []byte, okey []byte) ([]byte, error)
|
|
||||||
// DecryptBytes decrypts a buffer using object encryption key, as returned by MakeKey.
|
|
||||||
// Implementation may reuse a buffer and decrypt data in-place.
|
|
||||||
DecryptBytes(p []byte, okey []byte) ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cryptFilterToDict(cf CryptFilter, event authEvent) *PdfObjectDictionary {
|
|
||||||
if event == "" {
|
|
||||||
event = authEventDocOpen
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
type cryptFilteridentity struct{}
|
|
||||||
|
|
||||||
func (cryptFilteridentity) Name() string {
|
|
||||||
return "Identity"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilteridentity) KeyLength() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilteridentity) MakeKey(objNum, genNum uint32, fkey []byte) ([]byte, error) {
|
|
||||||
return fkey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilteridentity) EncryptBytes(p []byte, okey []byte) ([]byte, error) {
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilteridentity) DecryptBytes(p []byte, okey []byte) ([]byte, error) {
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeKeyV2 is a common object key generation shared by V2 and AESV2 crypt filters.
|
|
||||||
func makeKeyV2(objNum, genNum uint32, ekey []byte, isAES bool) ([]byte, error) {
|
|
||||||
key := make([]byte, len(ekey)+5)
|
|
||||||
for i := 0; i < len(ekey); i++ {
|
|
||||||
key[i] = ekey[i]
|
|
||||||
}
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
b := byte((objNum >> uint32(8*i)) & 0xff)
|
|
||||||
key[i+len(ekey)] = b
|
|
||||||
}
|
|
||||||
for i := 0; i < 2; i++ {
|
|
||||||
b := byte((genNum >> uint32(8*i)) & 0xff)
|
|
||||||
key[i+len(ekey)+3] = b
|
|
||||||
}
|
|
||||||
if isAES {
|
|
||||||
// If using the AES algorithm, extend the encryption key an
|
|
||||||
// additional 4 bytes by adding the value “sAlT”, which
|
|
||||||
// corresponds to the hexadecimal values 0x73, 0x41, 0x6C, 0x54.
|
|
||||||
key = append(key, 0x73)
|
|
||||||
key = append(key, 0x41)
|
|
||||||
key = append(key, 0x6C)
|
|
||||||
key = append(key, 0x54)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take the MD5.
|
|
||||||
h := md5.New()
|
|
||||||
h.Write(key)
|
|
||||||
hashb := h.Sum(nil)
|
|
||||||
|
|
||||||
if len(ekey)+5 < 16 {
|
|
||||||
return hashb[0 : len(ekey)+5], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashb, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCryptFilterV2(d cryptFilterDict) (CryptFilter, error) {
|
|
||||||
if d.Length%8 != 0 {
|
|
||||||
return nil, fmt.Errorf("Crypt filter length not multiple of 8 (%d)", d.Length)
|
|
||||||
}
|
|
||||||
// Standard security handler expresses the length in multiples of 8 (16 means 128)
|
|
||||||
// We only deal with standard so far. (Public key not supported yet).
|
|
||||||
if d.Length < 5 || d.Length > 16 {
|
|
||||||
if d.Length == 64 || d.Length == 128 {
|
|
||||||
common.Log.Debug("STANDARD VIOLATION: Crypt Length appears to be in bits rather than bytes - assuming bits (%d)", d.Length)
|
|
||||||
d.Length /= 8
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("Crypt filter length not in range 40 - 128 bit (%d)", d.Length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cryptFilterV2{length: d.Length}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cryptFilterV2 is a RC4-based filter
|
|
||||||
type cryptFilterV2 struct {
|
|
||||||
length int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterV2) Name() string {
|
|
||||||
return "V2"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f cryptFilterV2) KeyLength() int {
|
|
||||||
return f.length
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f cryptFilterV2) MakeKey(objNum, genNum uint32, ekey []byte) ([]byte, error) {
|
|
||||||
return makeKeyV2(objNum, genNum, ekey, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterV2) EncryptBytes(buf []byte, okey []byte) ([]byte, error) {
|
|
||||||
// Standard RC4 algorithm.
|
|
||||||
ciph, err := rc4.NewCipher(okey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
common.Log.Trace("RC4 Encrypt: % x", buf)
|
|
||||||
ciph.XORKeyStream(buf, buf)
|
|
||||||
common.Log.Trace("to: % x", buf)
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterV2) DecryptBytes(buf []byte, okey []byte) ([]byte, error) {
|
|
||||||
// Standard RC4 algorithm.
|
|
||||||
ciph, err := rc4.NewCipher(okey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
common.Log.Trace("RC4 Decrypt: % x", buf)
|
|
||||||
ciph.XORKeyStream(buf, buf)
|
|
||||||
common.Log.Trace("to: % x", buf)
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cryptFilterAES implements a generic AES encryption and decryption algorithm used by AESV2 and AESV3 filter methods.
|
|
||||||
type cryptFilterAES struct{}
|
|
||||||
|
|
||||||
func (cryptFilterAES) EncryptBytes(buf []byte, okey []byte) ([]byte, error) {
|
|
||||||
// Strings and streams encrypted with AES shall use a padding
|
|
||||||
// scheme that is described in Internet RFC 2898, PKCS #5:
|
|
||||||
// Password-Based Cryptography Specification Version 2.0; see
|
|
||||||
// the Bibliography. For an original message length of M,
|
|
||||||
// the pad shall consist of 16 - (M mod 16) bytes whose value
|
|
||||||
// shall also be 16 - (M mod 16).
|
|
||||||
//
|
|
||||||
// A 9-byte message has a pad of 7 bytes, each with the value
|
|
||||||
// 0x07. The pad can be unambiguously removed to determine the
|
|
||||||
// original message length when decrypting. Note that the pad is
|
|
||||||
// present when M is evenly divisible by 16; it contains 16 bytes
|
|
||||||
// of 0x10.
|
|
||||||
|
|
||||||
ciph, err := aes.NewCipher(okey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
common.Log.Trace("AES Encrypt (%d): % x", len(buf), buf)
|
|
||||||
|
|
||||||
// If using the AES algorithm, the Cipher Block Chaining (CBC)
|
|
||||||
// mode, which requires an initialization vector, is used. The
|
|
||||||
// block size parameter is set to 16 bytes, and the initialization
|
|
||||||
// vector is a 16-byte random number that is stored as the first
|
|
||||||
// 16 bytes of the encrypted stream or string.
|
|
||||||
|
|
||||||
const block = aes.BlockSize // 16
|
|
||||||
|
|
||||||
pad := block - len(buf)%block
|
|
||||||
for i := 0; i < pad; i++ {
|
|
||||||
buf = append(buf, byte(pad))
|
|
||||||
}
|
|
||||||
common.Log.Trace("Padded to %d bytes", len(buf))
|
|
||||||
|
|
||||||
// Generate random 16 bytes, place in beginning of buffer.
|
|
||||||
ciphertext := make([]byte, block+len(buf))
|
|
||||||
iv := ciphertext[:block]
|
|
||||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
mode := cipher.NewCBCEncrypter(ciph, iv)
|
|
||||||
mode.CryptBlocks(ciphertext[block:], buf)
|
|
||||||
|
|
||||||
buf = ciphertext
|
|
||||||
common.Log.Trace("to (%d): % x", len(buf), buf)
|
|
||||||
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterAES) DecryptBytes(buf []byte, okey []byte) ([]byte, error) {
|
|
||||||
// Strings and streams encrypted with AES shall use a padding
|
|
||||||
// scheme that is described in Internet RFC 2898, PKCS #5:
|
|
||||||
// Password-Based Cryptography Specification Version 2.0; see
|
|
||||||
// the Bibliography. For an original message length of M,
|
|
||||||
// the pad shall consist of 16 - (M mod 16) bytes whose value
|
|
||||||
// shall also be 16 - (M mod 16).
|
|
||||||
//
|
|
||||||
// A 9-byte message has a pad of 7 bytes, each with the value
|
|
||||||
// 0x07. The pad can be unambiguously removed to determine the
|
|
||||||
// original message length when decrypting. Note that the pad is
|
|
||||||
// present when M is evenly divisible by 16; it contains 16 bytes
|
|
||||||
// of 0x10.
|
|
||||||
|
|
||||||
ciph, err := aes.NewCipher(okey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If using the AES algorithm, the Cipher Block Chaining (CBC)
|
|
||||||
// mode, which requires an initialization vector, is used. The
|
|
||||||
// block size parameter is set to 16 bytes, and the initialization
|
|
||||||
// vector is a 16-byte random number that is stored as the first
|
|
||||||
// 16 bytes of the encrypted stream or string.
|
|
||||||
if len(buf) < 16 {
|
|
||||||
common.Log.Debug("ERROR AES invalid buf %s", buf)
|
|
||||||
return buf, fmt.Errorf("AES: Buf len < 16 (%d)", len(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
iv := buf[:16]
|
|
||||||
buf = buf[16:]
|
|
||||||
|
|
||||||
if len(buf)%16 != 0 {
|
|
||||||
common.Log.Debug(" iv (%d): % x", len(iv), iv)
|
|
||||||
common.Log.Debug("buf (%d): % x", len(buf), buf)
|
|
||||||
return buf, fmt.Errorf("AES buf length not multiple of 16 (%d)", len(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
mode := cipher.NewCBCDecrypter(ciph, iv)
|
|
||||||
|
|
||||||
common.Log.Trace("AES Decrypt (%d): % x", len(buf), buf)
|
|
||||||
common.Log.Trace("chop AES Decrypt (%d): % x", len(buf), buf)
|
|
||||||
mode.CryptBlocks(buf, buf)
|
|
||||||
common.Log.Trace("to (%d): % x", len(buf), buf)
|
|
||||||
|
|
||||||
if len(buf) == 0 {
|
|
||||||
common.Log.Trace("Empty buf, returning empty string")
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The padded length is indicated by the last values. Remove those.
|
|
||||||
|
|
||||||
padLen := int(buf[len(buf)-1])
|
|
||||||
if padLen >= len(buf) {
|
|
||||||
common.Log.Debug("Illegal pad length")
|
|
||||||
return buf, fmt.Errorf("Invalid pad length")
|
|
||||||
}
|
|
||||||
buf = buf[:len(buf)-padLen]
|
|
||||||
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCryptFilterAESV2(d cryptFilterDict) (CryptFilter, error) {
|
|
||||||
if d.Length != 0 && d.Length != 16 {
|
|
||||||
return nil, fmt.Errorf("Invalid AESV2 crypt filter length (%d)", d.Length)
|
|
||||||
}
|
|
||||||
return cryptFilterAESV2{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cryptFilterAESV2 is an AES-based filter (128 bit key, PDF 1.6)
|
|
||||||
type cryptFilterAESV2 struct {
|
|
||||||
cryptFilterAES
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterAESV2) Name() string {
|
|
||||||
return "AESV2"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterAESV2) KeyLength() int {
|
|
||||||
return 128 / 8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterAESV2) MakeKey(objNum, genNum uint32, ekey []byte) ([]byte, error) {
|
|
||||||
return makeKeyV2(objNum, genNum, ekey, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCryptFilterAESV3(d cryptFilterDict) (CryptFilter, error) {
|
|
||||||
if d.Length != 0 && d.Length != 32 {
|
|
||||||
return nil, fmt.Errorf("Invalid AESV3 crypt filter length (%d)", d.Length)
|
|
||||||
}
|
|
||||||
return cryptFilterAESV3{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cryptFilterAESV3 is an AES-based filter (256 bit key, PDF 2.0)
|
|
||||||
type cryptFilterAESV3 struct {
|
|
||||||
cryptFilterAES
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterAESV3) Name() string {
|
|
||||||
return "AESV3"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterAESV3) KeyLength() int {
|
|
||||||
return 256 / 8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cryptFilterAESV3) MakeKey(_, _ uint32, ekey []byte) ([]byte, error) {
|
|
||||||
return ekey, nil
|
|
||||||
}
|
|
9
pdf/core/security/auth.go
Normal file
9
pdf/core/security/auth.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
// AuthEvent is an event type that triggers authentication.
|
||||||
|
type AuthEvent string
|
||||||
|
|
||||||
|
const (
|
||||||
|
EventDocOpen = AuthEvent("DocOpen") // document open
|
||||||
|
EventEFOpen = AuthEvent("EFOpen") // embedded file open
|
||||||
|
)
|
49
pdf/core/security/crypt/filter_aesv2.go
Normal file
49
pdf/core/security/crypt/filter_aesv2.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package crypt
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerFilter("AESV2", newFilterAESV2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilterAESV2 creates an AES-based filter with a 128 bit key (AESV2).
|
||||||
|
func NewFilterAESV2() Filter {
|
||||||
|
f, err := newFilterAESV2(FilterDict{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFilterAESV2(d FilterDict) (Filter, error) {
|
||||||
|
if d.Length != 0 && d.Length != 16 {
|
||||||
|
return nil, fmt.Errorf("Invalid AESV2 crypt filter length (%d)", d.Length)
|
||||||
|
}
|
||||||
|
return filterAESV2{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterAESV2 is an AES-based filter (128 bit key, PDF 1.6)
|
||||||
|
type filterAESV2 struct {
|
||||||
|
filterAES
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV2) PDFVersion() [2]int {
|
||||||
|
return [2]int{1, 5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV2) HandlerVersion() (V, R int) {
|
||||||
|
V, R = 4, 4
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV2) Name() string {
|
||||||
|
return "AESV2"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV2) KeyLength() int {
|
||||||
|
return 128 / 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV2) MakeKey(objNum, genNum uint32, ekey []byte) ([]byte, error) {
|
||||||
|
return makeKeyV2(objNum, genNum, ekey, true)
|
||||||
|
}
|
173
pdf/core/security/crypt/filter_aesv3.go
Normal file
173
pdf/core/security/crypt/filter_aesv3.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package crypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/unidoc/unidoc/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerFilter("AESV3", newFilterAESV3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilterAESV3 creates an AES-based filter with a 256 bit key (AESV3).
|
||||||
|
func NewFilterAESV3() Filter {
|
||||||
|
f, err := newFilterAESV3(FilterDict{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFilterAESV3(d FilterDict) (Filter, error) {
|
||||||
|
if d.Length != 0 && d.Length != 32 {
|
||||||
|
return nil, fmt.Errorf("Invalid AESV3 crypt filter length (%d)", d.Length)
|
||||||
|
}
|
||||||
|
return filterAESV3{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterAES implements a generic AES encryption and decryption algorithm used by AESV2 and AESV3 filter methods.
|
||||||
|
type filterAES struct{}
|
||||||
|
|
||||||
|
func (filterAES) EncryptBytes(buf []byte, okey []byte) ([]byte, error) {
|
||||||
|
// Strings and streams encrypted with AES shall use a padding
|
||||||
|
// scheme that is described in Internet RFC 2898, PKCS #5:
|
||||||
|
// Password-Based Cryptography Specification Version 2.0; see
|
||||||
|
// the Bibliography. For an original message length of M,
|
||||||
|
// the pad shall consist of 16 - (M mod 16) bytes whose value
|
||||||
|
// shall also be 16 - (M mod 16).
|
||||||
|
//
|
||||||
|
// A 9-byte message has a pad of 7 bytes, each with the value
|
||||||
|
// 0x07. The pad can be unambiguously removed to determine the
|
||||||
|
// original message length when decrypting. Note that the pad is
|
||||||
|
// present when M is evenly divisible by 16; it contains 16 bytes
|
||||||
|
// of 0x10.
|
||||||
|
|
||||||
|
ciph, err := aes.NewCipher(okey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Log.Trace("AES Encrypt (%d): % x", len(buf), buf)
|
||||||
|
|
||||||
|
// If using the AES algorithm, the Cipher Block Chaining (CBC)
|
||||||
|
// mode, which requires an initialization vector, is used. The
|
||||||
|
// block size parameter is set to 16 bytes, and the initialization
|
||||||
|
// vector is a 16-byte random number that is stored as the first
|
||||||
|
// 16 bytes of the encrypted stream or string.
|
||||||
|
|
||||||
|
const block = aes.BlockSize // 16
|
||||||
|
|
||||||
|
pad := block - len(buf)%block
|
||||||
|
for i := 0; i < pad; i++ {
|
||||||
|
buf = append(buf, byte(pad))
|
||||||
|
}
|
||||||
|
common.Log.Trace("Padded to %d bytes", len(buf))
|
||||||
|
|
||||||
|
// Generate random 16 bytes, place in beginning of buffer.
|
||||||
|
ciphertext := make([]byte, block+len(buf))
|
||||||
|
iv := ciphertext[:block]
|
||||||
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCEncrypter(ciph, iv)
|
||||||
|
mode.CryptBlocks(ciphertext[block:], buf)
|
||||||
|
|
||||||
|
buf = ciphertext
|
||||||
|
common.Log.Trace("to (%d): % x", len(buf), buf)
|
||||||
|
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAES) DecryptBytes(buf []byte, okey []byte) ([]byte, error) {
|
||||||
|
// Strings and streams encrypted with AES shall use a padding
|
||||||
|
// scheme that is described in Internet RFC 2898, PKCS #5:
|
||||||
|
// Password-Based Cryptography Specification Version 2.0; see
|
||||||
|
// the Bibliography. For an original message length of M,
|
||||||
|
// the pad shall consist of 16 - (M mod 16) bytes whose value
|
||||||
|
// shall also be 16 - (M mod 16).
|
||||||
|
//
|
||||||
|
// A 9-byte message has a pad of 7 bytes, each with the value
|
||||||
|
// 0x07. The pad can be unambiguously removed to determine the
|
||||||
|
// original message length when decrypting. Note that the pad is
|
||||||
|
// present when M is evenly divisible by 16; it contains 16 bytes
|
||||||
|
// of 0x10.
|
||||||
|
|
||||||
|
ciph, err := aes.NewCipher(okey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If using the AES algorithm, the Cipher Block Chaining (CBC)
|
||||||
|
// mode, which requires an initialization vector, is used. The
|
||||||
|
// block size parameter is set to 16 bytes, and the initialization
|
||||||
|
// vector is a 16-byte random number that is stored as the first
|
||||||
|
// 16 bytes of the encrypted stream or string.
|
||||||
|
if len(buf) < 16 {
|
||||||
|
common.Log.Debug("ERROR AES invalid buf %s", buf)
|
||||||
|
return buf, fmt.Errorf("AES: Buf len < 16 (%d)", len(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
iv := buf[:16]
|
||||||
|
buf = buf[16:]
|
||||||
|
|
||||||
|
if len(buf)%16 != 0 {
|
||||||
|
common.Log.Debug(" iv (%d): % x", len(iv), iv)
|
||||||
|
common.Log.Debug("buf (%d): % x", len(buf), buf)
|
||||||
|
return buf, fmt.Errorf("AES buf length not multiple of 16 (%d)", len(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCDecrypter(ciph, iv)
|
||||||
|
|
||||||
|
common.Log.Trace("AES Decrypt (%d): % x", len(buf), buf)
|
||||||
|
common.Log.Trace("chop AES Decrypt (%d): % x", len(buf), buf)
|
||||||
|
mode.CryptBlocks(buf, buf)
|
||||||
|
common.Log.Trace("to (%d): % x", len(buf), buf)
|
||||||
|
|
||||||
|
if len(buf) == 0 {
|
||||||
|
common.Log.Trace("Empty buf, returning empty string")
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The padded length is indicated by the last values. Remove those.
|
||||||
|
|
||||||
|
padLen := int(buf[len(buf)-1])
|
||||||
|
if padLen >= len(buf) {
|
||||||
|
common.Log.Debug("Illegal pad length")
|
||||||
|
return buf, fmt.Errorf("Invalid pad length")
|
||||||
|
}
|
||||||
|
buf = buf[:len(buf)-padLen]
|
||||||
|
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterAESV3 is an AES-based filter (256 bit key, PDF 2.0)
|
||||||
|
type filterAESV3 struct {
|
||||||
|
filterAES
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV3) PDFVersion() [2]int {
|
||||||
|
return [2]int{2, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV3) HandlerVersion() (V, R int) {
|
||||||
|
V, R = 5, 6
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV3) Name() string {
|
||||||
|
return "AESV3"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV3) KeyLength() int {
|
||||||
|
return 256 / 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAESV3) MakeKey(_, _ uint32, ekey []byte) ([]byte, error) {
|
||||||
|
return ekey, nil
|
||||||
|
}
|
125
pdf/core/security/crypt/filter_v2.go
Normal file
125
pdf/core/security/crypt/filter_v2.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package crypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rc4"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/unidoc/unidoc/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerFilter("V2", newFilterV2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilterV2 creates a RC4-based filter with a specified key length (in bytes).
|
||||||
|
func NewFilterV2(length int) Filter {
|
||||||
|
f, err := newFilterV2(FilterDict{Length: length})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFilterV2(d FilterDict) (Filter, error) {
|
||||||
|
if d.Length%8 != 0 {
|
||||||
|
return nil, fmt.Errorf("Crypt filter length not multiple of 8 (%d)", d.Length)
|
||||||
|
}
|
||||||
|
// Standard security handler expresses the length in multiples of 8 (16 means 128)
|
||||||
|
// We only deal with standard so far. (Public key not supported yet).
|
||||||
|
if d.Length < 5 || d.Length > 16 {
|
||||||
|
if d.Length == 64 || d.Length == 128 {
|
||||||
|
common.Log.Debug("STANDARD VIOLATION: Crypt Length appears to be in bits rather than bytes - assuming bits (%d)", d.Length)
|
||||||
|
d.Length /= 8
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("Crypt filter length not in range 40 - 128 bit (%d)", d.Length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filterV2{length: d.Length}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeKeyV2 is a common object key generation shared by V2 and AESV2 crypt filters.
|
||||||
|
func makeKeyV2(objNum, genNum uint32, ekey []byte, isAES bool) ([]byte, error) {
|
||||||
|
key := make([]byte, len(ekey)+5)
|
||||||
|
for i := 0; i < len(ekey); i++ {
|
||||||
|
key[i] = ekey[i]
|
||||||
|
}
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
b := byte((objNum >> uint32(8*i)) & 0xff)
|
||||||
|
key[i+len(ekey)] = b
|
||||||
|
}
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
b := byte((genNum >> uint32(8*i)) & 0xff)
|
||||||
|
key[i+len(ekey)+3] = b
|
||||||
|
}
|
||||||
|
if isAES {
|
||||||
|
// If using the AES algorithm, extend the encryption key an
|
||||||
|
// additional 4 bytes by adding the value “sAlT”, which
|
||||||
|
// corresponds to the hexadecimal values 0x73, 0x41, 0x6C, 0x54.
|
||||||
|
key = append(key, 0x73)
|
||||||
|
key = append(key, 0x41)
|
||||||
|
key = append(key, 0x6C)
|
||||||
|
key = append(key, 0x54)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take the MD5.
|
||||||
|
h := md5.New()
|
||||||
|
h.Write(key)
|
||||||
|
hashb := h.Sum(nil)
|
||||||
|
|
||||||
|
if len(ekey)+5 < 16 {
|
||||||
|
return hashb[0 : len(ekey)+5], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterV2 is a RC4-based filter
|
||||||
|
type filterV2 struct {
|
||||||
|
length int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f filterV2) PDFVersion() [2]int {
|
||||||
|
return [2]int{} // TODO(dennwc): unspecified; check what it should be
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f filterV2) HandlerVersion() (V, R int) {
|
||||||
|
V, R = 2, 3
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterV2) Name() string {
|
||||||
|
return "V2"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f filterV2) KeyLength() int {
|
||||||
|
return f.length
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f filterV2) MakeKey(objNum, genNum uint32, ekey []byte) ([]byte, error) {
|
||||||
|
return makeKeyV2(objNum, genNum, ekey, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterV2) EncryptBytes(buf []byte, okey []byte) ([]byte, error) {
|
||||||
|
// Standard RC4 algorithm.
|
||||||
|
ciph, err := rc4.NewCipher(okey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
common.Log.Trace("RC4 Encrypt: % x", buf)
|
||||||
|
ciph.XORKeyStream(buf, buf)
|
||||||
|
common.Log.Trace("to: % x", buf)
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterV2) DecryptBytes(buf []byte, okey []byte) ([]byte, error) {
|
||||||
|
// Standard RC4 algorithm.
|
||||||
|
ciph, err := rc4.NewCipher(okey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
common.Log.Trace("RC4 Decrypt: % x", buf)
|
||||||
|
ciph.XORKeyStream(buf, buf)
|
||||||
|
common.Log.Trace("to: % x", buf)
|
||||||
|
return buf, nil
|
||||||
|
}
|
108
pdf/core/security/crypt/filters.go
Normal file
108
pdf/core/security/crypt/filters.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package crypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/unidoc/unidoc/pdf/core/security"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
filterMethods = make(map[string]filterFunc)
|
||||||
|
)
|
||||||
|
|
||||||
|
// filterFunc is used to construct crypt filters from CryptFilter dictionary
|
||||||
|
type filterFunc func(d FilterDict) (Filter, error)
|
||||||
|
|
||||||
|
// NewFilter creates CryptFilter from a corresponding dictionary.
|
||||||
|
func NewFilter(d FilterDict) (Filter, error) {
|
||||||
|
fnc, err := getFilter(d.CFM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cf, err := fnc(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIdentity creates an identity filter that bypasses all data without changes.
|
||||||
|
func NewIdentity() Filter {
|
||||||
|
return filterIdentity{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterDict represents information from a CryptFilter dictionary.
|
||||||
|
type FilterDict struct {
|
||||||
|
CFM string // The method used, if any, by the PDF reader to decrypt data.
|
||||||
|
AuthEvent security.AuthEvent
|
||||||
|
Length int // in bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerFilter register supported crypt filter methods.
|
||||||
|
// Table 25, CFM (page 92)
|
||||||
|
func registerFilter(name string, fnc filterFunc) {
|
||||||
|
if _, ok := filterMethods[name]; ok {
|
||||||
|
panic("already registered")
|
||||||
|
}
|
||||||
|
filterMethods[name] = fnc
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFilter check if a CFM with a specified name is supported an returns its implementation.
|
||||||
|
func getFilter(name string) (filterFunc, error) {
|
||||||
|
f := filterMethods[string(name)]
|
||||||
|
if f == nil {
|
||||||
|
return nil, fmt.Errorf("unsupported crypt filter: %q", name)
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter is a common interface for crypt filter methods.
|
||||||
|
type Filter interface {
|
||||||
|
// Name returns a name of the filter that should be used in CFM field of Encrypt dictionary.
|
||||||
|
Name() string
|
||||||
|
// KeyLength returns a length of the encryption key in bytes.
|
||||||
|
KeyLength() int
|
||||||
|
// PDFVersion reports the minimal version of PDF document that introduced this filter.
|
||||||
|
PDFVersion() [2]int
|
||||||
|
// HandlerVersion reports V and R parameters that should be used for this filter.
|
||||||
|
HandlerVersion() (V, R int)
|
||||||
|
// MakeKey generates a object encryption key based on file encryption key and object numbers.
|
||||||
|
// Used only for legacy filters - AESV3 doesn't change the key for each object.
|
||||||
|
MakeKey(objNum, genNum uint32, fkey []byte) ([]byte, error)
|
||||||
|
// EncryptBytes encrypts a buffer using object encryption key, as returned by MakeKey.
|
||||||
|
// Implementation may reuse a buffer and encrypt data in-place.
|
||||||
|
EncryptBytes(p []byte, okey []byte) ([]byte, error)
|
||||||
|
// DecryptBytes decrypts a buffer using object encryption key, as returned by MakeKey.
|
||||||
|
// Implementation may reuse a buffer and decrypt data in-place.
|
||||||
|
DecryptBytes(p []byte, okey []byte) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type filterIdentity struct{}
|
||||||
|
|
||||||
|
func (filterIdentity) PDFVersion() [2]int {
|
||||||
|
return [2]int{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterIdentity) HandlerVersion() (V, R int) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterIdentity) Name() string {
|
||||||
|
return "Identity"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterIdentity) KeyLength() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterIdentity) MakeKey(objNum, genNum uint32, fkey []byte) ([]byte, error) {
|
||||||
|
return fkey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterIdentity) EncryptBytes(p []byte, okey []byte) ([]byte, error) {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterIdentity) DecryptBytes(p []byte, okey []byte) ([]byte, error) {
|
||||||
|
return p, nil
|
||||||
|
}
|
@ -17,6 +17,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/unidoc/unidoc/pdf/core/security/crypt"
|
||||||
|
|
||||||
"github.com/unidoc/unidoc/common"
|
"github.com/unidoc/unidoc/common"
|
||||||
"github.com/unidoc/unidoc/common/license"
|
"github.com/unidoc/unidoc/common/license"
|
||||||
. "github.com/unidoc/unidoc/pdf/core"
|
. "github.com/unidoc/unidoc/pdf/core"
|
||||||
@ -676,16 +678,14 @@ func (this *PdfWriter) Encrypt(userPass, ownerPass []byte, options *EncryptOptio
|
|||||||
perm = options.Permissions
|
perm = options.Permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
var cf CryptFilter
|
var cf crypt.Filter
|
||||||
switch algo {
|
switch algo {
|
||||||
case RC4_128bit:
|
case RC4_128bit:
|
||||||
cf = NewCryptFilterV2(16)
|
cf = crypt.NewFilterV2(16)
|
||||||
case AES_128bit:
|
case AES_128bit:
|
||||||
this.SetVersion(1, 5)
|
cf = crypt.NewFilterAESV2()
|
||||||
cf = NewCryptFilterAESV2()
|
|
||||||
case AES_256bit:
|
case AES_256bit:
|
||||||
this.SetVersion(2, 0)
|
cf = crypt.NewFilterAESV3()
|
||||||
cf = NewCryptFilterAESV3()
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported algorithm: %v", options.Algorithm)
|
return fmt.Errorf("unsupported algorithm: %v", options.Algorithm)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user