test_pkcs1_15.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. # -*- coding: utf-8 -*-
  2. #
  3. # SelfTest/Cipher/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 encryption
  4. #
  5. # ===================================================================
  6. # The contents of this file are dedicated to the public domain. To
  7. # the extent that dedication to the public domain is not available,
  8. # everyone is granted a worldwide, perpetual, royalty-free,
  9. # non-exclusive license to exercise all rights associated with the
  10. # contents of this file for any purpose whatsoever.
  11. # No rights are reserved.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  14. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  15. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  16. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  17. # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  18. # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  19. # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. # SOFTWARE.
  21. # ===================================================================
  22. from __future__ import print_function
  23. import json
  24. import unittest
  25. from binascii import unhexlify
  26. from tls.Crypto.PublicKey import RSA
  27. from tls.Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
  28. from tls.Crypto import Random
  29. from tls.Crypto.Cipher import PKCS1_v1_5 as PKCS
  30. from tls.Crypto.Util.py3compat import b
  31. from tls.Crypto.Util.number import bytes_to_long, long_to_bytes
  32. from tls.Crypto.Util._file_system import pycryptodome_filename
  33. def rws(t):
  34. """Remove white spaces, tabs, and new lines from a string"""
  35. for c in ['\n', '\t', ' ']:
  36. t = t.replace(c,'')
  37. return t
  38. def t2b(t):
  39. """Convert a text string with bytes in hex form to a byte string"""
  40. clean = b(rws(t))
  41. if len(clean)%2 == 1:
  42. raise ValueError("Even number of characters expected")
  43. return a2b_hex(clean)
  44. class PKCS1_15_Tests(unittest.TestCase):
  45. def setUp(self):
  46. self.rng = Random.new().read
  47. self.key1024 = RSA.generate(1024, self.rng)
  48. # List of tuples with test data for PKCS#1 v1.5.
  49. # Each tuple is made up by:
  50. # Item #0: dictionary with RSA key component, or key to import
  51. # Item #1: plaintext
  52. # Item #2: ciphertext
  53. # Item #3: random data
  54. _testData = (
  55. #
  56. # Generated with openssl 0.9.8o
  57. #
  58. (
  59. # Private key
  60. '''-----BEGIN RSA PRIVATE KEY-----
  61. MIICXAIBAAKBgQDAiAnvIAOvqVwJTaYzsKnefZftgtXGE2hPJppGsWl78yz9jeXY
  62. W/FxX/gTPURArNhdnhP6n3p2ZaDIBrO2zizbgIXs0IsljTTcr4vnI8fMXzyNUOjA
  63. zP3nzMqZDZK6757XQAobOssMkBFqRWwilT/3DsBhRpl3iMUhF+wvpTSHewIDAQAB
  64. AoGAC4HV/inOrpgTvSab8Wj0riyZgQOZ3U3ZpSlsfR8ra9Ib9Uee3jCYnKscu6Gk
  65. y6zI/cdt8EPJ4PuwAWSNJzbpbVaDvUq25OD+CX8/uRT08yBS4J8TzBitZJTD4lS7
  66. atdTnKT0Wmwk+u8tDbhvMKwnUHdJLcuIsycts9rwJVapUtkCQQDvDpx2JMun0YKG
  67. uUttjmL8oJ3U0m3ZvMdVwBecA0eebZb1l2J5PvI3EJD97eKe91Nsw8T3lwpoN40k
  68. IocSVDklAkEAzi1HLHE6EzVPOe5+Y0kGvrIYRRhncOb72vCvBZvD6wLZpQgqo6c4
  69. d3XHFBBQWA6xcvQb5w+VVEJZzw64y25sHwJBAMYReRl6SzL0qA0wIYrYWrOt8JeQ
  70. 8mthulcWHXmqTgC6FEXP9Es5GD7/fuKl4wqLKZgIbH4nqvvGay7xXLCXD/ECQH9a
  71. 1JYNMtRen5unSAbIOxRcKkWz92F0LKpm9ZW/S9vFHO+mBcClMGoKJHiuQxLBsLbT
  72. NtEZfSJZAeS2sUtn3/0CQDb2M2zNBTF8LlM0nxmh0k9VGm5TVIyBEMcipmvOgqIs
  73. HKukWBcq9f/UOmS0oEhai/6g+Uf7VHJdWaeO5LzuvwU=
  74. -----END RSA PRIVATE KEY-----''',
  75. # Plaintext
  76. '''THIS IS PLAINTEXT\x0A''',
  77. # Ciphertext
  78. '''3f dc fd 3c cd 5c 9b 12 af 65 32 e3 f7 d0 da 36
  79. 8f 8f d9 e3 13 1c 7f c8 b3 f9 c1 08 e4 eb 79 9c
  80. 91 89 1f 96 3b 94 77 61 99 a4 b1 ee 5d e6 17 c9
  81. 5d 0a b5 63 52 0a eb 00 45 38 2a fb b0 71 3d 11
  82. f7 a1 9e a7 69 b3 af 61 c0 bb 04 5b 5d 4b 27 44
  83. 1f 5b 97 89 ba 6a 08 95 ee 4f a2 eb 56 64 e5 0f
  84. da 7c f9 9a 61 61 06 62 ed a0 bc 5f aa 6c 31 78
  85. 70 28 1a bb 98 3c e3 6a 60 3c d1 0b 0f 5a f4 75''',
  86. # Random data
  87. '''eb d7 7d 86 a4 35 23 a3 54 7e 02 0b 42 1d
  88. 61 6c af 67 b8 4e 17 56 80 66 36 04 64 34 26 8a
  89. 47 dd 44 b3 1a b2 17 60 f4 91 2e e2 b5 95 64 cc
  90. f9 da c8 70 94 54 86 4c ef 5b 08 7d 18 c4 ab 8d
  91. 04 06 33 8f ca 15 5f 52 60 8a a1 0c f5 08 b5 4c
  92. bb 99 b8 94 25 04 9c e6 01 75 e6 f9 63 7a 65 61
  93. 13 8a a7 47 77 81 ae 0d b8 2c 4d 50 a5'''
  94. ),
  95. )
  96. def testEncrypt1(self):
  97. for test in self._testData:
  98. # Build the key
  99. key = RSA.importKey(test[0])
  100. # RNG that takes its random numbers from a pool given
  101. # at initialization
  102. class randGen:
  103. def __init__(self, data):
  104. self.data = data
  105. self.idx = 0
  106. def __call__(self, N):
  107. r = self.data[self.idx:self.idx+N]
  108. self.idx += N
  109. return r
  110. # The real test
  111. cipher = PKCS.new(key, randfunc=randGen(t2b(test[3])))
  112. ct = cipher.encrypt(b(test[1]))
  113. self.assertEqual(ct, t2b(test[2]))
  114. def testEncrypt2(self):
  115. # Verify that encryption fail if plaintext is too long
  116. pt = '\x00'*(128-11+1)
  117. cipher = PKCS.new(self.key1024)
  118. self.assertRaises(ValueError, cipher.encrypt, pt)
  119. def testVerify1(self):
  120. for test in self._testData:
  121. # Build the key
  122. key = RSA.importKey(test[0])
  123. # The real test
  124. cipher = PKCS.new(key)
  125. pt = cipher.decrypt(t2b(test[2]), "---")
  126. self.assertEqual(pt, b(test[1]))
  127. def testVerify2(self):
  128. # Verify that decryption fails if ciphertext is not as long as
  129. # RSA modulus
  130. cipher = PKCS.new(self.key1024)
  131. self.assertRaises(ValueError, cipher.decrypt, '\x00'*127, "---")
  132. self.assertRaises(ValueError, cipher.decrypt, '\x00'*129, "---")
  133. # Verify that decryption fails if there are less then 8 non-zero padding
  134. # bytes
  135. pt = b('\x00\x02' + '\xFF'*7 + '\x00' + '\x45'*118)
  136. pt_int = bytes_to_long(pt)
  137. ct_int = self.key1024._encrypt(pt_int)
  138. ct = long_to_bytes(ct_int, 128)
  139. self.assertEqual("---", cipher.decrypt(ct, "---"))
  140. def testEncryptVerify1(self):
  141. # Encrypt/Verify messages of length [0..RSAlen-11]
  142. # and therefore padding [8..117]
  143. for pt_len in range(0,128-11+1):
  144. pt = self.rng(pt_len)
  145. cipher = PKCS.new(self.key1024)
  146. ct = cipher.encrypt(pt)
  147. pt2 = cipher.decrypt(ct, "---")
  148. self.assertEqual(pt,pt2)
  149. def testByteArray(self):
  150. pt = b"XER"
  151. cipher = PKCS.new(self.key1024)
  152. ct = cipher.encrypt(bytearray(pt))
  153. pt2 = cipher.decrypt(bytearray(ct), "---")
  154. self.assertEqual(pt, pt2)
  155. def testMemoryview(self):
  156. pt = b"XER"
  157. cipher = PKCS.new(self.key1024)
  158. ct = cipher.encrypt(memoryview(bytearray(pt)))
  159. pt2 = cipher.decrypt(memoryview(bytearray(ct)), "---")
  160. self.assertEqual(pt, pt2)
  161. import sys
  162. if sys.version[:3] == "2.6":
  163. del testMemoryview
  164. class TestVectorsWycheproof(unittest.TestCase):
  165. def __init__(self, wycheproof_warnings, skip_slow_tests):
  166. unittest.TestCase.__init__(self)
  167. self._wycheproof_warnings = wycheproof_warnings
  168. self._skip_slow_tests = skip_slow_tests
  169. self._id = "None"
  170. def load_tests(self, filename):
  171. comps = "Crypto.SelfTest.Cipher.test_vectors.wycheproof".split(".")
  172. with open(pycryptodome_filename(comps, filename), "rt") as file_in:
  173. tv_tree = json.load(file_in)
  174. class TestVector(object):
  175. pass
  176. result = []
  177. for group in tv_tree['testGroups']:
  178. rsa_key = RSA.import_key(group['privateKeyPem'])
  179. for test in group['tests']:
  180. tv = TestVector()
  181. tv.rsa_key = rsa_key
  182. tv.id = test['tcId']
  183. tv.comment = test['comment']
  184. for attr in 'msg', 'ct':
  185. setattr(tv, attr, unhexlify(test[attr]))
  186. tv.valid = test['result'] != "invalid"
  187. tv.warning = test['result'] == "acceptable"
  188. result.append(tv)
  189. return result
  190. def setUp(self):
  191. self.tv = []
  192. self.tv.extend(self.load_tests("rsa_pkcs1_2048_test.json"))
  193. if not self._skip_slow_tests:
  194. self.tv.extend(self.load_tests("rsa_pkcs1_3072_test.json"))
  195. self.tv.extend(self.load_tests("rsa_pkcs1_4096_test.json"))
  196. def shortDescription(self):
  197. return self._id
  198. def warn(self, tv):
  199. if tv.warning and self._wycheproof_warnings:
  200. import warnings
  201. warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment))
  202. def test_decrypt(self, tv):
  203. self._id = "Wycheproof Decrypt PKCS#1v1.5 Test #%s" % tv.id
  204. cipher = PKCS.new(tv.rsa_key)
  205. try:
  206. pt = cipher.decrypt(tv.ct, sentinel=b'---')
  207. except ValueError:
  208. assert not tv.valid
  209. else:
  210. if pt == b'---':
  211. assert not tv.valid
  212. else:
  213. assert tv.valid
  214. self.assertEqual(pt, tv.msg)
  215. self.warn(tv)
  216. def runTest(self):
  217. for tv in self.tv:
  218. self.test_decrypt(tv)
  219. def get_tests(config={}):
  220. skip_slow_tests = not config.get('slow_tests')
  221. wycheproof_warnings = config.get('wycheproof_warnings')
  222. tests = []
  223. tests += list_test_cases(PKCS1_15_Tests)
  224. tests += [TestVectorsWycheproof(wycheproof_warnings, skip_slow_tests)]
  225. return tests
  226. if __name__ == '__main__':
  227. suite = lambda: unittest.TestSuite(get_tests())
  228. unittest.main(defaultTest='suite')
  229. # vim:set ts=4 sw=4 sts=4 expandtab: