mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-27 13:48:51 +08:00

* Remove panic on font nil Differences array * Remove unused bcmaps function * Remove panics from the core/security/crypt package * Fix extractor invalid Do operand crash * Fix TTF parser crash for invalid hhea number of hMetrics * Remove ECB crypt panics * Remove standard_r6 panics * Remove panic from render package
548 lines
13 KiB
Go
548 lines
13 KiB
Go
/*
|
|
* This file is subject to the terms and conditions defined in
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
*/
|
|
|
|
package security
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"encoding/binary"
|
|
"errors"
|
|
"hash"
|
|
"io"
|
|
"math"
|
|
|
|
"github.com/unidoc/unipdf/v3/common"
|
|
)
|
|
|
|
var _ StdHandler = stdHandlerR6{}
|
|
|
|
// newAESCipher creates a new AES block cipher.
|
|
// The size of a buffer should be exactly 16, 24 or 32 bytes.
|
|
func newAESCipher(b []byte) (cipher.Block, error) {
|
|
c, err := aes.NewCipher(b)
|
|
if err != nil {
|
|
common.Log.Error("ERROR: could not create AES cipher: %v", err)
|
|
return nil, err
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
// 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.
|
|
// Both revisions are expected to be used with AES encryption filters.
|
|
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, Permissions, error) {
|
|
// O & U: 32 byte hash + 8 byte Validation Salt + 8 byte Key Salt
|
|
if err := checkAtLeast("alg2a", "O", 48, d.O); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if err := checkAtLeast("alg2a", "U", 48, d.U); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// step a: Unicode normalization
|
|
// TODO(dennwc): make sure that UTF-8 strings are normalized
|
|
|
|
// step b: truncate to 127 bytes
|
|
if len(pass) > 127 {
|
|
pass = pass[:127]
|
|
}
|
|
|
|
// step c: test pass against the owner key
|
|
h, err := sh.alg12(d, pass)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var (
|
|
data []byte // data to hash
|
|
ekey []byte // encrypted file key
|
|
ukey []byte // user key; set only when using owner's password
|
|
)
|
|
var perm Permissions
|
|
if len(h) != 0 {
|
|
// owner password valid
|
|
perm = PermOwner
|
|
|
|
// step d: compute an intermediate owner key
|
|
str := make([]byte, len(pass)+8+48)
|
|
i := copy(str, pass)
|
|
i += copy(str[i:], d.O[40:48]) // owner Key Salt
|
|
i += copy(str[i:], d.U[0:48])
|
|
|
|
data = str
|
|
ekey = d.OE
|
|
ukey = d.U[0:48]
|
|
} else {
|
|
// check user password
|
|
h, err = sh.alg11(d, pass)
|
|
if err == nil && len(h) == 0 {
|
|
// try default password
|
|
h, err = sh.alg11(d, []byte(""))
|
|
}
|
|
if err != nil {
|
|
return nil, 0, err
|
|
} else if len(h) == 0 {
|
|
// wrong password
|
|
return nil, 0, nil
|
|
}
|
|
perm = d.P
|
|
// step e: compute an intermediate user key
|
|
str := make([]byte, len(pass)+8)
|
|
i := copy(str, pass)
|
|
i += copy(str[i:], d.U[40:48]) // user Key Salt
|
|
|
|
data = str
|
|
ekey = d.UE
|
|
ukey = nil
|
|
}
|
|
if err := checkAtLeast("alg2a", "Key", 32, ekey); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
ekey = ekey[:32]
|
|
|
|
// intermediate key
|
|
ikey, err := sh.alg2b(d.R, data, pass, ukey)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
ac, err := aes.NewCipher(ikey[:32])
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
iv := make([]byte, aes.BlockSize)
|
|
cbc := cipher.NewCBCDecrypter(ac, iv)
|
|
fkey := make([]byte, 32)
|
|
cbc.CryptBlocks(fkey, ekey)
|
|
|
|
if d.R == 5 {
|
|
return fkey, perm, nil
|
|
}
|
|
// validate permissions
|
|
err = sh.alg13(d, fkey)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return fkey, perm, nil
|
|
}
|
|
|
|
// alg2bR5 computes a hash for R=5, used in a deprecated extension.
|
|
// It's used the same way as a hash described in Algorithm 2.B, but it doesn't use the original password
|
|
// and the user key to calculate the hash.
|
|
func alg2bR5(data []byte) ([]byte, error) {
|
|
h := sha256.New()
|
|
h.Write(data)
|
|
return h.Sum(nil), nil
|
|
}
|
|
|
|
// repeat repeats first n bytes of buf until the end of the buffer.
|
|
// It assumes that the length of buf is a multiple of n.
|
|
func repeat(buf []byte, n int) {
|
|
bp := n
|
|
for bp < len(buf) {
|
|
copy(buf[bp:], buf[:bp])
|
|
bp *= 2
|
|
}
|
|
}
|
|
|
|
// alg2b computes a hash for R=6.
|
|
// 7.6.4.3.3 Algorithm 2.B (page 83)
|
|
func alg2b(data, pwd, userKey []byte) ([]byte, error) {
|
|
var (
|
|
s256, s384, s512 hash.Hash
|
|
)
|
|
s256 = sha256.New()
|
|
hbuf := make([]byte, 64)
|
|
|
|
h := s256
|
|
h.Write(data)
|
|
K := h.Sum(hbuf[:0])
|
|
|
|
buf := make([]byte, 64*(127+64+48))
|
|
|
|
round := func(rnd int) ([]byte, error) {
|
|
// step a: repeat pass+K 64 times
|
|
n := len(pwd) + len(K) + len(userKey)
|
|
part := buf[:n]
|
|
i := copy(part, pwd)
|
|
i += copy(part[i:], K[:])
|
|
i += copy(part[i:], userKey)
|
|
if i != n {
|
|
common.Log.Error("ERROR: unexpected round input size.")
|
|
return nil, errors.New("wrong size")
|
|
}
|
|
K1 := buf[:n*64]
|
|
repeat(K1, n)
|
|
|
|
// step b: encrypt K1 with AES-128 CBC
|
|
ac, err := newAESCipher(K[0:16])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cbc := cipher.NewCBCEncrypter(ac, K[16:32])
|
|
cbc.CryptBlocks(K1, K1)
|
|
E := K1
|
|
|
|
// step c: use 16 bytes of E as big-endian int, select the next hash
|
|
b := 0
|
|
for i := 0; i < 16; i++ {
|
|
b += int(E[i] % 3)
|
|
}
|
|
var h hash.Hash
|
|
switch b % 3 {
|
|
case 0:
|
|
h = s256
|
|
case 1:
|
|
if s384 == nil {
|
|
s384 = sha512.New384()
|
|
}
|
|
h = s384
|
|
case 2:
|
|
if s512 == nil {
|
|
s512 = sha512.New()
|
|
}
|
|
h = s512
|
|
}
|
|
|
|
// step d: take the hash of E, use as a new K
|
|
h.Reset()
|
|
h.Write(E)
|
|
K = h.Sum(hbuf[:0])
|
|
|
|
return E, nil
|
|
}
|
|
|
|
for i := 0; ; {
|
|
E, err := round(i)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b := uint8(E[len(E)-1])
|
|
// from the spec, it appears that i should be incremented after
|
|
// the test, but that doesn't match what Adobe does
|
|
i++
|
|
if i >= 64 && b <= uint8(i-32) {
|
|
break
|
|
}
|
|
}
|
|
return K[:32], nil
|
|
}
|
|
|
|
// alg2b computes a hash for R=5 and R=6.
|
|
func (sh stdHandlerR6) alg2b(R int, data, pwd, userKey []byte) ([]byte, error) {
|
|
if R == 5 {
|
|
return alg2bR5(data)
|
|
}
|
|
return alg2b(data, pwd, userKey)
|
|
}
|
|
|
|
// 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 {
|
|
if err := checkAtLeast("alg8", "Key", 32, ekey); err != nil {
|
|
return err
|
|
}
|
|
// step a: compute U (user password)
|
|
var rbuf [16]byte
|
|
if _, err := io.ReadFull(rand.Reader, rbuf[:]); err != nil {
|
|
return err
|
|
}
|
|
valSalt := rbuf[0:8]
|
|
keySalt := rbuf[8:16]
|
|
|
|
str := make([]byte, len(upass)+len(valSalt))
|
|
i := copy(str, upass)
|
|
i += copy(str[i:], valSalt)
|
|
|
|
h, err := sh.alg2b(d.R, str, upass, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
U := make([]byte, len(h)+len(valSalt)+len(keySalt))
|
|
i = copy(U, h[:32])
|
|
i += copy(U[i:], valSalt)
|
|
i += copy(U[i:], keySalt)
|
|
|
|
d.U = U
|
|
|
|
// step b: compute UE (user encryption)
|
|
|
|
// str still contains a password, reuse it
|
|
i = len(upass)
|
|
i += copy(str[i:], keySalt)
|
|
|
|
h, err = sh.alg2b(d.R, str, upass, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ac, err := newAESCipher(h[:32])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iv := make([]byte, aes.BlockSize)
|
|
cbc := cipher.NewCBCEncrypter(ac, iv)
|
|
UE := make([]byte, 32)
|
|
cbc.CryptBlocks(UE, ekey[:32])
|
|
d.UE = UE
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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 {
|
|
if err := checkAtLeast("alg9", "Key", 32, ekey); err != nil {
|
|
return err
|
|
}
|
|
if err := checkAtLeast("alg9", "U", 48, d.U); err != nil {
|
|
return err
|
|
}
|
|
// step a: compute O (owner password)
|
|
var rbuf [16]byte
|
|
if _, err := io.ReadFull(rand.Reader, rbuf[:]); err != nil {
|
|
return err
|
|
}
|
|
valSalt := rbuf[0:8]
|
|
keySalt := rbuf[8:16]
|
|
userKey := d.U[:48]
|
|
|
|
str := make([]byte, len(opass)+len(valSalt)+len(userKey))
|
|
i := copy(str, opass)
|
|
i += copy(str[i:], valSalt)
|
|
i += copy(str[i:], userKey)
|
|
|
|
h, err := sh.alg2b(d.R, str, opass, userKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
O := make([]byte, len(h)+len(valSalt)+len(keySalt))
|
|
i = copy(O, h[:32])
|
|
i += copy(O[i:], valSalt)
|
|
i += copy(O[i:], keySalt)
|
|
|
|
d.O = O
|
|
|
|
// step b: compute OE (owner encryption)
|
|
|
|
// str still contains a password and a user key - reuse both, but overwrite the salt
|
|
i = len(opass)
|
|
i += copy(str[i:], keySalt)
|
|
// i += len(userKey)
|
|
|
|
h, err = sh.alg2b(d.R, str, opass, userKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ac, err := newAESCipher(h[:32])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iv := make([]byte, aes.BlockSize)
|
|
cbc := cipher.NewCBCEncrypter(ac, iv)
|
|
OE := make([]byte, 32)
|
|
cbc.CryptBlocks(OE, ekey[:32])
|
|
d.OE = OE
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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 {
|
|
if err := checkAtLeast("alg10", "Key", 32, ekey); err != nil {
|
|
return err
|
|
}
|
|
// step a: extend permissions to 64 bits
|
|
perms := uint64(uint32(d.P)) | (math.MaxUint32 << 32)
|
|
|
|
// step b: record permissions
|
|
Perms := make([]byte, 16)
|
|
binary.LittleEndian.PutUint64(Perms[:8], perms)
|
|
|
|
// step c: record EncryptMetadata
|
|
if d.EncryptMetadata {
|
|
Perms[8] = 'T'
|
|
} else {
|
|
Perms[8] = 'F'
|
|
}
|
|
|
|
// step d: write "adb" magic
|
|
copy(Perms[9:12], "adb")
|
|
|
|
// step e: write 4 bytes of random data
|
|
|
|
// spec doesn't specify them as generated "from a strong random source",
|
|
// but we will use the cryptographic random generator anyway
|
|
if _, err := io.ReadFull(rand.Reader, Perms[12:16]); err != nil {
|
|
return err
|
|
}
|
|
|
|
// step f: encrypt permissions
|
|
ac, err := newAESCipher(ekey[:32])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ecb := newECBEncrypter(ac)
|
|
ecb.CryptBlocks(Perms, Perms)
|
|
|
|
d.Perms = Perms[:16]
|
|
return nil
|
|
}
|
|
|
|
// alg11 authenticates the user password (R >= 5) and returns the hash.
|
|
func (sh stdHandlerR6) alg11(d *StdEncryptDict, upass []byte) ([]byte, error) {
|
|
if err := checkAtLeast("alg11", "U", 48, d.U); err != nil {
|
|
return nil, err
|
|
}
|
|
str := make([]byte, len(upass)+8)
|
|
i := copy(str, upass)
|
|
i += copy(str[i:], d.U[32:40]) // user Validation Salt
|
|
|
|
h, err := sh.alg2b(d.R, str, upass, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h = h[:32]
|
|
if !bytes.Equal(h, d.U[:32]) {
|
|
return nil, nil
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
// 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) {
|
|
if err := checkAtLeast("alg12", "U", 48, d.U); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := checkAtLeast("alg12", "O", 48, d.O); err != nil {
|
|
return nil, err
|
|
}
|
|
str := make([]byte, len(opass)+8+48)
|
|
i := copy(str, opass)
|
|
i += copy(str[i:], d.O[32:40]) // owner Validation Salt
|
|
i += copy(str[i:], d.U[0:48])
|
|
|
|
h, err := sh.alg2b(d.R, str, opass, d.U[0:48])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h = h[:32]
|
|
if !bytes.Equal(h, d.O[:32]) {
|
|
return nil, nil
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
// 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 {
|
|
if err := checkAtLeast("alg13", "Key", 32, fkey); err != nil {
|
|
return err
|
|
}
|
|
if err := checkAtLeast("alg13", "Perms", 16, d.Perms); err != nil {
|
|
return err
|
|
}
|
|
perms := make([]byte, 16)
|
|
copy(perms, d.Perms[:16])
|
|
|
|
ac, err := aes.NewCipher(fkey[:32])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ecb := newECBDecrypter(ac)
|
|
ecb.CryptBlocks(perms, perms)
|
|
|
|
if !bytes.Equal(perms[9:12], []byte("adb")) {
|
|
return errors.New("decoded permissions are invalid")
|
|
}
|
|
p := Permissions(binary.LittleEndian.Uint32(perms[0:4]))
|
|
if p != d.P {
|
|
return errors.New("permissions validation failed")
|
|
}
|
|
encMeta := true
|
|
if perms[8] == 'T' {
|
|
encMeta = true
|
|
} else if perms[8] == 'F' {
|
|
encMeta = false
|
|
} else {
|
|
return errors.New("decoded metadata encryption flag is invalid")
|
|
}
|
|
if encMeta != d.EncryptMetadata {
|
|
return errors.New("metadata encryption validation failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GenerateParams 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.
|
|
// It expects R, P and EncryptMetadata fields to be set.
|
|
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
|
|
}
|
|
// all these field will be populated by functions below
|
|
d.U = nil
|
|
d.O = nil
|
|
d.UE = nil
|
|
d.OE = nil
|
|
d.Perms = nil // populated only for R=6
|
|
|
|
if len(upass) > 127 {
|
|
upass = upass[:127]
|
|
}
|
|
if len(opass) > 127 {
|
|
opass = opass[:127]
|
|
}
|
|
// generate U and UE
|
|
if err := sh.alg8(d, ekey, upass); err != nil {
|
|
return nil, err
|
|
}
|
|
// generate O and OE
|
|
if err := sh.alg9(d, ekey, opass); err != nil {
|
|
return nil, err
|
|
}
|
|
if d.R == 5 {
|
|
return ekey, nil
|
|
}
|
|
// generate Perms
|
|
if err := sh.alg10(d, ekey); err != nil {
|
|
return nil, err
|
|
}
|
|
return ekey, nil
|
|
}
|
|
|
|
// Authenticate implements StdHandler interface.
|
|
func (sh stdHandlerR6) Authenticate(d *StdEncryptDict, pass []byte) ([]byte, Permissions, error) {
|
|
return sh.alg2a(d, pass)
|
|
}
|