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/pdf/core/security"
|
||||
crypto "github.com/unidoc/unidoc/pdf/core/security/crypt"
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
@ -28,7 +29,7 @@ type EncryptInfo struct {
|
||||
}
|
||||
|
||||
// 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{
|
||||
encryptedObjects: make(map[PdfObject]bool),
|
||||
cryptFilters: make(cryptFilters),
|
||||
@ -37,26 +38,19 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm securit
|
||||
EncryptMetadata: true,
|
||||
},
|
||||
}
|
||||
// TODO(dennwc): define it in the CF interface
|
||||
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 {
|
||||
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 = StandardCryptFilter
|
||||
defaultFilter = stdCryptFilter
|
||||
)
|
||||
crypter.cryptFilters[defaultFilter] = cf
|
||||
if crypter.encrypt.V >= 4 {
|
||||
@ -97,6 +91,28 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm securit
|
||||
}, 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.
|
||||
func encodeEncryptStd(d *security.StdEncryptDict, ed *PdfObjectDictionary) {
|
||||
ed.Set("R", MakeInteger(int64(d.R)))
|
||||
@ -200,26 +216,32 @@ func decodeEncryptStd(d *security.StdEncryptDict, ed *PdfObjectDictionary) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// PdfCrypt provides PDF encryption/decryption support.
|
||||
// The PDF standard supports encryption of strings and streams (Section 7.6).
|
||||
// TODO (v3): Consider unexporting.
|
||||
type PdfCrypt struct {
|
||||
encrypt encryptDict
|
||||
encryptStd security.StdEncryptDict
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
id0 string
|
||||
encryptionKey []byte
|
||||
decryptedObjects map[PdfObject]bool
|
||||
encryptedObjects map[PdfObject]bool
|
||||
authenticated bool
|
||||
// Crypt filters (V4).
|
||||
cryptFilters cryptFilters
|
||||
streamFilter string
|
||||
stringFilter string
|
||||
// Method.
|
||||
name, ok := d.Get("CFM").(*PdfObjectName)
|
||||
if !ok {
|
||||
return fmt.Errorf("Unsupported crypt filter (None)")
|
||||
}
|
||||
cf.CFM = string(*name)
|
||||
|
||||
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 {
|
||||
@ -262,65 +284,31 @@ func (crypt *PdfCrypt) String() string {
|
||||
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.
|
||||
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.
|
||||
CF cryptFiltersDict // Crypt filters dictionary.
|
||||
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.
|
||||
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.
|
||||
}
|
||||
|
||||
// StandardCryptFilter is a default name for a standard crypt filter.
|
||||
const StandardCryptFilter = "StdCF"
|
||||
// stdCryptFilter is a default name for a standard crypt filter.
|
||||
const stdCryptFilter = "StdCF"
|
||||
|
||||
func newCryptFiltersV2(length int) 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.
|
||||
type cryptFilters map[string]CryptFilter
|
||||
type cryptFilters map[string]crypto.Filter
|
||||
|
||||
// loadCryptFilters loads crypt filter information from the encryption dictionary (V>=4).
|
||||
func (crypt *PdfCrypt) loadCryptFilters(ed *PdfObjectDictionary) error {
|
||||
@ -365,22 +353,18 @@ func (crypt *PdfCrypt) loadCryptFilters(ed *PdfObjectDictionary) error {
|
||||
continue
|
||||
}
|
||||
|
||||
var cfd cryptFilterDict
|
||||
if err := cfd.ReadFrom(dict); err != nil {
|
||||
var cfd crypto.FilterDict
|
||||
if err := decodeCryptFilter(&cfd, dict); err != nil {
|
||||
return err
|
||||
}
|
||||
fnc, err := getCryptFilterMethod(cfd.CFM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cf, err := fnc(cfd)
|
||||
cf, err := crypto.NewFilter(cfd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
crypt.cryptFilters[string(name)] = cf
|
||||
}
|
||||
// Cannot be overwritten.
|
||||
crypt.cryptFilters["Identity"] = cryptFilteridentity{}
|
||||
crypt.cryptFilters["Identity"] = crypto.NewIdentity()
|
||||
|
||||
// StrF strings filter.
|
||||
crypt.stringFilter = "Identity"
|
||||
@ -403,6 +387,18 @@ func (crypt *PdfCrypt) loadCryptFilters(ed *PdfObjectDictionary) error {
|
||||
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 {
|
||||
@ -415,7 +411,7 @@ func (crypt *PdfCrypt) saveCryptFilters(ed *PdfObjectDictionary) error {
|
||||
if name == "Identity" {
|
||||
continue
|
||||
}
|
||||
v := cryptFilterToDict(filter, "")
|
||||
v := encodeCryptFilter(filter, "")
|
||||
cf.Set(PdfObjectName(name), v)
|
||||
}
|
||||
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).
|
||||
// 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 {
|
||||
streamFilter = 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:
|
||||
common.Log.Trace("Decrypting string!")
|
||||
|
||||
stringFilter := StandardCryptFilter
|
||||
stringFilter := stdCryptFilter
|
||||
if crypt.encrypt.V >= 4 {
|
||||
// Currently only support Identity / RC4.
|
||||
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).
|
||||
// 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 {
|
||||
// For now. Need to change when we add support for more than
|
||||
// Identity / RC4.
|
||||
@ -893,7 +889,7 @@ func (crypt *PdfCrypt) Encrypt(obj PdfObject, parentObjNum, parentGenNum int64)
|
||||
case *PdfObjectString:
|
||||
common.Log.Trace("Encrypting string!")
|
||||
|
||||
stringFilter := StandardCryptFilter
|
||||
stringFilter := stdCryptFilter
|
||||
if crypt.encrypt.V >= 4 {
|
||||
common.Log.Trace("with %s filter", crypt.stringFilter)
|
||||
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"
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unidoc/pdf/core/security/crypt"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/common/license"
|
||||
. "github.com/unidoc/unidoc/pdf/core"
|
||||
@ -676,16 +678,14 @@ func (this *PdfWriter) Encrypt(userPass, ownerPass []byte, options *EncryptOptio
|
||||
perm = options.Permissions
|
||||
}
|
||||
|
||||
var cf CryptFilter
|
||||
var cf crypt.Filter
|
||||
switch algo {
|
||||
case RC4_128bit:
|
||||
cf = NewCryptFilterV2(16)
|
||||
cf = crypt.NewFilterV2(16)
|
||||
case AES_128bit:
|
||||
this.SetVersion(1, 5)
|
||||
cf = NewCryptFilterAESV2()
|
||||
cf = crypt.NewFilterAESV2()
|
||||
case AES_256bit:
|
||||
this.SetVersion(2, 0)
|
||||
cf = NewCryptFilterAESV3()
|
||||
cf = crypt.NewFilterAESV3()
|
||||
default:
|
||||
return fmt.Errorf("unsupported algorithm: %v", options.Algorithm)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user