Spaces:
Running
Running
File size: 10,944 Bytes
ba2f5d6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# -*- 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 Crypto.PublicKey import RSA
from Crypto.SelfTest.st_common import list_test_cases, a2b_hex
from Crypto import Random
from Crypto.Cipher import PKCS1_v1_5 as PKCS
from Crypto.Util.py3compat import b
from Crypto.Util.number import bytes_to_long, long_to_bytes
from Crypto.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:
|