plugin.audio.librespot/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_pkcs1_15.py

284 lines
11 KiB
Python
Raw Normal View History

2024-02-21 06:17:59 +00:00
# -*- 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 unittest
from Cryptodome.PublicKey import RSA
from Cryptodome.SelfTest.st_common import list_test_cases, a2b_hex
from Cryptodome import Random
from Cryptodome.Cipher import PKCS1_v1_5 as PKCS
from Cryptodome.Util.py3compat import b
from Cryptodome.Util.number import bytes_to_long, long_to_bytes
from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof
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:
key = RSA.importKey(test[0])
expected_pt = b(test[1])
ct = t2b(test[2])
cipher = PKCS.new(key)
# The real test
pt = cipher.decrypt(ct, None)
self.assertEqual(pt, expected_pt)
pt = cipher.decrypt(ct, b'\xFF' * len(expected_pt))
self.assertEqual(pt, expected_pt)
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(b"---", cipher.decrypt(ct, b"---"))
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, b'\xAA' * pt_len)
self.assertEqual(pt, pt2)
def test_encrypt_verify_exp_pt_len(self):
cipher = PKCS.new(self.key1024)
pt = b'5' * 16
ct = cipher.encrypt(pt)
sentinel = b'\xAA' * 16
pt_A = cipher.decrypt(ct, sentinel, 16)
self.assertEqual(pt, pt_A)
pt_B = cipher.decrypt(ct, sentinel, 15)
self.assertEqual(sentinel, pt_B)
pt_C = cipher.decrypt(ct, sentinel, 17)
self.assertEqual(sentinel, pt_C)
def testByteArray(self):
pt = b"XER"
cipher = PKCS.new(self.key1024)
ct = cipher.encrypt(bytearray(pt))
pt2 = cipher.decrypt(bytearray(ct), '\xFF' * len(pt))
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)), b'\xFF' * len(pt))
self.assertEqual(pt, pt2)
def test_return_type(self):
pt = b"XYZ"
cipher = PKCS.new(self.key1024)
ct = cipher.encrypt(pt)
self.assertTrue(isinstance(ct, bytes))
pt2 = cipher.decrypt(ct, b'\xAA' * 3)
self.assertTrue(isinstance(pt2, bytes))
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):
def filter_rsa(group):
return RSA.import_key(group['privateKeyPem'])
result = load_test_vectors_wycheproof(("Cipher", "wycheproof"),
filename,
"Wycheproof PKCS#1v1.5 (%s)" % filename,
group_tag={'rsa_key': filter_rsa}
)
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
sentinel = b'\xAA' * max(3, len(tv.msg))
cipher = PKCS.new(tv.rsa_key)
try:
pt = cipher.decrypt(tv.ct, sentinel=sentinel)
except ValueError:
assert not tv.valid
else:
if pt == sentinel:
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__':
def suite():
return unittest.TestSuite(get_tests())
unittest.main(defaultTest='suite')
# vim:set ts=4 sw=4 sts=4 expandtab: