123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- # -*- coding: utf-8 -*-
- #
- # SelfTest/Cipher/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 encryption
- #
- # ===================================================================
- # The contents of this file are dedicated to the public domain. To
- # the extent that dedication to the public domain is not available,
- # everyone is granted a worldwide, perpetual, royalty-free,
- # non-exclusive license to exercise all rights associated with the
- # contents of this file for any purpose whatsoever.
- # No rights are reserved.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- # SOFTWARE.
- # ===================================================================
- from __future__ import print_function
- import json
- import unittest
- from binascii import unhexlify
- from tls.Crypto.PublicKey import RSA
- from tls.Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
- from tls.Crypto import Random
- from tls.Crypto.Cipher import PKCS1_v1_5 as PKCS
- from tls.Crypto.Util.py3compat import b
- from tls.Crypto.Util.number import bytes_to_long, long_to_bytes
- from tls.Crypto.Util._file_system import pycryptodome_filename
- def rws(t):
- """Remove white spaces, tabs, and new lines from a string"""
- for c in ['\n', '\t', ' ']:
- t = t.replace(c,'')
- return t
- def t2b(t):
- """Convert a text string with bytes in hex form to a byte string"""
- clean = b(rws(t))
- if len(clean)%2 == 1:
- raise ValueError("Even number of characters expected")
- return a2b_hex(clean)
- class PKCS1_15_Tests(unittest.TestCase):
- def setUp(self):
- self.rng = Random.new().read
- self.key1024 = RSA.generate(1024, self.rng)
- # List of tuples with test data for PKCS#1 v1.5.
- # Each tuple is made up by:
- # Item #0: dictionary with RSA key component, or key to import
- # Item #1: plaintext
- # Item #2: ciphertext
- # Item #3: random data
- _testData = (
- #
- # Generated with openssl 0.9.8o
- #
- (
- # Private key
- '''-----BEGIN RSA PRIVATE KEY-----
- MIICXAIBAAKBgQDAiAnvIAOvqVwJTaYzsKnefZftgtXGE2hPJppGsWl78yz9jeXY
- W/FxX/gTPURArNhdnhP6n3p2ZaDIBrO2zizbgIXs0IsljTTcr4vnI8fMXzyNUOjA
- zP3nzMqZDZK6757XQAobOssMkBFqRWwilT/3DsBhRpl3iMUhF+wvpTSHewIDAQAB
- AoGAC4HV/inOrpgTvSab8Wj0riyZgQOZ3U3ZpSlsfR8ra9Ib9Uee3jCYnKscu6Gk
- y6zI/cdt8EPJ4PuwAWSNJzbpbVaDvUq25OD+CX8/uRT08yBS4J8TzBitZJTD4lS7
- atdTnKT0Wmwk+u8tDbhvMKwnUHdJLcuIsycts9rwJVapUtkCQQDvDpx2JMun0YKG
- uUttjmL8oJ3U0m3ZvMdVwBecA0eebZb1l2J5PvI3EJD97eKe91Nsw8T3lwpoN40k
- IocSVDklAkEAzi1HLHE6EzVPOe5+Y0kGvrIYRRhncOb72vCvBZvD6wLZpQgqo6c4
- d3XHFBBQWA6xcvQb5w+VVEJZzw64y25sHwJBAMYReRl6SzL0qA0wIYrYWrOt8JeQ
- 8mthulcWHXmqTgC6FEXP9Es5GD7/fuKl4wqLKZgIbH4nqvvGay7xXLCXD/ECQH9a
- 1JYNMtRen5unSAbIOxRcKkWz92F0LKpm9ZW/S9vFHO+mBcClMGoKJHiuQxLBsLbT
- NtEZfSJZAeS2sUtn3/0CQDb2M2zNBTF8LlM0nxmh0k9VGm5TVIyBEMcipmvOgqIs
- HKukWBcq9f/UOmS0oEhai/6g+Uf7VHJdWaeO5LzuvwU=
- -----END RSA PRIVATE KEY-----''',
- # Plaintext
- '''THIS IS PLAINTEXT\x0A''',
- # Ciphertext
- '''3f dc fd 3c cd 5c 9b 12 af 65 32 e3 f7 d0 da 36
- 8f 8f d9 e3 13 1c 7f c8 b3 f9 c1 08 e4 eb 79 9c
- 91 89 1f 96 3b 94 77 61 99 a4 b1 ee 5d e6 17 c9
- 5d 0a b5 63 52 0a eb 00 45 38 2a fb b0 71 3d 11
- f7 a1 9e a7 69 b3 af 61 c0 bb 04 5b 5d 4b 27 44
- 1f 5b 97 89 ba 6a 08 95 ee 4f a2 eb 56 64 e5 0f
- da 7c f9 9a 61 61 06 62 ed a0 bc 5f aa 6c 31 78
- 70 28 1a bb 98 3c e3 6a 60 3c d1 0b 0f 5a f4 75''',
- # Random data
- '''eb d7 7d 86 a4 35 23 a3 54 7e 02 0b 42 1d
- 61 6c af 67 b8 4e 17 56 80 66 36 04 64 34 26 8a
- 47 dd 44 b3 1a b2 17 60 f4 91 2e e2 b5 95 64 cc
- f9 da c8 70 94 54 86 4c ef 5b 08 7d 18 c4 ab 8d
- 04 06 33 8f ca 15 5f 52 60 8a a1 0c f5 08 b5 4c
- bb 99 b8 94 25 04 9c e6 01 75 e6 f9 63 7a 65 61
- 13 8a a7 47 77 81 ae 0d b8 2c 4d 50 a5'''
- ),
- )
- def testEncrypt1(self):
- for test in self._testData:
- # Build the key
- key = RSA.importKey(test[0])
- # RNG that takes its random numbers from a pool given
- # at initialization
- class randGen:
- def __init__(self, data):
- self.data = data
- self.idx = 0
- def __call__(self, N):
- r = self.data[self.idx:self.idx+N]
- self.idx += N
- return r
- # The real test
- cipher = PKCS.new(key, randfunc=randGen(t2b(test[3])))
- ct = cipher.encrypt(b(test[1]))
- self.assertEqual(ct, t2b(test[2]))
- def testEncrypt2(self):
- # Verify that encryption fail if plaintext is too long
- pt = '\x00'*(128-11+1)
- cipher = PKCS.new(self.key1024)
- self.assertRaises(ValueError, cipher.encrypt, pt)
- def testVerify1(self):
- for test in self._testData:
- # Build the key
- key = RSA.importKey(test[0])
- # The real test
- cipher = PKCS.new(key)
- pt = cipher.decrypt(t2b(test[2]), "---")
- self.assertEqual(pt, b(test[1]))
- def testVerify2(self):
- # Verify that decryption fails if ciphertext is not as long as
- # RSA modulus
- cipher = PKCS.new(self.key1024)
- self.assertRaises(ValueError, cipher.decrypt, '\x00'*127, "---")
- self.assertRaises(ValueError, cipher.decrypt, '\x00'*129, "---")
- # Verify that decryption fails if there are less then 8 non-zero padding
- # bytes
- pt = b('\x00\x02' + '\xFF'*7 + '\x00' + '\x45'*118)
- pt_int = bytes_to_long(pt)
- ct_int = self.key1024._encrypt(pt_int)
- ct = long_to_bytes(ct_int, 128)
- self.assertEqual("---", cipher.decrypt(ct, "---"))
- def testEncryptVerify1(self):
- # Encrypt/Verify messages of length [0..RSAlen-11]
- # and therefore padding [8..117]
- for pt_len in range(0,128-11+1):
- pt = self.rng(pt_len)
- cipher = PKCS.new(self.key1024)
- ct = cipher.encrypt(pt)
- pt2 = cipher.decrypt(ct, "---")
- self.assertEqual(pt,pt2)
- def testByteArray(self):
- pt = b"XER"
- cipher = PKCS.new(self.key1024)
- ct = cipher.encrypt(bytearray(pt))
- pt2 = cipher.decrypt(bytearray(ct), "---")
- self.assertEqual(pt, pt2)
- def testMemoryview(self):
- pt = b"XER"
- cipher = PKCS.new(self.key1024)
- ct = cipher.encrypt(memoryview(bytearray(pt)))
- pt2 = cipher.decrypt(memoryview(bytearray(ct)), "---")
- self.assertEqual(pt, pt2)
- import sys
- if sys.version[:3] == "2.6":
- del testMemoryview
- class TestVectorsWycheproof(unittest.TestCase):
- def __init__(self, wycheproof_warnings, skip_slow_tests):
- unittest.TestCase.__init__(self)
- self._wycheproof_warnings = wycheproof_warnings
- self._skip_slow_tests = skip_slow_tests
- self._id = "None"
- def load_tests(self, filename):
- comps = "Crypto.SelfTest.Cipher.test_vectors.wycheproof".split(".")
- with open(pycryptodome_filename(comps, filename), "rt") as file_in:
- tv_tree = json.load(file_in)
- class TestVector(object):
- pass
- result = []
- for group in tv_tree['testGroups']:
- rsa_key = RSA.import_key(group['privateKeyPem'])
-
- for test in group['tests']:
- tv = TestVector()
- tv.rsa_key = rsa_key
- tv.id = test['tcId']
- tv.comment = test['comment']
- for attr in 'msg', 'ct':
- setattr(tv, attr, unhexlify(test[attr]))
- tv.valid = test['result'] != "invalid"
- tv.warning = test['result'] == "acceptable"
- result.append(tv)
- return result
- def setUp(self):
- self.tv = []
- self.tv.extend(self.load_tests("rsa_pkcs1_2048_test.json"))
- if not self._skip_slow_tests:
- self.tv.extend(self.load_tests("rsa_pkcs1_3072_test.json"))
- self.tv.extend(self.load_tests("rsa_pkcs1_4096_test.json"))
- def shortDescription(self):
- return self._id
- def warn(self, tv):
- if tv.warning and self._wycheproof_warnings:
- import warnings
- warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment))
- def test_decrypt(self, tv):
- self._id = "Wycheproof Decrypt PKCS#1v1.5 Test #%s" % tv.id
- cipher = PKCS.new(tv.rsa_key)
- try:
- pt = cipher.decrypt(tv.ct, sentinel=b'---')
- except ValueError:
- assert not tv.valid
- else:
- if pt == b'---':
- assert not tv.valid
- else:
- assert tv.valid
- self.assertEqual(pt, tv.msg)
- self.warn(tv)
- def runTest(self):
- for tv in self.tv:
- self.test_decrypt(tv)
- def get_tests(config={}):
- skip_slow_tests = not config.get('slow_tests')
- wycheproof_warnings = config.get('wycheproof_warnings')
- tests = []
- tests += list_test_cases(PKCS1_15_Tests)
- tests += [TestVectorsWycheproof(wycheproof_warnings, skip_slow_tests)]
- return tests
- if __name__ == '__main__':
- suite = lambda: unittest.TestSuite(get_tests())
- unittest.main(defaultTest='suite')
- # vim:set ts=4 sw=4 sts=4 expandtab:
|