4252
|
1 |
#copyright ReportLab Europe Limited. 2000-2016
|
3039
|
2 |
#see license.txt for license details
|
4252
|
3 |
__version__='3.3.0'
|
3039
|
4 |
__doc__="""
|
|
5 |
This contains tests for the encryption algorithms.
|
|
6 |
|
|
7 |
The algorithmic approach is to take values from a known
|
|
8 |
readable-but-secured PDF file and put them in as assertions.
|
|
9 |
If a platform varies in any way or we change an algorithm
|
|
10 |
by mistake, it should shriek at us.
|
|
11 |
|
|
12 |
It also generates a directory of files to scan by eyeball,
|
|
13 |
with meaningful names to suggest the properties they have.
|
|
14 |
|
|
15 |
"""
|
|
16 |
import unittest
|
|
17 |
from reportlab.lib.testutils import setOutDir,makeSuiteForClasses, outputfile, printLocation
|
|
18 |
setOutDir(__name__)
|
|
19 |
from reportlab.pdfgen.canvas import Canvas
|
|
20 |
from reportlab.lib.pdfencrypt import computeO, \
|
|
21 |
computeU, hexText, unHexText, encryptionkey, encodePDF, \
|
|
22 |
encryptCanvas
|
|
23 |
|
|
24 |
VERBOSE = 0
|
|
25 |
|
|
26 |
class EncryptionAlgorithmTestCase(unittest.TestCase):
|
|
27 |
"""Acrobat algorithms. Two specific cases known to work.
|
|
28 |
|
|
29 |
We are dealing with 8 bit strings which may contain nasty
|
|
30 |
escape characters, get trashed in FTP or editors etc. Therefore
|
|
31 |
I am using two explicit routines hexText and unHexText which
|
|
32 |
provide a 'safe' way to represent strings as hex and which are
|
|
33 |
not going to vary with Python versions"""
|
|
34 |
|
|
35 |
def check0wnerHash40Bit(self):
|
|
36 |
"owner key calculation"
|
|
37 |
ownerHash = computeO('userpass','ownerpass', revision=2)
|
4550
|
38 |
self.assertEqual(hexText(ownerHash),'<F86213EB0CED81F097947F3B343E34CAC8CA92CE8F6FEE2556FA31EC1FE968AF>')
|
3039
|
39 |
|
|
40 |
def checkEncryptionKey40Bit(self):
|
|
41 |
userPass = 'userpass'
|
|
42 |
ownerHash = unHexText('<F86213EB0CED81F097947F3B343E34CAC8CA92CE8F6FEE2556FA31EC1FE968AF>')
|
|
43 |
documentID = 'xxxxxxxxxxxxxxxx'
|
|
44 |
permissions = -4
|
|
45 |
encKey = encryptionkey(userPass, ownerHash, permissions, documentID, revision=2)
|
4550
|
46 |
self.assertEqual(hexText(encKey),'<7EBBD07A88>')
|
3039
|
47 |
|
|
48 |
def checkUserHash40Bit(self):
|
|
49 |
encKey = unHexText('<7EBBD07A88>')
|
|
50 |
userHash = computeU(encKey, revision=2, documentId='xxxxxxxxxxxxxxxx')
|
4550
|
51 |
self.assertEqual(hexText(userHash),'<AA154131D8FA105317F7104D2001A345D78A3DEEFA3D85D032FC9B4B35DA72A0>')
|
3039
|
52 |
|
|
53 |
def checkEncryptString40Bit(self):
|
4550
|
54 |
self.assertEqual(hexText(encodePDF(unHexText('<3DC3EBDA71>'), 9, 0, 'anonymous')), '<57AC33DDEB5775982A>')
|
3039
|
55 |
|
|
56 |
|
|
57 |
def check0wnerHash128Bit(self):
|
|
58 |
"owner key calculation"
|
|
59 |
ownerHash = computeO('userpass','ownerpass', revision=3)
|
4550
|
60 |
self.assertEqual(hexText(ownerHash), '<68E5704AC779A5F0CD89704406587A52F25BF61CADC56A0F8DB6C4DB0052534D>')
|
3039
|
61 |
|
|
62 |
def checkEncryptionKey128Bit(self):
|
|
63 |
userPass = 'userpass'
|
|
64 |
ownerHash = unHexText('<68E5704AC779A5F0CD89704406587A52F25BF61CADC56A0F8DB6C4DB0052534D>')
|
|
65 |
documentID = 'xxxxxxxxxxxxxxxx'
|
|
66 |
permissions = -4
|
|
67 |
encKey = encryptionkey(userPass, ownerHash, permissions, documentID, revision=3)
|
4550
|
68 |
self.assertEqual(hexText(encKey), '<13DDE7585D9BE366C976DDD56AF541D1>')
|
3039
|
69 |
|
|
70 |
def checkUserHash128Bit(self):
|
|
71 |
encKey = unHexText('<13DDE7585D9BE366C976DDD56AF541D1>')
|
|
72 |
userHash = computeU(encKey, revision=3, documentId='xxxxxxxxxxxxxxxx')
|
4550
|
73 |
self.assertEqual(hexText(userHash), '<A9AE45CDE827FE0B7D6536267948836A00000000000000000000000000000000>')
|
3039
|
74 |
|
|
75 |
def checkEncryptString128Bit(self):
|
4550
|
76 |
self.assertEqual(hexText(encodePDF(unHexText('<3C0C5EBE0122D8EB2BDDF8A09FA8E29E>'),
|
3039
|
77 |
9,
|
|
78 |
0,
|
|
79 |
'anonymous')
|
4550
|
80 |
),'<27FB3E943FCF61878B>')
|
3039
|
81 |
|
|
82 |
class EyeballTestCase(unittest.TestCase):
|
|
83 |
"This makes a gaxillion self-explanatory files"
|
|
84 |
def check40BitOptions(self):
|
|
85 |
userPass = 'userpass'
|
|
86 |
for canPrint in (0, 1):
|
|
87 |
for canModify in (0, 1):
|
|
88 |
for canCopy in (0, 1):
|
|
89 |
for canAnnotate in (0, 1):
|
|
90 |
for strength in (40, 128):
|
|
91 |
# work out a 4-char string to be a mnemonic for the options
|
|
92 |
p = m = c = a = 'x'
|
|
93 |
if canPrint: p = 'P'
|
|
94 |
if canModify: m = 'M'
|
|
95 |
if canCopy: c = 'C'
|
|
96 |
if canAnnotate: a = 'A'
|
|
97 |
|
|
98 |
filename = 'test_crypto_%03dbit_%s_%s%s%s%s.pdf' % (
|
|
99 |
strength, userPass, p, m, c, a)
|
|
100 |
import os
|
|
101 |
filepath = outputfile(filename)
|
|
102 |
canv = Canvas(filepath)
|
|
103 |
|
|
104 |
canv.setFont('Helvetica', 24)
|
|
105 |
canv.drawString(100,700, 'PDF Encryption test case')
|
|
106 |
canv.setFont('Helvetica', 16)
|
|
107 |
canv.drawString(100, 675, 'Verify by looking at File - Document Info - Security')
|
|
108 |
|
|
109 |
canv.drawString(100, 600, 'open password = %s' % userPass)
|
|
110 |
canv.drawString(100, 575, 'strength = %d buts' % strength)
|
|
111 |
canv.drawString(100, 500, 'canPrint = %d' % canPrint)
|
|
112 |
canv.drawString(100, 475, 'canModify = %d' % canModify)
|
|
113 |
canv.drawString(100, 450, 'canCopy = %d' % canCopy)
|
|
114 |
canv.drawString(100, 425, 'canAnnotate = %d' % canAnnotate)
|
|
115 |
|
|
116 |
encryptCanvas(canv,
|
|
117 |
userPass,
|
|
118 |
canPrint=canPrint,
|
|
119 |
canModify=canModify,
|
|
120 |
canCopy=canCopy,
|
|
121 |
canAnnotate=canAnnotate,
|
|
122 |
strength=strength)
|
|
123 |
|
|
124 |
canv.save()
|
3721
|
125 |
if VERBOSE: print('saved %s' % filepath)
|
3039
|
126 |
|
|
127 |
def makeSuite():
|
|
128 |
return unittest.TestSuite((
|
|
129 |
unittest.makeSuite(EncryptionAlgorithmTestCase,'check'),
|
|
130 |
unittest.makeSuite(EyeballTestCase,'check'),
|
|
131 |
))
|
|
132 |
|
|
133 |
if __name__=='__main__':
|
|
134 |
unittest.TextTestRunner().run(makeSuite())
|