(self, password='')
| 342 | # with the document. |
| 343 | PASSWORD_PADDING = '(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08..\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz' |
| 344 | def initialize(self, password=''): |
| 345 | if not self.encryption: |
| 346 | self.is_printable = self.is_modifiable = self.is_extractable = True |
| 347 | return |
| 348 | (docid, param) = self.encryption |
| 349 | if literal_name(param.get('Filter')) != 'Standard': |
| 350 | raise PDFEncryptionError('Unknown filter: param=%r' % param) |
| 351 | V = int_value(param.get('V', 0)) |
| 352 | if not (V == 1 or V == 2): |
| 353 | raise PDFEncryptionError('Unknown algorithm: param=%r' % param) |
| 354 | length = int_value(param.get('Length', 40)) # Key length (bits) |
| 355 | O = str_value(param['O']) |
| 356 | R = int_value(param['R']) # Revision |
| 357 | if 5 <= R: |
| 358 | raise PDFEncryptionError('Unknown revision: %r' % R) |
| 359 | U = str_value(param['U']) |
| 360 | P = int_value(param['P']) |
| 361 | self.is_printable = bool(P & 4) |
| 362 | self.is_modifiable = bool(P & 8) |
| 363 | self.is_extractable = bool(P & 16) |
| 364 | # Algorithm 3.2 |
| 365 | password = (password+self.PASSWORD_PADDING)[:32] # 1 |
| 366 | hash = md5.md5(password) # 2 |
| 367 | hash.update(O) # 3 |
| 368 | hash.update(struct.pack('<l', P)) # 4 |
| 369 | hash.update(docid[0]) # 5 |
| 370 | if 4 <= R: |
| 371 | # 6 |
| 372 | raise PDFNotImplementedError('Revision 4 encryption is currently unsupported') |
| 373 | if 3 <= R: |
| 374 | # 8 |
| 375 | for _ in xrange(50): |
| 376 | hash = md5.md5(hash.digest()[:length/8]) |
| 377 | key = hash.digest()[:length/8] |
| 378 | if R == 2: |
| 379 | # Algorithm 3.4 |
| 380 | u1 = Arcfour(key).process(self.PASSWORD_PADDING) |
| 381 | elif R == 3: |
| 382 | # Algorithm 3.5 |
| 383 | hash = md5.md5(self.PASSWORD_PADDING) # 2 |
| 384 | hash.update(docid[0]) # 3 |
| 385 | x = Arcfour(key).process(hash.digest()[:16]) # 4 |
| 386 | for i in xrange(1,19+1): |
| 387 | k = ''.join( chr(ord(c) ^ i) for c in key ) |
| 388 | x = Arcfour(k).process(x) |
| 389 | u1 = x+x # 32bytes total |
| 390 | if R == 2: |
| 391 | is_authenticated = (u1 == U) |
| 392 | else: |
| 393 | is_authenticated = (u1[:16] == U[:16]) |
| 394 | if not is_authenticated: |
| 395 | raise PDFPasswordIncorrect |
| 396 | self.decrypt_key = key |
| 397 | self.decipher = self.decrypt_rc4 # XXX may be AES |
| 398 | return |
| 399 | |
| 400 | def decrypt_rc4(self, objid, genno, data): |
| 401 | key = self.decrypt_key + struct.pack('<L',objid)[:3]+struct.pack('<L',genno)[:2] |
no test coverage detected