Pack the /P (ermissions) entry from the /Encrypt dictionionary in the file trailer, as unsigned long (#352)

Fixes #186 

* Tread the permissions (the /P entry) as unsigned long, fix #186

* handle negative values for p

* Extract function for resolving an twos-complement

* Add test for issue #352

* Add line to CHANGELOG.md

* Only ints can be converted to a uint using two's-complement method

* Standardize import style; multiple imports from same module on one line

Co-authored-by: Pieter Marsman <pietermarsman@gmail.com>
pull/360/head
Recursing 2020-01-07 21:59:13 +01:00 committed by Pieter Marsman
parent e4790fdbc2
commit 0b1741b9bf
6 changed files with 34 additions and 28 deletions

View File

@ -5,7 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
Nothing here ### Fixed
- Interpret two's complement integer as unsigned integer ([#352](https://github.com/pdfminer/pdfminer.six/pull/352))
## [20200104] - 2019-01-04 ## [20200104] - 2019-01-04

View File

@ -1,34 +1,21 @@
import hashlib as md5
import logging
import re import re
import struct import struct
import logging
import hashlib as md5
try: try:
from Crypto.Cipher import ARC4 from Crypto.Cipher import ARC4, AES
from Crypto.Cipher import AES
from Crypto.Hash import SHA256 from Crypto.Hash import SHA256
except ImportError: except ImportError:
AES = SHA256 = None AES = SHA256 = None
from . import arcfour as ARC4 from . import arcfour as ARC4
from .psparser import PSEOF from .psparser import PSEOF, literal_name, LIT, KWD
from .psparser import literal_name
from .psparser import LIT
from .psparser import KWD
from . import settings from . import settings
from .pdftypes import PDFException from .pdftypes import PDFException, uint_value, PDFTypeError, PDFStream, \
from .pdftypes import PDFTypeError PDFObjectNotFound, decipher_all, int_value, str_value, list_value, \
from .pdftypes import PDFStream dict_value, stream_value
from .pdftypes import PDFObjectNotFound from .pdfparser import PDFSyntaxError, PDFStreamParser
from .pdftypes import decipher_all from .utils import choplist, nunpack, decode_text
from .pdftypes import int_value
from .pdftypes import str_value
from .pdftypes import list_value
from .pdftypes import dict_value
from .pdftypes import stream_value
from .pdfparser import PDFSyntaxError
from .pdfparser import PDFStreamParser
from .utils import choplist
from .utils import nunpack
from .utils import decode_text
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -307,7 +294,7 @@ class PDFStandardSecurityHandler:
def init_params(self): def init_params(self):
self.v = int_value(self.param.get('V', 0)) self.v = int_value(self.param.get('V', 0))
self.r = int_value(self.param['R']) self.r = int_value(self.param['R'])
self.p = int_value(self.param['P']) self.p = uint_value(self.param['P'], 32)
self.o = str_value(self.param['O']) self.o = str_value(self.param['O'])
self.u = str_value(self.param['U']) self.u = str_value(self.param['U'])
self.length = int_value(self.param.get('Length', 40)) self.length = int_value(self.param.get('Length', 40))
@ -348,7 +335,8 @@ class PDFStandardSecurityHandler:
password = (password + self.PASSWORD_PADDING)[:32] # 1 password = (password + self.PASSWORD_PADDING)[:32] # 1
hash = md5.md5(password) # 2 hash = md5.md5(password) # 2
hash.update(self.o) # 3 hash.update(self.o) # 3
hash.update(struct.pack('<l', self.p)) # 4 # See https://github.com/pdfminer/pdfminer.six/issues/186
hash.update(struct.pack('<L', self.p)) # 4
hash.update(self.docid[0]) # 5 hash.update(self.docid[0]) # 5
if self.r >= 4: if self.r >= 4:
if not self.encrypt_metadata: if not self.encrypt_metadata:

View File

@ -139,6 +139,15 @@ def num_value(x):
return x return x
def uint_value(x, n_bits):
"""Resolve number and interpret it as a two's-complement unsigned number"""
x = int_value(x)
if x > 0:
return x
else:
return x + 2**n_bits
def str_value(x): def str_value(x):
x = resolve1(x) x = resolve1(x)
if not isinstance(x, bytes): if not isinstance(x, bytes):

View File

@ -124,7 +124,7 @@ def apply_matrix_norm(m, v):
# Utility functions # Utility functions
def isnumber(x): def isnumber(x):
return isinstance(x, ((int,), float)) return isinstance(x, (int, float))
def uniq(objs): def uniq(objs):

Binary file not shown.

View File

@ -35,7 +35,8 @@ class TestDumpPDF():
def test_nonfree_175(self): def test_nonfree_175(self):
"""Regression test for: """Regression test for:
https://github.com/pdfminer/pdfminer.six/issues/65""" https://github.com/pdfminer/pdfminer.six/issues/65
"""
run('nonfree/175.pdf') run('nonfree/175.pdf')
def test_nonfree_dmca(self): def test_nonfree_dmca(self):
@ -63,6 +64,13 @@ class TestDumpPDF():
"""Regression test for # https://github.com/euske/pdfminer/issues/96""" """Regression test for # https://github.com/euske/pdfminer/issues/96"""
run('scancode/patchelf.pdf') run('scancode/patchelf.pdf')
def test_contrib_hash_two_complement(self):
"""Check that unsigned integer is added correctly to encryption hash.
See https://github.com/pdfminer/pdfminer.six/issues/186
"""
run('contrib/issue-00352-hash-twos-complement.pdf')
class TestDumpImages: class TestDumpImages: