| | 108 | # Encrypt this PDF file with the PDF Standard encryption handler. |
|---|
| | 109 | # @param user_pwd The "user password", which allows for opening and reading |
|---|
| | 110 | # the PDF file with the restrictions provided. |
|---|
| | 111 | # @param owner_pwd The "owner password", which allows for opening the PDF |
|---|
| | 112 | # files without any restrictions. By default, the owner password is the |
|---|
| | 113 | # same as the user password. |
|---|
| | 114 | # @param use_128bit Boolean argument as to whether to use 128bit |
|---|
| | 115 | # encryption. When false, 40bit encryption will be used. By default, this |
|---|
| | 116 | # flag is on. |
|---|
| | 117 | def encrypt(self, user_pwd, owner_pwd = None, use_128bit = True): |
|---|
| | 118 | import md5, time, random |
|---|
| | 119 | if owner_pwd == None: |
|---|
| | 120 | owner_pwd = user_pwd |
|---|
| | 121 | if use_128bit: |
|---|
| | 122 | V = 2 |
|---|
| | 123 | rev = 3 |
|---|
| | 124 | keylen = 128 / 8 |
|---|
| | 125 | else: |
|---|
| | 126 | V = 1 |
|---|
| | 127 | rev = 2 |
|---|
| | 128 | keylen = 40 / 8 |
|---|
| | 129 | # permit everything: |
|---|
| | 130 | P = -1 |
|---|
| | 131 | O = StringObject(_alg33(owner_pwd, user_pwd, rev, keylen)) |
|---|
| | 132 | ID_1 = md5.new(repr(time.time())).digest() |
|---|
| | 133 | ID_2 = md5.new(repr(random.random())).digest() |
|---|
| | 134 | self._ID = ArrayObject((StringObject(ID_1), StringObject(ID_2))) |
|---|
| | 135 | if rev == 2: |
|---|
| | 136 | U, key = _alg34(user_pwd, O, P, ID_1) |
|---|
| | 137 | else: |
|---|
| | 138 | assert rev == 3 |
|---|
| | 139 | U, key = _alg35(user_pwd, rev, keylen, O, P, ID_1, False) |
|---|
| | 140 | encrypt = DictionaryObject() |
|---|
| | 141 | encrypt[NameObject("/Filter")] = NameObject("/Standard") |
|---|
| | 142 | encrypt[NameObject("/V")] = NumberObject(V) |
|---|
| | 143 | if V == 2: |
|---|
| | 144 | encrypt[NameObject("/Length")] = NumberObject(keylen * 8) |
|---|
| | 145 | encrypt[NameObject("/R")] = NumberObject(rev) |
|---|
| | 146 | encrypt[NameObject("/O")] = StringObject(O) |
|---|
| | 147 | encrypt[NameObject("/U")] = StringObject(U) |
|---|
| | 148 | encrypt[NameObject("/P")] = NumberObject(P) |
|---|
| | 149 | self._encrypt = self._addObject(encrypt) |
|---|
| | 150 | self._encrypt_key = key |
|---|
| | 151 | |
|---|
| | 152 | ## |
|---|
| 125 | | stream.write(str(i + 1) + " 0 obj\n") |
|---|
| 126 | | obj.writeToStream(stream) |
|---|
| | 173 | stream.write(str(idnum) + " 0 obj\n") |
|---|
| | 174 | key = None |
|---|
| | 175 | if idnum != self._encrypt.idnum and hasattr(self, "_encrypt_key"): |
|---|
| | 176 | pack1 = struct.pack("<i", i + 1)[:3] |
|---|
| | 177 | pack2 = struct.pack("<i", 0)[:2] |
|---|
| | 178 | key = self._encrypt_key + pack1 + pack2 |
|---|
| | 179 | assert len(key) == (len(self._encrypt_key) + 5) |
|---|
| | 180 | md5_hash = md5.new(key).digest() |
|---|
| | 181 | key = md5_hash[:min(16, len(self._encrypt_key) + 5)] |
|---|
| | 182 | obj.writeToStream(stream, key) |
|---|
| 542 | | # ref: pdf1.8 spec section 3.5.2 algorithm 3.2 |
|---|
| 543 | | _encryption_padding = '\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56' + \ |
|---|
| 544 | | '\xff\xfa\x01\x08\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c' + \ |
|---|
| 545 | | '\xa9\xfe\x64\x53\x69\x7a' |
|---|
| 546 | | |
|---|
| 547 | | def _alg32(self, password, rev, keylen, metadata_encrypt=True): |
|---|
| 548 | | import md5, struct |
|---|
| 549 | | m = md5.new() |
|---|
| 550 | | password = (password + self._encryption_padding)[:32] |
|---|
| 551 | | m.update(password) |
|---|
| 552 | | encrypt = self.safeGetObject(self.trailer['/Encrypt']) |
|---|
| 553 | | owner_entry = self.safeGetObject(encrypt['/O']) |
|---|
| 554 | | m.update(owner_entry) |
|---|
| 555 | | p_entry = self.safeGetObject(encrypt['/P']) |
|---|
| 556 | | p_entry = struct.pack('<i', p_entry) |
|---|
| 557 | | m.update(p_entry) |
|---|
| 558 | | id_entry = self.safeGetObject(self.trailer['/ID']) |
|---|
| 559 | | id1_entry = self.safeGetObject(id_entry[0]) |
|---|
| 560 | | m.update(id1_entry) |
|---|
| 561 | | if rev >= 3 and not metadata_encrypt: |
|---|
| 562 | | m.update("\xff\xff\xff\xff") |
|---|
| 563 | | md5_hash = m.digest() |
|---|
| 564 | | if rev >= 3: |
|---|
| 565 | | for i in range(50): |
|---|
| 566 | | md5_hash = md5.new(md5_hash[:keylen]).digest() |
|---|
| 567 | | return md5_hash[:keylen] |
|---|
| 568 | | |
|---|
| 569 | | def _alg34(self, password): |
|---|
| 570 | | key = self._alg32(password, 2, 5) |
|---|
| 571 | | U = utils.RC4_encrypt(key, self._encryption_padding) |
|---|
| 572 | | return U, key |
|---|
| 573 | | |
|---|
| 574 | | def _alg33_1(self, password, rev, keylen): |
|---|
| 575 | | import md5 |
|---|
| 576 | | m = md5.new() |
|---|
| 577 | | password = (password + self._encryption_padding)[:32] |
|---|
| 578 | | m.update(password) |
|---|
| 579 | | md5_hash = m.digest() |
|---|
| 580 | | if rev >= 3: |
|---|
| 581 | | for i in range(50): |
|---|
| 582 | | md5_hash = md5.new(md5_hash).digest() |
|---|
| 583 | | key = md5_hash[:keylen] |
|---|
| 584 | | return key |
|---|
| 585 | | |
|---|
| 586 | | def _alg35(self, password, rev, keylen, metadata_encrypt): |
|---|
| 587 | | import md5 |
|---|
| 588 | | m = md5.new() |
|---|
| 589 | | m.update(self._encryption_padding) |
|---|
| 590 | | id_entry = self.safeGetObject(self.trailer['/ID']) |
|---|
| 591 | | id1_entry = self.safeGetObject(id_entry[0]) |
|---|
| 592 | | m.update(id1_entry) |
|---|
| 593 | | md5_hash = m.digest() |
|---|
| 594 | | key = self._alg32(password, rev, keylen) |
|---|
| 595 | | val = utils.RC4_encrypt(key, md5_hash) |
|---|
| 596 | | for i in range(1, 20): |
|---|
| 597 | | new_key = '' |
|---|
| 598 | | for l in range(len(key)): |
|---|
| 599 | | new_key += chr(ord(key[l]) ^ i) |
|---|
| 600 | | val = utils.RC4_encrypt(new_key, val) |
|---|
| 601 | | return val + ('\x00' * 16), key |
|---|
| 602 | | |
|---|
| 603 | | def _authenticateUserPassword(self, password): |
|---|
| 604 | | encrypt = self.safeGetObject(self.trailer['/Encrypt']) |
|---|
| 605 | | rev = self.safeGetObject(encrypt['/R']) |
|---|
| 606 | | if rev == 2: |
|---|
| 607 | | U, key = self._alg34(password) |
|---|
| 608 | | elif rev >= 3: |
|---|
| 609 | | U, key = self._alg35(password, rev, self.safeGetObject(encrypt["/Length"]) / 8, |
|---|
| 610 | | self.safeGetObject(encrypt.get("/EncryptMetadata", False))) |
|---|
| 611 | | real_U = self.safeGetObject(encrypt['/U']) |
|---|
| 612 | | return U == real_U, key |
|---|
| 613 | | |
|---|
| | 660 | |
|---|
| | 661 | def _authenticateUserPassword(self, password): |
|---|
| | 662 | encrypt = self.safeGetObject(self.trailer['/Encrypt']) |
|---|
| | 663 | rev = self.safeGetObject(encrypt['/R']) |
|---|
| | 664 | owner_entry = self.safeGetObject(encrypt['/O']) |
|---|
| | 665 | p_entry = self.safeGetObject(encrypt['/P']) |
|---|
| | 666 | id_entry = self.safeGetObject(self.trailer['/ID']) |
|---|
| | 667 | id1_entry = self.safeGetObject(id_entry[0]) |
|---|
| | 668 | if rev == 2: |
|---|
| | 669 | U, key = _alg34(password, owner_entry, p_entry, id1_entry) |
|---|
| | 670 | elif rev >= 3: |
|---|
| | 671 | U, key = _alg35(password, rev, |
|---|
| | 672 | self.safeGetObject(encrypt["/Length"]) / 8, owner_entry, |
|---|
| | 673 | p_entry, id1_entry, |
|---|
| | 674 | self.safeGetObject(encrypt.get("/EncryptMetadata", False))) |
|---|
| | 675 | real_U = self.safeGetObject(encrypt['/U']) |
|---|
| | 676 | return U == real_U, key |
|---|
| | 1076 | # ref: pdf1.8 spec section 3.5.2 algorithm 3.2 |
|---|
| | 1077 | _encryption_padding = '\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56' + \ |
|---|
| | 1078 | '\xff\xfa\x01\x08\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c' + \ |
|---|
| | 1079 | '\xa9\xfe\x64\x53\x69\x7a' |
|---|
| | 1080 | |
|---|
| | 1081 | def _alg32(password, rev, keylen, owner_entry, p_entry, id1_entry, metadata_encrypt=True): |
|---|
| | 1082 | import md5, struct |
|---|
| | 1083 | m = md5.new() |
|---|
| | 1084 | password = (password + _encryption_padding)[:32] |
|---|
| | 1085 | m.update(password) |
|---|
| | 1086 | m.update(owner_entry) |
|---|
| | 1087 | p_entry = struct.pack('<i', p_entry) |
|---|
| | 1088 | m.update(p_entry) |
|---|
| | 1089 | m.update(id1_entry) |
|---|
| | 1090 | if rev >= 3 and not metadata_encrypt: |
|---|
| | 1091 | m.update("\xff\xff\xff\xff") |
|---|
| | 1092 | md5_hash = m.digest() |
|---|
| | 1093 | if rev >= 3: |
|---|
| | 1094 | for i in range(50): |
|---|
| | 1095 | md5_hash = md5.new(md5_hash[:keylen]).digest() |
|---|
| | 1096 | return md5_hash[:keylen] |
|---|
| | 1097 | |
|---|
| | 1098 | def _alg33(owner_pwd, user_pwd, rev, keylen): |
|---|
| | 1099 | key = _alg33_1(owner_pwd, rev, keylen) |
|---|
| | 1100 | user_pwd = (user_pwd + _encryption_padding)[:32] |
|---|
| | 1101 | val = utils.RC4_encrypt(key, user_pwd) |
|---|
| | 1102 | if rev >= 3: |
|---|
| | 1103 | for i in range(1, 20): |
|---|
| | 1104 | new_key = '' |
|---|
| | 1105 | for l in range(len(key)): |
|---|
| | 1106 | new_key += chr(ord(key[l]) ^ i) |
|---|
| | 1107 | val = utils.RC4_encrypt(new_key, val) |
|---|
| | 1108 | return val |
|---|
| | 1109 | |
|---|
| | 1110 | def _alg33_1(password, rev, keylen): |
|---|
| | 1111 | import md5 |
|---|
| | 1112 | m = md5.new() |
|---|
| | 1113 | password = (password + _encryption_padding)[:32] |
|---|
| | 1114 | m.update(password) |
|---|
| | 1115 | md5_hash = m.digest() |
|---|
| | 1116 | if rev >= 3: |
|---|
| | 1117 | for i in range(50): |
|---|
| | 1118 | md5_hash = md5.new(md5_hash).digest() |
|---|
| | 1119 | key = md5_hash[:keylen] |
|---|
| | 1120 | return key |
|---|
| | 1121 | |
|---|
| | 1122 | def _alg34(password, owner_entry, p_entry, id1_entry): |
|---|
| | 1123 | key = _alg32(password, 2, 5, owner_entry, p_entry, id1_entry) |
|---|
| | 1124 | U = utils.RC4_encrypt(key, _encryption_padding) |
|---|
| | 1125 | return U, key |
|---|
| | 1126 | |
|---|
| | 1127 | def _alg35(password, rev, keylen, owner_entry, p_entry, id1_entry, metadata_encrypt): |
|---|
| | 1128 | import md5 |
|---|
| | 1129 | m = md5.new() |
|---|
| | 1130 | m.update(_encryption_padding) |
|---|
| | 1131 | m.update(id1_entry) |
|---|
| | 1132 | md5_hash = m.digest() |
|---|
| | 1133 | key = _alg32(password, rev, keylen, owner_entry, p_entry, id1_entry) |
|---|
| | 1134 | val = utils.RC4_encrypt(key, md5_hash) |
|---|
| | 1135 | for i in range(1, 20): |
|---|
| | 1136 | new_key = '' |
|---|
| | 1137 | for l in range(len(key)): |
|---|
| | 1138 | new_key += chr(ord(key[l]) ^ i) |
|---|
| | 1139 | val = utils.RC4_encrypt(new_key, val) |
|---|
| | 1140 | return val + ('\x00' * 16), key |
|---|