mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-05 19:30:30 +08:00
core: split security handlers into a separate package
This commit is contained in:
parent
006e5f9e6e
commit
42df346e69
@ -10,10 +10,10 @@ import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/pdf/core/security"
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
@ -28,11 +28,11 @@ type EncryptInfo struct {
|
||||
}
|
||||
|
||||
// PdfCryptNewEncrypt makes the document crypt handler based on a specified crypt filter.
|
||||
func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessPermissions) (*PdfCrypt, *EncryptInfo, error) {
|
||||
func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm security.Permissions) (*PdfCrypt, *EncryptInfo, error) {
|
||||
crypter := &PdfCrypt{
|
||||
encryptedObjects: make(map[PdfObject]bool),
|
||||
cryptFilters: make(cryptFilters),
|
||||
encryptStd: stdEncryptDict{
|
||||
encryptStd: security.StdEncryptDict{
|
||||
P: perm,
|
||||
EncryptMetadata: true,
|
||||
},
|
||||
@ -83,7 +83,7 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessP
|
||||
return nil, nil, err
|
||||
}
|
||||
// encode parameters generated by the Standard security handler
|
||||
crypter.encryptStd.EncodeTo(ed)
|
||||
encodeEncryptStd(&crypter.encryptStd, ed)
|
||||
if crypter.encrypt.V >= 4 {
|
||||
if err := crypter.saveCryptFilters(ed); err != nil {
|
||||
return nil, nil, err
|
||||
@ -97,12 +97,115 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessP
|
||||
}, nil
|
||||
}
|
||||
|
||||
// encodeEncryptStd encodes fields of standard security handler to an Encrypt dictionary.
|
||||
func encodeEncryptStd(d *security.StdEncryptDict, ed *PdfObjectDictionary) {
|
||||
ed.Set("R", MakeInteger(int64(d.R)))
|
||||
ed.Set("P", MakeInteger(int64(d.P)))
|
||||
|
||||
ed.Set("O", MakeStringFromBytes(d.O))
|
||||
ed.Set("U", MakeStringFromBytes(d.U))
|
||||
if d.R >= 5 {
|
||||
ed.Set("OE", MakeStringFromBytes(d.OE))
|
||||
ed.Set("UE", MakeStringFromBytes(d.UE))
|
||||
ed.Set("EncryptMetadata", MakeBool(d.EncryptMetadata))
|
||||
if d.R > 5 {
|
||||
ed.Set("Perms", MakeStringFromBytes(d.Perms))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decodeEncryptStd decodes fields of standard security handler from an Encrypt dictionary.
|
||||
func decodeEncryptStd(d *security.StdEncryptDict, ed *PdfObjectDictionary) error {
|
||||
// TODO(dennwc): this code is too verbose; maybe use reflection to populate fields and validate afterwards?
|
||||
R, ok := ed.Get("R").(*PdfObjectInteger)
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing R")
|
||||
}
|
||||
// TODO(dennwc): according to spec, R should be validated according to V value
|
||||
if *R < 2 || *R > 6 {
|
||||
return fmt.Errorf("invalid R (%d)", *R)
|
||||
}
|
||||
d.R = int(*R)
|
||||
|
||||
O, ok := ed.GetString("O")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing O")
|
||||
}
|
||||
if d.R == 5 || d.R == 6 {
|
||||
// the spec says =48 bytes, but Acrobat pads them out longer
|
||||
if len(O) < 48 {
|
||||
return fmt.Errorf("Length(O) < 48 (%d)", len(O))
|
||||
}
|
||||
} else if len(O) != 32 {
|
||||
return fmt.Errorf("Length(O) != 32 (%d)", len(O))
|
||||
}
|
||||
d.O = []byte(O)
|
||||
|
||||
U, ok := ed.GetString("U")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing U")
|
||||
}
|
||||
if d.R == 5 || d.R == 6 {
|
||||
// the spec says =48 bytes, but Acrobat pads them out longer
|
||||
if len(U) < 48 {
|
||||
return fmt.Errorf("Length(U) < 48 (%d)", len(U))
|
||||
}
|
||||
} else if len(U) != 32 {
|
||||
// Strictly this does not cause an error.
|
||||
// If O is OK and others then can still read the file.
|
||||
common.Log.Debug("Warning: Length(U) != 32 (%d)", len(U))
|
||||
//return crypter, errors.New("Length(U) != 32")
|
||||
}
|
||||
d.U = []byte(U)
|
||||
|
||||
if d.R >= 5 {
|
||||
OE, ok := ed.GetString("OE")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing OE")
|
||||
} else if len(OE) != 32 {
|
||||
return fmt.Errorf("Length(OE) != 32 (%d)", len(OE))
|
||||
}
|
||||
d.OE = []byte(OE)
|
||||
|
||||
UE, ok := ed.GetString("UE")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing UE")
|
||||
} else if len(UE) != 32 {
|
||||
return fmt.Errorf("Length(UE) != 32 (%d)", len(UE))
|
||||
}
|
||||
d.UE = []byte(UE)
|
||||
}
|
||||
|
||||
P, ok := ed.Get("P").(*PdfObjectInteger)
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing permissions attr")
|
||||
}
|
||||
d.P = security.Permissions(*P)
|
||||
|
||||
if d.R == 6 {
|
||||
Perms, ok := ed.GetString("Perms")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing Perms")
|
||||
} else if len(Perms) != 16 {
|
||||
return fmt.Errorf("Length(Perms) != 16 (%d)", len(Perms))
|
||||
}
|
||||
d.Perms = []byte(Perms)
|
||||
}
|
||||
|
||||
if em, ok := ed.Get("EncryptMetadata").(*PdfObjectBool); ok {
|
||||
d.EncryptMetadata = bool(*em)
|
||||
} else {
|
||||
d.EncryptMetadata = true // True by default.
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 stdEncryptDict
|
||||
encryptStd security.StdEncryptDict
|
||||
|
||||
id0 string
|
||||
encryptionKey []byte
|
||||
@ -117,7 +220,6 @@ type PdfCrypt struct {
|
||||
parser *PdfParser
|
||||
|
||||
decryptedObjNum map[int]struct{}
|
||||
ivAESZero []byte // a zero buffer used as an initialization vector for AES
|
||||
}
|
||||
|
||||
func (crypt *PdfCrypt) newEncyptDict() *PdfObjectDictionary {
|
||||
@ -181,35 +283,6 @@ type encryptDict struct {
|
||||
EFF string // The filter that shall be used when decrypting embedded file streams.
|
||||
}
|
||||
|
||||
// AccessPermissions is a bitmask of access permissions for a PDF file.
|
||||
type AccessPermissions uint32
|
||||
|
||||
const (
|
||||
// PermOwner grants all permissions.
|
||||
PermOwner = AccessPermissions(math.MaxUint32)
|
||||
|
||||
PermPrinting = AccessPermissions(1 << 2) // bit 3
|
||||
PermModify = AccessPermissions(1 << 3) // bit 4
|
||||
PermExtractGraphics = AccessPermissions(1 << 4) // bit 5
|
||||
PermAnnotate = AccessPermissions(1 << 5) // bit 6
|
||||
// PermFillForms allow form filling, if annotation is disabled? If annotation enabled, is not looked at.
|
||||
PermFillForms = AccessPermissions(1 << 8) // bit 9
|
||||
PermDisabilityExtract = AccessPermissions(1 << 9) // bit 10 // TODO: not clear what this means!
|
||||
// PermRotateInsert allows rotating, editing page order.
|
||||
PermRotateInsert = AccessPermissions(1 << 10) // bit 11
|
||||
// PermFullPrintQuality limits print quality (lowres), assuming Printing bit is set.
|
||||
PermFullPrintQuality = AccessPermissions(1 << 11) // bit 12
|
||||
)
|
||||
|
||||
// Allowed checks if a set of permissions can be granted.
|
||||
func (p AccessPermissions) Allowed(p2 AccessPermissions) bool {
|
||||
return p&p2 == p2
|
||||
}
|
||||
|
||||
const padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF" +
|
||||
"\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C" +
|
||||
"\xA9\xFE\x64\x53\x69\x7A"
|
||||
|
||||
// StandardCryptFilter is a default name for a standard crypt filter.
|
||||
const StandardCryptFilter = "StdCF"
|
||||
|
||||
@ -405,7 +478,7 @@ func PdfCryptNewDecrypt(parser *PdfParser, ed, trailer *PdfObjectDictionary) (*P
|
||||
}
|
||||
|
||||
// decode Standard security handler parameters
|
||||
if err := crypter.encryptStd.DecodeFrom(ed); err != nil {
|
||||
if err := decodeEncryptStd(&crypter.encryptStd, ed); err != nil {
|
||||
return crypter, err
|
||||
}
|
||||
|
||||
@ -428,18 +501,15 @@ func PdfCryptNewDecrypt(parser *PdfParser, ed, trailer *PdfObjectDictionary) (*P
|
||||
}
|
||||
|
||||
// GetAccessPermissions returns the PDF access permissions as an AccessPermissions object.
|
||||
func (crypt *PdfCrypt) GetAccessPermissions() AccessPermissions {
|
||||
func (crypt *PdfCrypt) GetAccessPermissions() security.Permissions {
|
||||
return crypt.encryptStd.P
|
||||
}
|
||||
|
||||
func (crypt *PdfCrypt) securityHandler() stdSecurityHandler {
|
||||
func (crypt *PdfCrypt) securityHandler() security.StdHandler {
|
||||
if crypt.encryptStd.R >= 5 {
|
||||
return stdHandlerR6{}
|
||||
}
|
||||
return stdHandlerR4{
|
||||
ID0: crypt.id0,
|
||||
Length: crypt.encrypt.Length,
|
||||
return security.NewHandlerR6()
|
||||
}
|
||||
return security.NewHandlerR4(crypt.id0, crypt.encrypt.Length)
|
||||
}
|
||||
|
||||
// Check whether the specified password can be used to decrypt the document.
|
||||
@ -464,7 +534,7 @@ func (crypt *PdfCrypt) authenticate(password []byte) (bool, error) {
|
||||
// The bool flag indicates that the user can access and can view the file.
|
||||
// The AccessPermissions shows what access the user has for editing etc.
|
||||
// An error is returned if there was a problem performing the authentication.
|
||||
func (crypt *PdfCrypt) checkAccessRights(password []byte) (bool, AccessPermissions, error) {
|
||||
func (crypt *PdfCrypt) checkAccessRights(password []byte) (bool, security.Permissions, error) {
|
||||
h := crypt.securityHandler()
|
||||
// TODO(dennwc): it computes an encryption key as well; if necessary, define a new interface method to optimize this
|
||||
fkey, perm, err := h.Authenticate(&crypt.encryptStd, password)
|
||||
|
@ -1,139 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
)
|
||||
|
||||
type stdSecurityHandler interface {
|
||||
// GenerateParams uses owner and user passwords to set encryption parameters and generate an encryption key.
|
||||
// It assumes that R, P and EncryptMetadata are already set.
|
||||
GenerateParams(d *stdEncryptDict, ownerPass, userPass []byte) ([]byte, error)
|
||||
|
||||
// Authenticate uses encryption dictionary parameters and the password to calculate
|
||||
// the document encryption key. It also returns permissions that should be granted to a user.
|
||||
// In case of failed authentication, it returns empty key and zero permissions with no error.
|
||||
Authenticate(d *stdEncryptDict, pass []byte) ([]byte, AccessPermissions, error)
|
||||
}
|
||||
|
||||
// stdEncryptDict is a set of additional fields used in standard encryption dictionary.
|
||||
type stdEncryptDict struct {
|
||||
R int // (Required) A number specifying which revision of the standard security handler shall be used.
|
||||
|
||||
P AccessPermissions
|
||||
EncryptMetadata bool // Indicates whether the document-level metadata stream shall be encrypted.
|
||||
|
||||
// set by security handlers:
|
||||
|
||||
O, U []byte
|
||||
OE, UE []byte // R=6
|
||||
Perms []byte // An encrypted copy of P (16 bytes). Used to verify permissions. R=6
|
||||
}
|
||||
|
||||
func (d *stdEncryptDict) EncodeTo(ed *PdfObjectDictionary) {
|
||||
ed.Set("R", MakeInteger(int64(d.R)))
|
||||
ed.Set("P", MakeInteger(int64(d.P)))
|
||||
|
||||
ed.Set("O", MakeStringFromBytes(d.O))
|
||||
ed.Set("U", MakeStringFromBytes(d.U))
|
||||
if d.R >= 5 {
|
||||
ed.Set("OE", MakeStringFromBytes(d.OE))
|
||||
ed.Set("UE", MakeStringFromBytes(d.UE))
|
||||
ed.Set("EncryptMetadata", MakeBool(d.EncryptMetadata))
|
||||
if d.R > 5 {
|
||||
ed.Set("Perms", MakeStringFromBytes(d.Perms))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *stdEncryptDict) DecodeFrom(ed *PdfObjectDictionary) error {
|
||||
// TODO(dennwc): this code is too verbose; maybe use reflection to populate fields and validate afterwards?
|
||||
R, ok := ed.Get("R").(*PdfObjectInteger)
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing R")
|
||||
}
|
||||
// TODO(dennwc): according to spec, R should be validated according to V value
|
||||
if *R < 2 || *R > 6 {
|
||||
return fmt.Errorf("invalid R (%d)", *R)
|
||||
}
|
||||
d.R = int(*R)
|
||||
|
||||
O, ok := ed.GetString("O")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing O")
|
||||
}
|
||||
if d.R == 5 || d.R == 6 {
|
||||
// the spec says =48 bytes, but Acrobat pads them out longer
|
||||
if len(O) < 48 {
|
||||
return fmt.Errorf("Length(O) < 48 (%d)", len(O))
|
||||
}
|
||||
} else if len(O) != 32 {
|
||||
return fmt.Errorf("Length(O) != 32 (%d)", len(O))
|
||||
}
|
||||
d.O = []byte(O)
|
||||
|
||||
U, ok := ed.GetString("U")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing U")
|
||||
}
|
||||
if d.R == 5 || d.R == 6 {
|
||||
// the spec says =48 bytes, but Acrobat pads them out longer
|
||||
if len(U) < 48 {
|
||||
return fmt.Errorf("Length(U) < 48 (%d)", len(U))
|
||||
}
|
||||
} else if len(U) != 32 {
|
||||
// Strictly this does not cause an error.
|
||||
// If O is OK and others then can still read the file.
|
||||
common.Log.Debug("Warning: Length(U) != 32 (%d)", len(U))
|
||||
//return crypter, errors.New("Length(U) != 32")
|
||||
}
|
||||
d.U = []byte(U)
|
||||
|
||||
if d.R >= 5 {
|
||||
OE, ok := ed.GetString("OE")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing OE")
|
||||
} else if len(OE) != 32 {
|
||||
return fmt.Errorf("Length(OE) != 32 (%d)", len(OE))
|
||||
}
|
||||
d.OE = []byte(OE)
|
||||
|
||||
UE, ok := ed.GetString("UE")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing UE")
|
||||
} else if len(UE) != 32 {
|
||||
return fmt.Errorf("Length(UE) != 32 (%d)", len(UE))
|
||||
}
|
||||
d.UE = []byte(UE)
|
||||
}
|
||||
|
||||
P, ok := ed.Get("P").(*PdfObjectInteger)
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing permissions attr")
|
||||
}
|
||||
d.P = AccessPermissions(*P)
|
||||
|
||||
if d.R == 6 {
|
||||
Perms, ok := ed.GetString("Perms")
|
||||
if !ok {
|
||||
return errors.New("encrypt dictionary missing Perms")
|
||||
} else if len(Perms) != 16 {
|
||||
return fmt.Errorf("Length(Perms) != 16 (%d)", len(Perms))
|
||||
}
|
||||
d.Perms = []byte(Perms)
|
||||
}
|
||||
|
||||
if em, ok := ed.Get("EncryptMetadata").(*PdfObjectBool); ok {
|
||||
d.EncryptMetadata = bool(*em)
|
||||
} else {
|
||||
d.EncryptMetadata = true // True by default.
|
||||
}
|
||||
return nil
|
||||
}
|
@ -11,6 +11,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/pdf/core/security"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -24,7 +25,7 @@ func TestDecryption1(t *testing.T) {
|
||||
V: 2,
|
||||
Length: 128,
|
||||
},
|
||||
encryptStd: stdEncryptDict{
|
||||
encryptStd: security.StdEncryptDict{
|
||||
R: 3,
|
||||
P: 0xfffff0c0,
|
||||
EncryptMetadata: true,
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/pdf/core/security"
|
||||
)
|
||||
|
||||
// Regular Expressions for parsing and identifying object signatures.
|
||||
@ -1641,11 +1642,11 @@ func (parser *PdfParser) Decrypt(password []byte) (bool, error) {
|
||||
// The bool flag indicates that the user can access and view the file.
|
||||
// The AccessPermissions shows what access the user has for editing etc.
|
||||
// An error is returned if there was a problem performing the authentication.
|
||||
func (parser *PdfParser) CheckAccessRights(password []byte) (bool, AccessPermissions, error) {
|
||||
func (parser *PdfParser) CheckAccessRights(password []byte) (bool, security.Permissions, error) {
|
||||
// Also build the encryption/decryption key.
|
||||
if parser.crypter == nil {
|
||||
// If the crypter is not set, the file is not encrypted and we can assume full access permissions.
|
||||
return true, PermOwner, nil
|
||||
return true, security.PermOwner, nil
|
||||
}
|
||||
return parser.crypter.checkAccessRights(password)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package core
|
||||
package security
|
||||
|
||||
import "crypto/cipher"
|
||||
|
32
pdf/core/security/handlers.go
Normal file
32
pdf/core/security/handlers.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package security
|
||||
|
||||
// StdHandler is an interface for standard security handlers.
|
||||
type StdHandler interface {
|
||||
// GenerateParams uses owner and user passwords to set encryption parameters and generate an encryption key.
|
||||
// It assumes that R, P and EncryptMetadata are already set.
|
||||
GenerateParams(d *StdEncryptDict, ownerPass, userPass []byte) ([]byte, error)
|
||||
|
||||
// Authenticate uses encryption dictionary parameters and the password to calculate
|
||||
// the document encryption key. It also returns permissions that should be granted to a user.
|
||||
// In case of failed authentication, it returns empty key and zero permissions with no error.
|
||||
Authenticate(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error)
|
||||
}
|
||||
|
||||
// StdEncryptDict is a set of additional fields used in standard encryption dictionary.
|
||||
type StdEncryptDict struct {
|
||||
R int // (Required) A number specifying which revision of the standard security handler shall be used.
|
||||
|
||||
P Permissions
|
||||
EncryptMetadata bool // Indicates whether the document-level metadata stream shall be encrypted.
|
||||
|
||||
// set by security handlers:
|
||||
|
||||
O, U []byte
|
||||
OE, UE []byte // R=6
|
||||
Perms []byte // An encrypted copy of P (16 bytes). Used to verify permissions. R=6
|
||||
}
|
28
pdf/core/security/permissions.go
Normal file
28
pdf/core/security/permissions.go
Normal file
@ -0,0 +1,28 @@
|
||||
package security
|
||||
|
||||
import "math"
|
||||
|
||||
// Permissions is a bitmask of access permissions for a PDF file.
|
||||
type Permissions uint32
|
||||
|
||||
const (
|
||||
// PermOwner grants all permissions.
|
||||
PermOwner = Permissions(math.MaxUint32)
|
||||
|
||||
PermPrinting = Permissions(1 << 2) // bit 3
|
||||
PermModify = Permissions(1 << 3) // bit 4
|
||||
PermExtractGraphics = Permissions(1 << 4) // bit 5
|
||||
PermAnnotate = Permissions(1 << 5) // bit 6
|
||||
// PermFillForms allow form filling, if annotation is disabled? If annotation enabled, is not looked at.
|
||||
PermFillForms = Permissions(1 << 8) // bit 9
|
||||
PermDisabilityExtract = Permissions(1 << 9) // bit 10 // TODO: not clear what this means!
|
||||
// PermRotateInsert allows rotating, editing page order.
|
||||
PermRotateInsert = Permissions(1 << 10) // bit 11
|
||||
// PermFullPrintQuality limits print quality (lowres), assuming Printing bit is set.
|
||||
PermFullPrintQuality = Permissions(1 << 11) // bit 12
|
||||
)
|
||||
|
||||
// Allowed checks if a set of permissions can be granted.
|
||||
func (p Permissions) Allowed(p2 Permissions) bool {
|
||||
return p&p2 == p2
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package core
|
||||
package security
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -15,7 +15,16 @@ import (
|
||||
"github.com/unidoc/unidoc/common"
|
||||
)
|
||||
|
||||
var _ stdSecurityHandler = stdHandlerR4{}
|
||||
var _ StdHandler = stdHandlerR4{}
|
||||
|
||||
const padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF" +
|
||||
"\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C" +
|
||||
"\xA9\xFE\x64\x53\x69\x7A"
|
||||
|
||||
// NewHandlerR4 creates a new standard security handler for R<=4.
|
||||
func NewHandlerR4(id0 string, length int) StdHandler {
|
||||
return stdHandlerR4{ID0: id0, Length: length}
|
||||
}
|
||||
|
||||
type stdHandlerR4 struct {
|
||||
Length int
|
||||
@ -40,7 +49,7 @@ func (sh stdHandlerR4) paddedPass(pass []byte) []byte {
|
||||
}
|
||||
|
||||
// alg2 computes an encryption key.
|
||||
func (sh stdHandlerR4) alg2(d *stdEncryptDict, pass []byte) []byte {
|
||||
func (sh stdHandlerR4) alg2(d *StdEncryptDict, pass []byte) []byte {
|
||||
common.Log.Trace("alg2")
|
||||
key := sh.paddedPass(pass)
|
||||
|
||||
@ -212,7 +221,7 @@ func (sh stdHandlerR4) alg5(ekey []byte, upass []byte) ([]byte, error) {
|
||||
|
||||
// alg6 authenticates the user password and returns the document encryption key.
|
||||
// It returns an nil key in case authentication failed.
|
||||
func (sh stdHandlerR4) alg6(d *stdEncryptDict, upass []byte) ([]byte, error) {
|
||||
func (sh stdHandlerR4) alg6(d *StdEncryptDict, upass []byte) ([]byte, error) {
|
||||
var (
|
||||
uo []byte
|
||||
err error
|
||||
@ -252,7 +261,7 @@ func (sh stdHandlerR4) alg6(d *stdEncryptDict, upass []byte) ([]byte, error) {
|
||||
|
||||
// alg7 authenticates the owner password and returns the document encryption key.
|
||||
//// It returns an nil key in case authentication failed.
|
||||
func (sh stdHandlerR4) alg7(d *stdEncryptDict, opass []byte) ([]byte, error) {
|
||||
func (sh stdHandlerR4) alg7(d *StdEncryptDict, opass []byte) ([]byte, error) {
|
||||
encKey := sh.alg3Key(d.R, opass)
|
||||
|
||||
decrypted := make([]byte, len(d.O))
|
||||
@ -289,7 +298,7 @@ func (sh stdHandlerR4) alg7(d *stdEncryptDict, opass []byte) ([]byte, error) {
|
||||
return ekey, nil
|
||||
}
|
||||
|
||||
func (sh stdHandlerR4) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([]byte, error) {
|
||||
func (sh stdHandlerR4) GenerateParams(d *StdEncryptDict, opass, upass []byte) ([]byte, error) {
|
||||
// Make the O and U objects.
|
||||
O, err := sh.alg3(d.R, upass, opass)
|
||||
if err != nil {
|
||||
@ -311,7 +320,7 @@ func (sh stdHandlerR4) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([
|
||||
return ekey, nil
|
||||
}
|
||||
|
||||
func (sh stdHandlerR4) Authenticate(d *stdEncryptDict, pass []byte) ([]byte, AccessPermissions, error) {
|
||||
func (sh stdHandlerR4) Authenticate(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error) {
|
||||
// Try owner password.
|
||||
// May not be necessary if only want to get all contents.
|
||||
// (user pass needs to be known or empty).
|
@ -3,7 +3,7 @@
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package core
|
||||
package security
|
||||
|
||||
import (
|
||||
"github.com/unidoc/unidoc/common"
|
||||
@ -53,7 +53,7 @@ func TestAlg2(t *testing.T) {
|
||||
0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}),
|
||||
Length: 128,
|
||||
}
|
||||
d := &stdEncryptDict{
|
||||
d := &StdEncryptDict{
|
||||
R: 3,
|
||||
P: 0xfffff0c0,
|
||||
EncryptMetadata: true,
|
||||
@ -110,7 +110,7 @@ func TestAlg5(t *testing.T) {
|
||||
0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}),
|
||||
Length: 128,
|
||||
}
|
||||
d := &stdEncryptDict{
|
||||
d := &StdEncryptDict{
|
||||
R: 3,
|
||||
P: 0xfffff0c0,
|
||||
EncryptMetadata: true,
|
@ -3,7 +3,7 @@
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package core
|
||||
package security
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -19,13 +19,19 @@ import (
|
||||
"math"
|
||||
)
|
||||
|
||||
var _ stdSecurityHandler = stdHandlerR6{}
|
||||
var _ StdHandler = stdHandlerR6{}
|
||||
|
||||
// NewHandlerR6 creates a new standard security handler for R=5 and R=6.
|
||||
func NewHandlerR6() StdHandler {
|
||||
return stdHandlerR6{}
|
||||
}
|
||||
|
||||
// stdHandlerR6 is an implementation of standard security handler with R=5 and R=6.
|
||||
type stdHandlerR6 struct{}
|
||||
|
||||
// alg2a retrieves the encryption key from an encrypted document (R >= 5).
|
||||
// 7.6.4.3.2 Algorithm 2.A (page 83)
|
||||
func (sh stdHandlerR6) alg2a(d *stdEncryptDict, pass []byte) ([]byte, AccessPermissions, error) {
|
||||
func (sh stdHandlerR6) alg2a(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error) {
|
||||
// O & U: 32 byte hash + 8 byte Validation Salt + 8 byte Key Salt
|
||||
|
||||
// step a: Unicode normalization
|
||||
@ -46,7 +52,7 @@ func (sh stdHandlerR6) alg2a(d *stdEncryptDict, pass []byte) ([]byte, AccessPerm
|
||||
ekey []byte // encrypted file key
|
||||
ukey []byte // user key; set only when using owner's password
|
||||
)
|
||||
var perm AccessPermissions
|
||||
var perm Permissions
|
||||
if len(h) != 0 {
|
||||
// owner password valid
|
||||
perm = PermOwner
|
||||
@ -217,7 +223,7 @@ func (sh stdHandlerR6) alg2b(R int, data, pwd, userKey []byte) []byte {
|
||||
|
||||
// alg8 computes the encryption dictionary's U (user password) and UE (user encryption) values (R>=5).
|
||||
// 7.6.4.4.6 Algorithm 8 (page 86)
|
||||
func (sh stdHandlerR6) alg8(d *stdEncryptDict, ekey []byte, upass []byte) error {
|
||||
func (sh stdHandlerR6) alg8(d *StdEncryptDict, ekey []byte, upass []byte) error {
|
||||
// step a: compute U (user password)
|
||||
var rbuf [16]byte
|
||||
if _, err := io.ReadFull(rand.Reader, rbuf[:]); err != nil {
|
||||
@ -263,7 +269,7 @@ func (sh stdHandlerR6) alg8(d *stdEncryptDict, ekey []byte, upass []byte) error
|
||||
|
||||
// alg9 computes the encryption dictionary's O (owner password) and OE (owner encryption) values (R>=5).
|
||||
// 7.6.4.4.7 Algorithm 9 (page 86)
|
||||
func (sh stdHandlerR6) alg9(d *stdEncryptDict, ekey []byte, opass []byte) error {
|
||||
func (sh stdHandlerR6) alg9(d *StdEncryptDict, ekey []byte, opass []byte) error {
|
||||
// step a: compute O (owner password)
|
||||
var rbuf [16]byte
|
||||
if _, err := io.ReadFull(rand.Reader, rbuf[:]); err != nil {
|
||||
@ -312,7 +318,7 @@ func (sh stdHandlerR6) alg9(d *stdEncryptDict, ekey []byte, opass []byte) error
|
||||
|
||||
// alg10 computes the encryption dictionary's Perms (permissions) value (R=6).
|
||||
// 7.6.4.4.8 Algorithm 10 (page 87)
|
||||
func (sh stdHandlerR6) alg10(d *stdEncryptDict, ekey []byte) error {
|
||||
func (sh stdHandlerR6) alg10(d *StdEncryptDict, ekey []byte) error {
|
||||
// step a: extend permissions to 64 bits
|
||||
perms := uint64(uint32(d.P)) | (math.MaxUint32 << 32)
|
||||
|
||||
@ -352,7 +358,7 @@ func (sh stdHandlerR6) alg10(d *stdEncryptDict, ekey []byte) error {
|
||||
}
|
||||
|
||||
// alg11 authenticates the user password (R >= 5) and returns the hash.
|
||||
func (sh stdHandlerR6) alg11(d *stdEncryptDict, upass []byte) ([]byte, error) {
|
||||
func (sh stdHandlerR6) alg11(d *StdEncryptDict, upass []byte) ([]byte, error) {
|
||||
str := make([]byte, len(upass)+8)
|
||||
i := copy(str, upass)
|
||||
i += copy(str[i:], d.U[32:40]) // user Validation Salt
|
||||
@ -367,7 +373,7 @@ func (sh stdHandlerR6) alg11(d *stdEncryptDict, upass []byte) ([]byte, error) {
|
||||
|
||||
// alg12 authenticates the owner password (R >= 5) and returns the hash.
|
||||
// 7.6.4.4.10 Algorithm 12 (page 87)
|
||||
func (sh stdHandlerR6) alg12(d *stdEncryptDict, opass []byte) ([]byte, error) {
|
||||
func (sh stdHandlerR6) alg12(d *StdEncryptDict, opass []byte) ([]byte, error) {
|
||||
str := make([]byte, len(opass)+8+48)
|
||||
i := copy(str, opass)
|
||||
i += copy(str[i:], d.O[32:40]) // owner Validation Salt
|
||||
@ -383,7 +389,7 @@ func (sh stdHandlerR6) alg12(d *stdEncryptDict, opass []byte) ([]byte, error) {
|
||||
|
||||
// alg13 validates user permissions (P+EncryptMetadata vs Perms) for R=6.
|
||||
// 7.6.4.4.11 Algorithm 13 (page 87)
|
||||
func (sh stdHandlerR6) alg13(d *stdEncryptDict, fkey []byte) error {
|
||||
func (sh stdHandlerR6) alg13(d *StdEncryptDict, fkey []byte) error {
|
||||
perms := make([]byte, 16)
|
||||
copy(perms, d.Perms[:16])
|
||||
|
||||
@ -398,7 +404,7 @@ func (sh stdHandlerR6) alg13(d *stdEncryptDict, fkey []byte) error {
|
||||
if !bytes.Equal(perms[9:12], []byte("adb")) {
|
||||
return errors.New("decoded permissions are invalid")
|
||||
}
|
||||
p := AccessPermissions(binary.LittleEndian.Uint32(perms[0:4]))
|
||||
p := Permissions(binary.LittleEndian.Uint32(perms[0:4]))
|
||||
if p != d.P {
|
||||
return errors.New("permissions validation failed")
|
||||
}
|
||||
@ -419,7 +425,7 @@ func (sh stdHandlerR6) alg13(d *stdEncryptDict, fkey []byte) error {
|
||||
// generateR6 is the algorithm opposite to alg2a (R>=5).
|
||||
// It generates U,O,UE,OE,Perms fields using AESv3 encryption.
|
||||
// There is no algorithm number assigned to this function in the spec.
|
||||
func (sh stdHandlerR6) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([]byte, error) {
|
||||
func (sh stdHandlerR6) GenerateParams(d *StdEncryptDict, opass, upass []byte) ([]byte, error) {
|
||||
ekey := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, ekey); err != nil {
|
||||
return nil, err
|
||||
@ -455,6 +461,6 @@ func (sh stdHandlerR6) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([
|
||||
return ekey, nil
|
||||
}
|
||||
|
||||
func (sh stdHandlerR6) Authenticate(d *stdEncryptDict, pass []byte) ([]byte, AccessPermissions, error) {
|
||||
func (sh stdHandlerR6) Authenticate(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error) {
|
||||
return sh.alg2a(d, pass)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package core
|
||||
package security
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -60,7 +60,7 @@ func TestStdHandlerR6(t *testing.T) {
|
||||
c := c
|
||||
t.Run(c.Name, func(t *testing.T) {
|
||||
sh := stdHandlerR6{} // V=5
|
||||
d := &stdEncryptDict{
|
||||
d := &StdEncryptDict{
|
||||
R: R, P: perms,
|
||||
EncryptMetadata: c.EncMeta,
|
||||
}
|
@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
. "github.com/unidoc/unidoc/pdf/core"
|
||||
"github.com/unidoc/unidoc/pdf/core/security"
|
||||
)
|
||||
|
||||
// PdfReader represents a PDF file reader. It is a frontend to the lower level parsing mechanism and provides
|
||||
@ -110,7 +111,7 @@ func (this *PdfReader) Decrypt(password []byte) (bool, error) {
|
||||
// The bool flag indicates that the user can access and view the file.
|
||||
// The AccessPermissions shows what access the user has for editing etc.
|
||||
// An error is returned if there was a problem performing the authentication.
|
||||
func (this *PdfReader) CheckAccessRights(password []byte) (bool, AccessPermissions, error) {
|
||||
func (this *PdfReader) CheckAccessRights(password []byte) (bool, security.Permissions, error) {
|
||||
return this.parser.CheckAccessRights(password)
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/common/license"
|
||||
. "github.com/unidoc/unidoc/pdf/core"
|
||||
"github.com/unidoc/unidoc/pdf/core/security"
|
||||
"github.com/unidoc/unidoc/pdf/model/fonts"
|
||||
)
|
||||
|
||||
@ -648,7 +649,7 @@ func (this *PdfWriter) updateObjectNumbers() {
|
||||
|
||||
// EncryptOptions represents encryption options for an output PDF.
|
||||
type EncryptOptions struct {
|
||||
Permissions AccessPermissions
|
||||
Permissions security.Permissions
|
||||
Algorithm EncryptionAlgorithm
|
||||
}
|
||||
|
||||
@ -670,7 +671,7 @@ func (this *PdfWriter) Encrypt(userPass, ownerPass []byte, options *EncryptOptio
|
||||
if options != nil {
|
||||
algo = options.Algorithm
|
||||
}
|
||||
perm := PermOwner
|
||||
perm := security.PermOwner
|
||||
if options != nil {
|
||||
perm = options.Permissions
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user