From c7cfde7c1f45e43b999ae24b4210473e22a509c0 Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Mon, 17 Sep 2018 17:36:31 +0300 Subject: [PATCH] core: move test data, add docs and link to the specification --- pdf/core/crypt.go | 33 ++++++++++++------ pdf/core/crypt_file_test.go | 2 +- pdf/core/ecb.go | 7 ++++ .../core/testdata}/issue6010_1.pdf | 0 .../core/testdata}/issue6010_2.pdf | Bin .../AESv3 => pdf/core/testdata}/pr6531_1.pdf | Bin .../AESv3 => pdf/core/testdata}/pr6531_2.pdf | 0 .../core/testdata}/testcase_encry.pdf | Bin .../AESv3 => pdf/core/testdata}/x300.pdf | Bin 9 files changed, 31 insertions(+), 11 deletions(-) rename {testfiles/AESv3 => pdf/core/testdata}/issue6010_1.pdf (100%) rename {testfiles/AESv3 => pdf/core/testdata}/issue6010_2.pdf (100%) rename {testfiles/AESv3 => pdf/core/testdata}/pr6531_1.pdf (100%) rename {testfiles/AESv3 => pdf/core/testdata}/pr6531_2.pdf (100%) rename {testfiles/AESv3 => pdf/core/testdata}/testcase_encry.pdf (100%) rename {testfiles/AESv3 => pdf/core/testdata}/x300.pdf (100%) diff --git a/pdf/core/crypt.go b/pdf/core/crypt.go index f689772a..6ce1c576 100644 --- a/pdf/core/crypt.go +++ b/pdf/core/crypt.go @@ -50,6 +50,8 @@ type PdfCrypt struct { StringFilter string parser *PdfParser + + ivAESZero []byte // a zero buffer used as an initialization vector for AES } // AccessPermissions is a list of access permissions for a PDF file. @@ -81,11 +83,13 @@ type CryptFilter struct { Length int } +// Encryption filters names. +// Table 25, CFM (page 92) const ( - CryptFilterNone = "None" - CryptFilterV2 = "V2" - CryptFilterAESV2 = "AESV2" - CryptFilterAESV3 = "AESV3" + CryptFilterNone = "None" // do not decrypt data + CryptFilterV2 = "V2" // RC4-based filter + CryptFilterAESV2 = "AESV2" // AES-based filter (128 bit key, PDF 1.6) + CryptFilterAESV3 = "AESV3" // AES-based filter (256 bit key, PDF 2.0) ) // CryptFilters is a map of crypt filter name and underlying CryptFilter info. @@ -1113,10 +1117,9 @@ func (crypt *PdfCrypt) Encrypt(obj PdfObject, parentObjNum, parentGenNum int64) return nil } -var ivAESZero = make([]byte, aes.BlockSize) - // alg2a retrieves the encryption key from an encrypted document (R >= 5). // It returns false if the password was wrong. +// 7.6.4.3.2 Algorithm 2.A (page 83) func (crypt *PdfCrypt) alg2a(pass []byte) (bool, error) { // O & U: 32 byte hash + 8 byte Validation Salt + 8 byte Key Salt @@ -1181,7 +1184,10 @@ func (crypt *PdfCrypt) alg2a(pass []byte) (bool, error) { if err != nil { panic(err) } - iv := ivAESZero + if crypt.ivAESZero == nil { + crypt.ivAESZero = make([]byte, aes.BlockSize) + } + iv := crypt.ivAESZero cbc := cipher.NewCBCDecrypter(ac, iv) fkey := make([]byte, 32) cbc.CryptBlocks(fkey, ekey) @@ -1203,15 +1209,19 @@ func (crypt *PdfCrypt) alg2b(data, pwd, userKey []byte) []byte { return alg2b(data, pwd, userKey) } -// alg2b_R5 computes a hash for R=5. +// alg2b_R5 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 alg2b_R5(data []byte) []byte { h := sha256.New() h.Write(data) return h.Sum(nil) } -func repeat(buf []byte, sz int) { - bp := sz +// 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 @@ -1219,6 +1229,7 @@ func repeat(buf []byte, sz int) { } // alg2b computes a hash for R=6. +// 7.6.4.3.3 Algorithm 2.B (page 83) func alg2b(data, pwd, userKey []byte) []byte { var ( s256, s384, s512 hash.Hash @@ -1582,6 +1593,7 @@ func (crypt *PdfCrypt) alg11(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 (crypt *PdfCrypt) alg12(opass []byte) ([]byte, error) { str := make([]byte, len(opass)+8+48) i := copy(str, opass) @@ -1597,6 +1609,7 @@ func (crypt *PdfCrypt) alg12(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 (crypt *PdfCrypt) alg13(fkey []byte) (bool, error) { perms := crypt.Perms[:16] diff --git a/pdf/core/crypt_file_test.go b/pdf/core/crypt_file_test.go index c216e296..d75c248a 100644 --- a/pdf/core/crypt_file_test.go +++ b/pdf/core/crypt_file_test.go @@ -16,7 +16,7 @@ import ( pdf "github.com/unidoc/unidoc/pdf/model" ) -const aes3Dir = `../../testfiles/AESv3` +const aes3Dir = `./testdata` func TestDecryptAES3(t *testing.T) { cases := []struct { diff --git a/pdf/core/ecb.go b/pdf/core/ecb.go index bf76eb47..b96cdfa3 100644 --- a/pdf/core/ecb.go +++ b/pdf/core/ecb.go @@ -1,7 +1,14 @@ +/* + * 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 "crypto/cipher" +// ecb implements an Electronic Codebook encryption mode. +// This mode is used to compute or validate document permissions for R=6. type ecb struct { b cipher.Block blockSize int diff --git a/testfiles/AESv3/issue6010_1.pdf b/pdf/core/testdata/issue6010_1.pdf similarity index 100% rename from testfiles/AESv3/issue6010_1.pdf rename to pdf/core/testdata/issue6010_1.pdf diff --git a/testfiles/AESv3/issue6010_2.pdf b/pdf/core/testdata/issue6010_2.pdf similarity index 100% rename from testfiles/AESv3/issue6010_2.pdf rename to pdf/core/testdata/issue6010_2.pdf diff --git a/testfiles/AESv3/pr6531_1.pdf b/pdf/core/testdata/pr6531_1.pdf similarity index 100% rename from testfiles/AESv3/pr6531_1.pdf rename to pdf/core/testdata/pr6531_1.pdf diff --git a/testfiles/AESv3/pr6531_2.pdf b/pdf/core/testdata/pr6531_2.pdf similarity index 100% rename from testfiles/AESv3/pr6531_2.pdf rename to pdf/core/testdata/pr6531_2.pdf diff --git a/testfiles/AESv3/testcase_encry.pdf b/pdf/core/testdata/testcase_encry.pdf similarity index 100% rename from testfiles/AESv3/testcase_encry.pdf rename to pdf/core/testdata/testcase_encry.pdf diff --git a/testfiles/AESv3/x300.pdf b/pdf/core/testdata/x300.pdf similarity index 100% rename from testfiles/AESv3/x300.pdf rename to pdf/core/testdata/x300.pdf