test_pkcs1_15.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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 unittest
  24. from Crypto.PublicKey import RSA
  25. from Crypto.SelfTest.st_common import list_test_cases, a2b_hex
  26. from Crypto import Random
  27. from Crypto.Cipher import PKCS1_v1_5 as PKCS
  28. from Crypto.Util.py3compat import b
  29. from Crypto.Util.number import bytes_to_long, long_to_bytes
  30. from Crypto.SelfTest.loader import load_test_vectors_wycheproof
  31. def rws(t):
  32. """Remove white spaces, tabs, and new lines from a string"""
  33. for c in ['\n', '\t', ' ']:
  34. t = t.replace(c, '')
  35. return t
  36. def t2b(t):
  37. """Convert a text string with bytes in hex form to a byte string"""
  38. clean = b(rws(t))
  39. if len(clean) % 2 == 1:
  40. raise ValueError("Even number of characters expected")
  41. return a2b_hex(clean)
  42. class PKCS1_15_Tests(unittest.TestCase):
  43. def setUp(self):
  44. self.rng = Random.new().read
  45. self.key1024 = RSA.generate(1024, self.rng)
  46. # List of tuples with test data for PKCS#1 v1.5.
  47. # Each tuple is made up by:
  48. # Item #0: dictionary with RSA key component, or key to import
  49. # Item #1: plaintext
  50. # Item #2: ciphertext
  51. # Item #3: random data
  52. _testData = (
  53. #
  54. # Generated with openssl 0.9.8o
  55. #
  56. (
  57. # Private key
  58. '''-----BEGIN RSA PRIVATE KEY-----
  59. MIICXAIBAAKBgQDAiAnvIAOvqVwJTaYzsKnefZftgtXGE2hPJppGsWl78yz9jeXY
  60. W/FxX/gTPURArNhdnhP6n3p2ZaDIBrO2zizbgIXs0IsljTTcr4vnI8fMXzyNUOjA
  61. zP3nzMqZDZK6757XQAobOssMkBFqRWwilT/3DsBhRpl3iMUhF+wvpTSHewIDAQAB
  62. AoGAC4HV/inOrpgTvSab8Wj0riyZgQOZ3U3ZpSlsfR8ra9Ib9Uee3jCYnKscu6Gk
  63. y6zI/cdt8EPJ4PuwAWSNJzbpbVaDvUq25OD+CX8/uRT08yBS4J8TzBitZJTD4lS7
  64. atdTnKT0Wmwk+u8tDbhvMKwnUHdJLcuIsycts9rwJVapUtkCQQDvDpx2JMun0YKG
  65. uUttjmL8oJ3U0m3ZvMdVwBecA0eebZb1l2J5PvI3EJD97eKe91Nsw8T3lwpoN40k
  66. IocSVDklAkEAzi1HLHE6EzVPOe5+Y0kGvrIYRRhncOb72vCvBZvD6wLZpQgqo6c4
  67. d3XHFBBQWA6xcvQb5w+VVEJZzw64y25sHwJBAMYReRl6SzL0qA0wIYrYWrOt8JeQ
  68. 8mthulcWHXmqTgC6FEXP9Es5GD7/fuKl4wqLKZgIbH4nqvvGay7xXLCXD/ECQH9a
  69. 1JYNMtRen5unSAbIOxRcKkWz92F0LKpm9ZW/S9vFHO+mBcClMGoKJHiuQxLBsLbT
  70. NtEZfSJZAeS2sUtn3/0CQDb2M2zNBTF8LlM0nxmh0k9VGm5TVIyBEMcipmvOgqIs
  71. HKukWBcq9f/UOmS0oEhai/6g+Uf7VHJdWaeO5LzuvwU=
  72. -----END RSA PRIVATE KEY-----''',
  73. # Plaintext
  74. '''THIS IS PLAINTEXT\x0A''',
  75. # Ciphertext
  76. '''3f dc fd 3c cd 5c 9b 12 af 65 32 e3 f7 d0 da 36
  77. 8f 8f d9 e3 13 1c 7f c8 b3 f9 c1 08 e4 eb 79 9c
  78. 91 89 1f 96 3b 94 77 61 99 a4 b1 ee 5d e6 17 c9
  79. 5d 0a b5 63 52 0a eb 00 45 38 2a fb b0 71 3d 11
  80. f7 a1 9e a7 69 b3 af 61 c0 bb 04 5b 5d 4b 27 44
  81. 1f 5b 97 89 ba 6a 08 95 ee 4f a2 eb 56 64 e5 0f
  82. da 7c f9 9a 61 61 06 62 ed a0 bc 5f aa 6c 31 78
  83. 70 28 1a bb 98 3c e3 6a 60 3c d1 0b 0f 5a f4 75''',
  84. # Random data
  85. '''eb d7 7d 86 a4 35 23 a3 54 7e 02 0b 42 1d
  86. 61 6c af 67 b8 4e 17 56 80 66 36 04 64 34 26 8a
  87. 47 dd 44 b3 1a b2 17 60 f4 91 2e e2 b5 95 64 cc
  88. f9 da c8 70 94 54 86 4c ef 5b 08 7d 18 c4 ab 8d
  89. 04 06 33 8f ca 15 5f 52 60 8a a1 0c f5 08 b5 4c
  90. bb 99 b8 94 25 04 9c e6 01 75 e6 f9 63 7a 65 61
  91. 13 8a a7 47 77 81 ae 0d b8 2c 4d 50 a5'''
  92. ),
  93. )
  94. def testEncrypt1(self):
  95. for test in self._testData:
  96. # Build the key
  97. key = RSA.importKey(test[0])
  98. # RNG that takes its random numbers from a pool given
  99. # at initialization
  100. class randGen:
  101. def __init__(self, data):
  102. self.data = data
  103. self.idx = 0
  104. def __call__(self, N):
  105. r = self.data[self.idx:self.idx+N]
  106. self.idx += N
  107. return r
  108. # The real test
  109. cipher = PKCS.new(key, randfunc=randGen(t2b(test[3])))
  110. ct = cipher.encrypt(b(test[1]))
  111. self.assertEqual(ct, t2b(test[2]))
  112. def testEncrypt2(self):
  113. # Verify that encryption fail if plaintext is too long
  114. pt = '\x00'*(128-11+1)
  115. cipher = PKCS.new(self.key1024)
  116. self.assertRaises(ValueError, cipher.encrypt, pt)
  117. def testVerify1(self):
  118. for test in self._testData:
  119. key = RSA.importKey(test[0])
  120. expected_pt = b(test[1])
  121. ct = t2b(test[2])
  122. cipher = PKCS.new(key)
  123. # The real test
  124. pt = cipher.decrypt(ct, None)
  125. self.assertEqual(pt, expected_pt)
  126. pt = cipher.decrypt(ct, b'\xFF' * len(expected_pt))
  127. self.assertEqual(pt, expected_pt)
  128. def testVerify2(self):
  129. # Verify that decryption fails if ciphertext is not as long as
  130. # RSA modulus
  131. cipher = PKCS.new(self.key1024)
  132. self.assertRaises(ValueError, cipher.decrypt, '\x00'*127, "---")
  133. self.assertRaises(ValueError, cipher.decrypt, '\x00'*129, "---")
  134. # Verify that decryption fails if there are less then 8 non-zero padding
  135. # bytes
  136. pt = b('\x00\x02' + '\xFF'*7 + '\x00' + '\x45'*118)
  137. pt_int = bytes_to_long(pt)
  138. ct_int = self.key1024._encrypt(pt_int)
  139. ct = long_to_bytes(ct_int, 128)
  140. self.assertEqual(b"---", cipher.decrypt(ct, b"---"))
  141. def testEncryptVerify1(self):
  142. # Encrypt/Verify messages of length [0..RSAlen-11]
  143. # and therefore padding [8..117]
  144. for pt_len in range(0, 128 - 11 + 1):
  145. pt = self.rng(pt_len)
  146. cipher = PKCS.new(self.key1024)
  147. ct = cipher.encrypt(pt)
  148. pt2 = cipher.decrypt(ct, b'\xAA' * pt_len)
  149. self.assertEqual(pt, pt2)
  150. def test_encrypt_verify_exp_pt_len(self):
  151. cipher = PKCS.new(self.key1024)
  152. pt = b'5' * 16
  153. ct = cipher.encrypt(pt)
  154. sentinel = b'\xAA' * 16
  155. pt_A = cipher.decrypt(ct, sentinel, 16)
  156. self.assertEqual(pt, pt_A)
  157. pt_B = cipher.decrypt(ct, sentinel, 15)
  158. self.assertEqual(sentinel, pt_B)
  159. pt_C = cipher.decrypt(ct, sentinel, 17)
  160. self.assertEqual(sentinel, pt_C)
  161. def testByteArray(self):
  162. pt = b"XER"
  163. cipher = PKCS.new(self.key1024)
  164. ct = cipher.encrypt(bytearray(pt))
  165. pt2 = cipher.decrypt(bytearray(ct), '\xFF' * len(pt))
  166. self.assertEqual(pt, pt2)
  167. def testMemoryview(self):
  168. pt = b"XER"
  169. cipher = PKCS.new(self.key1024)
  170. ct = cipher.encrypt(memoryview(bytearray(pt)))
  171. pt2 = cipher.decrypt(memoryview(bytearray(ct)), b'\xFF' * len(pt))
  172. self.assertEqual(pt, pt2)
  173. def test_return_type(self):
  174. pt = b"XYZ"
  175. cipher = PKCS.new(self.key1024)
  176. ct = cipher.encrypt(pt)
  177. self.assertTrue(isinstance(ct, bytes))
  178. pt2 = cipher.decrypt(ct, b'\xAA' * 3)
  179. self.assertTrue(isinstance(pt2, bytes))
  180. class TestVectorsWycheproof(unittest.TestCase):
  181. def __init__(self, wycheproof_warnings, skip_slow_tests):
  182. unittest.TestCase.__init__(self)
  183. self._wycheproof_warnings = wycheproof_warnings
  184. self._skip_slow_tests = skip_slow_tests
  185. self._id = "None"
  186. def load_tests(self, filename):
  187. def filter_rsa(group):
  188. return RSA.import_key(group['privateKeyPem'])
  189. result = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
  190. filename,
  191. "Wycheproof PKCS#1v1.5 (%s)" % filename,
  192. group_tag={'rsa_key': filter_rsa}
  193. )
  194. return result
  195. def setUp(self):
  196. self.tv = []
  197. self.tv.extend(self.load_tests("rsa_pkcs1_2048_test.json"))
  198. if not self._skip_slow_tests:
  199. self.tv.extend(self.load_tests("rsa_pkcs1_3072_test.json"))
  200. self.tv.extend(self.load_tests("rsa_pkcs1_4096_test.json"))
  201. def shortDescription(self):
  202. return self._id
  203. def warn(self, tv):
  204. if tv.warning and self._wycheproof_warnings:
  205. import warnings
  206. warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment))
  207. def test_decrypt(self, tv):
  208. self._id = "Wycheproof Decrypt PKCS#1v1.5 Test #%s" % tv.id
  209. sentinel = b'\xAA' * max(3, len(tv.msg))
  210. cipher = PKCS.new(tv.rsa_key)
  211. try:
  212. pt = cipher.decrypt(tv.ct, sentinel=sentinel)
  213. except ValueError:
  214. assert not tv.valid
  215. else:
  216. if pt == sentinel:
  217. assert not tv.valid
  218. else:
  219. assert tv.valid
  220. self.assertEqual(pt, tv.msg)
  221. self.warn(tv)
  222. def runTest(self):
  223. for tv in self.tv:
  224. self.test_decrypt(tv)
  225. def get_tests(config={}):
  226. skip_slow_tests = not config.get('slow_tests')
  227. wycheproof_warnings = config.get('wycheproof_warnings')
  228. tests = []
  229. tests += list_test_cases(PKCS1_15_Tests)
  230. tests += [TestVectorsWycheproof(wycheproof_warnings, skip_slow_tests)]
  231. return tests
  232. if __name__ == '__main__':
  233. def suite():
  234. return unittest.TestSuite(get_tests())
  235. unittest.main(defaultTest='suite')
  236. # vim:set ts=4 sw=4 sts=4 expandtab: