core: split security handlers into a separate package

This commit is contained in:
Denys Smirnov 2018-10-04 04:33:32 +03:00
parent 006e5f9e6e
commit 42df346e69
13 changed files with 225 additions and 215 deletions

View File

@ -10,10 +10,10 @@ import (
"crypto/rand" "crypto/rand"
"errors" "errors"
"fmt" "fmt"
"math"
"time" "time"
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/core/security"
) )
type Version struct { type Version struct {
@ -28,11 +28,11 @@ 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 AccessPermissions) (*PdfCrypt, *EncryptInfo, error) { func PdfCryptNewEncrypt(cf CryptFilter, 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),
encryptStd: stdEncryptDict{ encryptStd: security.StdEncryptDict{
P: perm, P: perm,
EncryptMetadata: true, EncryptMetadata: true,
}, },
@ -83,7 +83,7 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessP
return nil, nil, err return nil, nil, err
} }
// encode parameters generated by the Standard security handler // encode parameters generated by the Standard security handler
crypter.encryptStd.EncodeTo(ed) encodeEncryptStd(&crypter.encryptStd, ed)
if crypter.encrypt.V >= 4 { if crypter.encrypt.V >= 4 {
if err := crypter.saveCryptFilters(ed); err != nil { if err := crypter.saveCryptFilters(ed); err != nil {
return nil, nil, err return nil, nil, err
@ -97,12 +97,115 @@ func PdfCryptNewEncrypt(cf CryptFilter, userPass, ownerPass []byte, perm AccessP
}, nil }, 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. // PdfCrypt provides PDF encryption/decryption support.
// The PDF standard supports encryption of strings and streams (Section 7.6). // The PDF standard supports encryption of strings and streams (Section 7.6).
// TODO (v3): Consider unexporting. // TODO (v3): Consider unexporting.
type PdfCrypt struct { type PdfCrypt struct {
encrypt encryptDict encrypt encryptDict
encryptStd stdEncryptDict encryptStd security.StdEncryptDict
id0 string id0 string
encryptionKey []byte encryptionKey []byte
@ -117,7 +220,6 @@ type PdfCrypt struct {
parser *PdfParser parser *PdfParser
decryptedObjNum map[int]struct{} decryptedObjNum map[int]struct{}
ivAESZero []byte // a zero buffer used as an initialization vector for AES
} }
func (crypt *PdfCrypt) newEncyptDict() *PdfObjectDictionary { 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. 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. // StandardCryptFilter is a default name for a standard crypt filter.
const StandardCryptFilter = "StdCF" const StandardCryptFilter = "StdCF"
@ -405,7 +478,7 @@ func PdfCryptNewDecrypt(parser *PdfParser, ed, trailer *PdfObjectDictionary) (*P
} }
// decode Standard security handler parameters // decode Standard security handler parameters
if err := crypter.encryptStd.DecodeFrom(ed); err != nil { if err := decodeEncryptStd(&crypter.encryptStd, ed); err != nil {
return crypter, err 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. // 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 return crypt.encryptStd.P
} }
func (crypt *PdfCrypt) securityHandler() stdSecurityHandler { func (crypt *PdfCrypt) securityHandler() security.StdHandler {
if crypt.encryptStd.R >= 5 { if crypt.encryptStd.R >= 5 {
return stdHandlerR6{} return security.NewHandlerR6()
}
return stdHandlerR4{
ID0: crypt.id0,
Length: crypt.encrypt.Length,
} }
return security.NewHandlerR4(crypt.id0, crypt.encrypt.Length)
} }
// Check whether the specified password can be used to decrypt the document. // 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 bool flag indicates that the user can access and can view the file.
// The AccessPermissions shows what access the user has for editing etc. // The AccessPermissions shows what access the user has for editing etc.
// An error is returned if there was a problem performing the authentication. // 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() h := crypt.securityHandler()
// TODO(dennwc): it computes an encryption key as well; if necessary, define a new interface method to optimize this // 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) fkey, perm, err := h.Authenticate(&crypt.encryptStd, password)

View File

@ -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
}

View File

@ -11,6 +11,7 @@ import (
"testing" "testing"
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/core/security"
) )
func init() { func init() {
@ -24,7 +25,7 @@ func TestDecryption1(t *testing.T) {
V: 2, V: 2,
Length: 128, Length: 128,
}, },
encryptStd: stdEncryptDict{ encryptStd: security.StdEncryptDict{
R: 3, R: 3,
P: 0xfffff0c0, P: 0xfffff0c0,
EncryptMetadata: true, EncryptMetadata: true,

View File

@ -18,6 +18,7 @@ import (
"strings" "strings"
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/core/security"
) )
// Regular Expressions for parsing and identifying object signatures. // 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 bool flag indicates that the user can access and view the file.
// The AccessPermissions shows what access the user has for editing etc. // The AccessPermissions shows what access the user has for editing etc.
// An error is returned if there was a problem performing the authentication. // 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. // Also build the encryption/decryption key.
if parser.crypter == nil { if parser.crypter == nil {
// If the crypter is not set, the file is not encrypted and we can assume full access permissions. // 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) return parser.crypter.checkAccessRights(password)
} }

View File

@ -3,7 +3,7 @@
* file 'LICENSE.md', which is part of this source code package. * file 'LICENSE.md', which is part of this source code package.
*/ */
package core package security
import "crypto/cipher" import "crypto/cipher"

View 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
}

View 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
}

View File

@ -3,7 +3,7 @@
* file 'LICENSE.md', which is part of this source code package. * file 'LICENSE.md', which is part of this source code package.
*/ */
package core package security
import ( import (
"bytes" "bytes"
@ -15,7 +15,16 @@ import (
"github.com/unidoc/unidoc/common" "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 { type stdHandlerR4 struct {
Length int Length int
@ -40,7 +49,7 @@ func (sh stdHandlerR4) paddedPass(pass []byte) []byte {
} }
// alg2 computes an encryption key. // 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") common.Log.Trace("alg2")
key := sh.paddedPass(pass) 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. // alg6 authenticates the user password and returns the document encryption key.
// It returns an nil key in case authentication failed. // 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 ( var (
uo []byte uo []byte
err error 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. // alg7 authenticates the owner password and returns the document encryption key.
//// It returns an nil key in case authentication failed. //// 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) encKey := sh.alg3Key(d.R, opass)
decrypted := make([]byte, len(d.O)) decrypted := make([]byte, len(d.O))
@ -289,7 +298,7 @@ func (sh stdHandlerR4) alg7(d *stdEncryptDict, opass []byte) ([]byte, error) {
return ekey, nil 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. // Make the O and U objects.
O, err := sh.alg3(d.R, upass, opass) O, err := sh.alg3(d.R, upass, opass)
if err != nil { if err != nil {
@ -311,7 +320,7 @@ func (sh stdHandlerR4) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([
return ekey, nil 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. // Try owner password.
// May not be necessary if only want to get all contents. // May not be necessary if only want to get all contents.
// (user pass needs to be known or empty). // (user pass needs to be known or empty).

View File

@ -3,7 +3,7 @@
* file 'LICENSE.md', which is part of this source code package. * file 'LICENSE.md', which is part of this source code package.
*/ */
package core package security
import ( import (
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
@ -53,7 +53,7 @@ func TestAlg2(t *testing.T) {
0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}), 0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}),
Length: 128, Length: 128,
} }
d := &stdEncryptDict{ d := &StdEncryptDict{
R: 3, R: 3,
P: 0xfffff0c0, P: 0xfffff0c0,
EncryptMetadata: true, EncryptMetadata: true,
@ -110,7 +110,7 @@ func TestAlg5(t *testing.T) {
0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}), 0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}),
Length: 128, Length: 128,
} }
d := &stdEncryptDict{ d := &StdEncryptDict{
R: 3, R: 3,
P: 0xfffff0c0, P: 0xfffff0c0,
EncryptMetadata: true, EncryptMetadata: true,

View File

@ -3,7 +3,7 @@
* file 'LICENSE.md', which is part of this source code package. * file 'LICENSE.md', which is part of this source code package.
*/ */
package core package security
import ( import (
"bytes" "bytes"
@ -19,13 +19,19 @@ import (
"math" "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{} type stdHandlerR6 struct{}
// alg2a retrieves the encryption key from an encrypted document (R >= 5). // alg2a retrieves the encryption key from an encrypted document (R >= 5).
// 7.6.4.3.2 Algorithm 2.A (page 83) // 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 // O & U: 32 byte hash + 8 byte Validation Salt + 8 byte Key Salt
// step a: Unicode normalization // step a: Unicode normalization
@ -46,7 +52,7 @@ func (sh stdHandlerR6) alg2a(d *stdEncryptDict, pass []byte) ([]byte, AccessPerm
ekey []byte // encrypted file key ekey []byte // encrypted file key
ukey []byte // user key; set only when using owner's password ukey []byte // user key; set only when using owner's password
) )
var perm AccessPermissions var perm Permissions
if len(h) != 0 { if len(h) != 0 {
// owner password valid // owner password valid
perm = PermOwner 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). // 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) // 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) // step a: compute U (user password)
var rbuf [16]byte var rbuf [16]byte
if _, err := io.ReadFull(rand.Reader, rbuf[:]); err != nil { 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). // 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) // 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) // step a: compute O (owner password)
var rbuf [16]byte var rbuf [16]byte
if _, err := io.ReadFull(rand.Reader, rbuf[:]); err != nil { 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). // alg10 computes the encryption dictionary's Perms (permissions) value (R=6).
// 7.6.4.4.8 Algorithm 10 (page 87) // 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 // step a: extend permissions to 64 bits
perms := uint64(uint32(d.P)) | (math.MaxUint32 << 32) 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. // 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) str := make([]byte, len(upass)+8)
i := copy(str, upass) i := copy(str, upass)
i += copy(str[i:], d.U[32:40]) // user Validation Salt 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. // alg12 authenticates the owner password (R >= 5) and returns the hash.
// 7.6.4.4.10 Algorithm 12 (page 87) // 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) str := make([]byte, len(opass)+8+48)
i := copy(str, opass) i := copy(str, opass)
i += copy(str[i:], d.O[32:40]) // owner Validation Salt 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. // alg13 validates user permissions (P+EncryptMetadata vs Perms) for R=6.
// 7.6.4.4.11 Algorithm 13 (page 87) // 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) perms := make([]byte, 16)
copy(perms, d.Perms[: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")) { if !bytes.Equal(perms[9:12], []byte("adb")) {
return errors.New("decoded permissions are invalid") 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 { if p != d.P {
return errors.New("permissions validation failed") 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). // generateR6 is the algorithm opposite to alg2a (R>=5).
// It generates U,O,UE,OE,Perms fields using AESv3 encryption. // It generates U,O,UE,OE,Perms fields using AESv3 encryption.
// There is no algorithm number assigned to this function in the spec. // 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) ekey := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, ekey); err != nil { if _, err := io.ReadFull(rand.Reader, ekey); err != nil {
return nil, err return nil, err
@ -455,6 +461,6 @@ func (sh stdHandlerR6) GenerateParams(d *stdEncryptDict, opass, upass []byte) ([
return ekey, nil 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) return sh.alg2a(d, pass)
} }

View File

@ -1,4 +1,4 @@
package core package security
import ( import (
"bytes" "bytes"
@ -60,7 +60,7 @@ func TestStdHandlerR6(t *testing.T) {
c := c c := c
t.Run(c.Name, func(t *testing.T) { t.Run(c.Name, func(t *testing.T) {
sh := stdHandlerR6{} // V=5 sh := stdHandlerR6{} // V=5
d := &stdEncryptDict{ d := &StdEncryptDict{
R: R, P: perms, R: R, P: perms,
EncryptMetadata: c.EncMeta, EncryptMetadata: c.EncMeta,
} }

View File

@ -13,6 +13,7 @@ import (
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
. "github.com/unidoc/unidoc/pdf/core" . "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 // 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 bool flag indicates that the user can access and view the file.
// The AccessPermissions shows what access the user has for editing etc. // The AccessPermissions shows what access the user has for editing etc.
// An error is returned if there was a problem performing the authentication. // 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) return this.parser.CheckAccessRights(password)
} }

View File

@ -20,6 +20,7 @@ import (
"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"
"github.com/unidoc/unidoc/pdf/core/security"
"github.com/unidoc/unidoc/pdf/model/fonts" "github.com/unidoc/unidoc/pdf/model/fonts"
) )
@ -648,7 +649,7 @@ func (this *PdfWriter) updateObjectNumbers() {
// EncryptOptions represents encryption options for an output PDF. // EncryptOptions represents encryption options for an output PDF.
type EncryptOptions struct { type EncryptOptions struct {
Permissions AccessPermissions Permissions security.Permissions
Algorithm EncryptionAlgorithm Algorithm EncryptionAlgorithm
} }
@ -670,7 +671,7 @@ func (this *PdfWriter) Encrypt(userPass, ownerPass []byte, options *EncryptOptio
if options != nil { if options != nil {
algo = options.Algorithm algo = options.Algorithm
} }
perm := PermOwner perm := security.PermOwner
if options != nil { if options != nil {
perm = options.Permissions perm = options.Permissions
} }