Skip to content

Commit

Permalink
Pack the /P (ermissions) entry from the /Encrypt dictionionary in the…
Browse files Browse the repository at this point in the history
… 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>
  • Loading branch information
Recursing and pietermarsman committed Jan 7, 2020
1 parent e4790fd commit 0b1741b
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 28 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [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

Expand Down
38 changes: 13 additions & 25 deletions pdfminer/pdfdocument.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,21 @@
import hashlib as md5
import logging
import re
import struct
import logging
import hashlib as md5

try:
from Crypto.Cipher import ARC4
from Crypto.Cipher import AES
from Crypto.Cipher import ARC4, AES
from Crypto.Hash import SHA256
except ImportError:
AES = SHA256 = None
from . import arcfour as ARC4
from .psparser import PSEOF
from .psparser import literal_name
from .psparser import LIT
from .psparser import KWD
from .psparser import PSEOF, literal_name, LIT, KWD
from . import settings
from .pdftypes import PDFException
from .pdftypes import PDFTypeError
from .pdftypes import PDFStream
from .pdftypes import PDFObjectNotFound
from .pdftypes import decipher_all
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
from .pdftypes import PDFException, uint_value, PDFTypeError, PDFStream, \
PDFObjectNotFound, decipher_all, int_value, str_value, list_value, \
dict_value, stream_value
from .pdfparser import PDFSyntaxError, PDFStreamParser
from .utils import choplist, nunpack, decode_text


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -307,7 +294,7 @@ def init(self):
def init_params(self):
self.v = int_value(self.param.get('V', 0))
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.u = str_value(self.param['U'])
self.length = int_value(self.param.get('Length', 40))
Expand Down Expand Up @@ -348,7 +335,8 @@ def compute_encryption_key(self, password):
password = (password + self.PASSWORD_PADDING)[:32] # 1
hash = md5.md5(password) # 2
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
if self.r >= 4:
if not self.encrypt_metadata:
Expand Down
9 changes: 9 additions & 0 deletions pdfminer/pdftypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ def num_value(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):
x = resolve1(x)
if not isinstance(x, bytes):
Expand Down
2 changes: 1 addition & 1 deletion pdfminer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def apply_matrix_norm(m, v):
# Utility functions

def isnumber(x):
return isinstance(x, ((int,), float))
return isinstance(x, (int, float))


def uniq(objs):
Expand Down
Binary file not shown.
10 changes: 9 additions & 1 deletion tests/test_tools_pdf2txt.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ def test_sample_one_byte_identity_encode(self):

def test_nonfree_175(self):
"""Regression test for:
https://github.com/pdfminer/pdfminer.six/issues/65"""
https://github.com/pdfminer/pdfminer.six/issues/65
"""
run('nonfree/175.pdf')

def test_nonfree_dmca(self):
Expand Down Expand Up @@ -63,6 +64,13 @@ def test_scancode_patchelf(self):
"""Regression test for # https://github.com/euske/pdfminer/issues/96"""
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:

Expand Down

0 comments on commit 0b1741b

Please sign in to comment.