github.com/unidoc/unidoc@v2.2.0+incompatible/pdf/core/crypt_test.go (about) 1 /* 2 * This file is subject to the terms and conditions defined in 3 * file 'LICENSE.md', which is part of this source code package. 4 */ 5 6 // Test the PDF crypt support. 7 8 package core 9 10 import ( 11 "bytes" 12 "fmt" 13 "math" 14 "math/rand" 15 "strings" 16 "testing" 17 "time" 18 19 "github.com/unidoc/unidoc/common" 20 ) 21 22 func init() { 23 common.SetLogger(common.ConsoleLogger{}) 24 } 25 26 func TestPadding(t *testing.T) { 27 crypter := PdfCrypt{} 28 29 // Case 1 empty pass, should match padded string. 30 key := crypter.paddedPass([]byte("")) 31 if len(key) != 32 { 32 t.Errorf("Fail, expected padded pass length = 32 (%d)", len(key)) 33 } 34 if key[0] != 0x28 { 35 t.Errorf("key[0] != 0x28 (%q in %q)", key[0], key) 36 } 37 if key[31] != 0x7A { 38 t.Errorf("key[31] != 0x7A (%q in %q)", key[31], key) 39 } 40 41 // Case 2, non empty pass. 42 key = crypter.paddedPass([]byte("bla")) 43 if len(key) != 32 { 44 t.Errorf("Fail, expected padded pass length = 32 (%d)", len(key)) 45 } 46 if string(key[0:3]) != "bla" { 47 t.Errorf("Expecting start with bla (%s)", key) 48 } 49 if key[3] != 0x28 { 50 t.Errorf("key[3] != 0x28 (%q in %q)", key[3], key) 51 } 52 if key[31] != 0x64 { 53 t.Errorf("key[31] != 0x64 (%q in %q)", key[31], key) 54 } 55 } 56 57 // Test algorithm 2. 58 func TestAlg2(t *testing.T) { 59 crypter := PdfCrypt{} 60 crypter.V = 2 61 crypter.R = 3 62 crypter.P = -3904 63 crypter.Id0 = string([]byte{0x4e, 0x00, 0x99, 0xe5, 0x36, 0x78, 0x93, 0x24, 64 0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}) 65 crypter.O = []byte{0xE6, 0x00, 0xEC, 0xC2, 0x02, 0x88, 0xAD, 0x8B, 66 0x5C, 0x72, 0x64, 0xA9, 0x5C, 0x29, 0xC6, 0xA8, 0x3E, 0xE2, 0x51, 67 0x76, 0x79, 0xAA, 0x02, 0x18, 0xBE, 0xCE, 0xEA, 0x8B, 0x79, 0x86, 68 0x72, 0x6A, 0x8C, 0xDB} 69 crypter.Length = 128 70 crypter.EncryptMetadata = true 71 72 key := crypter.Alg2([]byte("")) 73 74 keyExp := []byte{0xf8, 0x94, 0x9c, 0x5a, 0xf5, 0xa0, 0xc0, 0xca, 75 0x30, 0xb8, 0x91, 0xc1, 0xbb, 0x2c, 0x4f, 0xf5} 76 77 if string(key) != string(keyExp) { 78 common.Log.Debug(" Key (%d): % x", len(key), key) 79 common.Log.Debug("KeyExp (%d): % x", len(keyExp), keyExp) 80 t.Errorf("alg2 -> key != expected\n") 81 } 82 83 } 84 85 // Test algorithm 3. 86 func TestAlg3(t *testing.T) { 87 crypter := PdfCrypt{} 88 crypter.V = 2 89 crypter.R = 3 90 crypter.P = -3904 91 crypter.Id0 = string([]byte{0x4e, 0x00, 0x99, 0xe5, 0x36, 0x78, 0x93, 0x24, 92 0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}) 93 Oexp := []byte{0xE6, 0x00, 0xEC, 0xC2, 0x02, 0x88, 0xAD, 0x8B, 94 0x0d, 0x64, 0xA9, 0x29, 0xC6, 0xA8, 0x3E, 0xE2, 0x51, 95 0x76, 0x79, 0xAA, 0x02, 0x18, 0xBE, 0xCE, 0xEA, 0x8B, 0x79, 0x86, 96 0x72, 0x6A, 0x8C, 0xDB} 97 crypter.Length = 128 98 crypter.EncryptMetadata = true 99 100 O, err := crypter.Alg3([]byte(""), []byte("test")) 101 if err != nil { 102 t.Errorf("crypt alg3 error %s", err) 103 return 104 } 105 106 if string(O) != string(Oexp) { 107 common.Log.Debug(" O (%d): % x", len(O), O) 108 common.Log.Debug("Oexp (%d): % x", len(Oexp), Oexp) 109 t.Errorf("alg3 -> key != expected") 110 } 111 } 112 113 // Test algorithm 5 for computing dictionary's U (user password) value 114 // valid for R >= 3. 115 func TestAlg5(t *testing.T) { 116 crypter := PdfCrypt{} 117 crypter.V = 2 118 crypter.R = 3 119 crypter.P = -3904 120 crypter.Id0 = string([]byte{0x4e, 0x00, 0x99, 0xe5, 0x36, 0x78, 0x93, 0x24, 121 0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4}) 122 crypter.O = []byte{0xE6, 0x00, 0xEC, 0xC2, 0x02, 0x88, 0xAD, 0x8B, 123 0x5C, 0x72, 0x64, 0xA9, 0x5C, 0x29, 0xC6, 0xA8, 0x3E, 0xE2, 0x51, 124 0x76, 0x79, 0xAA, 0x02, 0x18, 0xBE, 0xCE, 0xEA, 0x8B, 0x79, 0x86, 125 0x72, 0x6A, 0x8C, 0xDB} 126 crypter.Length = 128 127 crypter.EncryptMetadata = true 128 129 U, _, err := crypter.Alg5([]byte("")) 130 if err != nil { 131 t.Errorf("Error %s", err) 132 return 133 } 134 135 Uexp := []byte{0x59, 0x66, 0x38, 0x6c, 0x76, 0xfe, 0x95, 0x7d, 0x3d, 136 0x0d, 0x14, 0x3d, 0x36, 0xfd, 0x01, 0x3d, 0x00, 0x00, 0x00, 0x00, 137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 138 139 if string(U[0:16]) != string(Uexp[0:16]) { 140 common.Log.Info(" U (%d): % x", len(U), U) 141 common.Log.Info("Uexp (%d): % x", len(Uexp), Uexp) 142 t.Errorf("U != expected\n") 143 } 144 } 145 146 // Test decrypting. Example with V=2, R=3, using standard algorithm. 147 func TestDecryption1(t *testing.T) { 148 crypter := PdfCrypt{} 149 crypter.DecryptedObjects = map[PdfObject]bool{} 150 // Default algorithm is V2 (RC4). 151 crypter.CryptFilters = newCryptFiltersV2(crypter.Length) 152 crypter.V = 2 153 crypter.R = 3 154 crypter.P = -3904 155 crypter.Id0 = string([]byte{0x5f, 0x91, 0xff, 0xf2, 0x00, 0x88, 0x13, 156 0x5f, 0x30, 0x24, 0xd1, 0x0f, 0x28, 0x31, 0xc6, 0xfa}) 157 crypter.O = []byte{0xE6, 0x00, 0xEC, 0xC2, 0x02, 0x88, 0xAD, 0x8B, 158 0x0d, 0x64, 0xA9, 0x29, 0xC6, 0xA8, 0x3E, 0xE2, 0x51, 159 0x76, 0x79, 0xAA, 0x02, 0x18, 0xBE, 0xCE, 0xEA, 0x8B, 0x79, 0x86, 160 0x72, 0x6A, 0x8C, 0xDB} 161 crypter.U = []byte{0xED, 0x5B, 0xA7, 0x76, 0xFD, 0xD8, 0xE3, 0x89, 162 0x4F, 0x54, 0x05, 0xC1, 0x3B, 0xFD, 0x86, 0xCF, 0x00, 0x00, 0x00, 163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 0x00, 0x00} 165 crypter.Length = 128 166 crypter.EncryptMetadata = true 167 168 streamData := []byte{0xBC, 0x89, 0x86, 0x8B, 0x3E, 0xCF, 0x24, 0x1C, 169 0xC4, 0x88, 0xF3, 0x60, 0x74, 0x8A, 0x22, 0xE3, 0xAD, 0xF4, 0x48, 170 0x8E, 0x20, 0x94, 0x06, 0x4B, 0x4B, 0xB5, 0x3E, 0x93, 0x89, 0x4E, 171 0x32, 0x38, 0xB4, 0xF6, 0x05, 0x3C, 0x5D, 0x0C, 0x12, 0xE4, 0xEB, 172 0x9B, 0x8D, 0x26, 0x32, 0x7B, 0x09, 0x97, 0xA1, 0xC5, 0x98, 0xF6, 173 0xE7, 0x1C, 0x3B} 174 175 // Plain text stream (hello world). 176 exp := []byte{0x20, 0x20, 0x42, 0x54, 0x0A, 0x20, 0x20, 0x20, 0x20, 177 0x2F, 0x46, 0x31, 0x20, 0x31, 0x38, 0x20, 0x54, 0x66, 0x0A, 0x20, 178 0x20, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x54, 0x64, 0x0A, 0x20, 179 0x20, 0x20, 0x20, 0x28, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 180 0x6F, 0x72, 0x6C, 0x64, 0x29, 0x20, 0x54, 0x6A, 0x0A, 0x20, 0x20, 181 0x45, 0x54} 182 rawText := "2 0 obj\n<< /Length 55 >>\nstream\n" + string(streamData) + "\nendstream\n" 183 184 parser := PdfParser{} 185 parser.xrefs = make(XrefTable) 186 parser.objstms = make(ObjectStreams) 187 parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText) 188 parser.crypter = &crypter 189 190 obj, err := parser.ParseIndirectObject() 191 if err != nil { 192 t.Errorf("Error parsing object") 193 return 194 } 195 196 so, ok := obj.(*PdfObjectStream) 197 if !ok { 198 t.Errorf("Should be stream (is %q)", obj) 199 return 200 } 201 202 authenticated, err := parser.Decrypt([]byte("")) 203 if err != nil { 204 t.Errorf("Error authenticating") 205 return 206 } 207 if !authenticated { 208 t.Errorf("Failed to authenticate") 209 return 210 } 211 212 parser.crypter.Decrypt(so, 0, 0) 213 if string(so.Stream) != string(exp) { 214 t.Errorf("Stream content wrong") 215 return 216 } 217 } 218 219 func BenchmarkAlg2b(b *testing.B) { 220 // hash runs a variable number of rounds, so we need to have a 221 // deterministic random source to make benchmark results comparable 222 r := rand.New(rand.NewSource(1234567)) 223 const n = 20 224 pass := make([]byte, n) 225 r.Read(pass) 226 data := make([]byte, n+8+48) 227 r.Read(data) 228 user := make([]byte, 48) 229 r.Read(user) 230 231 b.ResetTimer() 232 b.ReportAllocs() 233 for i := 0; i < b.N; i++ { 234 _ = alg2b(data, pass, user) 235 } 236 } 237 238 func TestAESv3(t *testing.T) { 239 const keySize = 32 240 241 seed := time.Now().UnixNano() 242 rand := rand.New(rand.NewSource(seed)) 243 244 var cases = []struct { 245 Name string 246 EncMeta bool 247 UserPass string 248 OwnerPass string 249 }{ 250 { 251 Name: "simple", EncMeta: true, 252 UserPass: "user", OwnerPass: "owner", 253 }, 254 { 255 Name: "utf8", EncMeta: false, 256 UserPass: "æøå-u", OwnerPass: "æøå-o", 257 }, 258 { 259 Name: "long", EncMeta: true, 260 UserPass: strings.Repeat("user", 80), 261 OwnerPass: strings.Repeat("owner", 80), 262 }, 263 } 264 265 const ( 266 perms = 0x12345678 267 ) 268 269 for _, R := range []int{5, 6} { 270 R := R 271 t.Run(fmt.Sprintf("R=%d", R), func(t *testing.T) { 272 for _, c := range cases { 273 c := c 274 t.Run(c.Name, func(t *testing.T) { 275 fkey := make([]byte, keySize) 276 rand.Read(fkey) 277 278 crypt := &PdfCrypt{ 279 V: 5, R: R, 280 P: perms, 281 EncryptionKey: append([]byte{}, fkey...), 282 EncryptMetadata: c.EncMeta, 283 } 284 285 // generate encryption parameters 286 err := crypt.generateR6([]byte(c.UserPass), []byte(c.OwnerPass)) 287 if err != nil { 288 t.Fatal("Failed to encrypt:", err) 289 } 290 291 // Perms and EncryptMetadata are checked as a part of alg2a 292 293 // decrypt using user password 294 crypt.EncryptionKey = nil 295 ok, err := crypt.alg2a([]byte(c.UserPass)) 296 if err != nil || !ok { 297 t.Error("Failed to authenticate user pass:", err) 298 } else if !bytes.Equal(crypt.EncryptionKey, fkey) { 299 t.Error("wrong encryption key") 300 } 301 302 // decrypt using owner password 303 crypt.EncryptionKey = nil 304 ok, err = crypt.alg2a([]byte(c.OwnerPass)) 305 if err != nil || !ok { 306 t.Error("Failed to authenticate owner pass:", err) 307 } else if !bytes.Equal(crypt.EncryptionKey, fkey) { 308 t.Error("wrong encryption key") 309 } 310 311 // try to elevate user permissions 312 crypt.P = math.MaxUint32 313 314 crypt.EncryptionKey = nil 315 ok, err = crypt.alg2a([]byte(c.UserPass)) 316 if R == 5 { 317 // it's actually possible with R=5, since Perms is not generated 318 if err != nil || !ok { 319 t.Error("Failed to authenticate user pass:", err) 320 } 321 } else { 322 // not possible in R=6, should return an error 323 if err == nil || ok { 324 t.Error("was able to elevate permissions with R=6") 325 } 326 } 327 }) 328 } 329 }) 330 } 331 }