Add tests of all the AES cipher modes.

This tests the CBC and SDCTR modes, in all key lengths, and in
particular includes a set of SDCTR tests designed to test the
procedure for incrementing the IV as a single 128-bit integer, by
checking propagation of the carry between every pair of words.
This commit is contained in:
Simon Tatham 2019-01-09 21:54:52 +00:00
Родитель 7fd815014e
Коммит 48ff6f13e2
1 изменённых файлов: 129 добавлений и 1 удалений

Просмотреть файл

@ -5,7 +5,6 @@ import struct
import itertools
import contextlib
import hashlib
from binascii import unhexlify as unhex
try:
from math import gcd
except ImportError:
@ -28,6 +27,9 @@ def nbits(n):
toret += 1
return toret
def unhex(s):
return s.replace(" ", "").replace("\n", "").decode("hex")
def ssh_uint32(n):
return struct.pack(">L", n)
def ssh_string(s):
@ -753,6 +755,132 @@ class crypt(unittest.TestCase):
self.assertEqual(
fp, b"768 96:12:c8:bc:e6:03:75:86:e8:c7:b9:af:d8:0c:15:75")
def testAES(self):
# My own test cases, generated by a mostly independent
# reference implementation of AES in Python. ('Mostly'
# independent in that it was written by me.)
def vector(cipher, key, iv, plaintext, ciphertext):
c = ssh2_cipher_new(cipher)
ssh2_cipher_setkey(c, key)
ssh2_cipher_setiv(c, iv)
self.assertEqual(ssh2_cipher_encrypt(c, plaintext), ciphertext)
ssh2_cipher_setiv(c, iv)
self.assertEqual(ssh2_cipher_decrypt(c, ciphertext), plaintext)
# Tests of CBC mode.
key = unhex(
'98483c6eb40b6c31a448c22a66ded3b5e5e8d5119cac8327b655c8b5c4836489')
iv = unhex('38f87b0b9b736160bfc0cbd8447af6ee')
plaintext = unhex('''
ee16271827b12d828f61d56fddccc38ccaa69601da2b36d3af1a34c51947b71a
362f05e07bf5e7766c24599799b252ad2d5954353c0c6ca668c46779c2659c94
8df04e4179666e335470ff042e213c8bcff57f54842237fbf9f3c7e6111620ac
1c007180edd25f0e337c2a49d890a7173f6b52d61e3d2a21ddc8e41513a0e825
afd5932172270940b01014b5b7fb8495946151520a126518946b44ea32f9b2a9
''')
vector('aes128', key[:16], iv, plaintext, unhex('''
547ee90514cb6406d5bb00855c8092892c58299646edda0b4e7c044247795c8d
3c3eb3d91332e401215d4d528b94a691969d27b7890d1ae42fe3421b91c989d5
113fefa908921a573526259c6b4f8e4d90ea888e1d8b7747457ba3a43b5b79b9
34873ebf21102d14b51836709ee85ed590b7ca618a1e884f5c57c8ea73fe3d0d
6bf8c082dd602732bde28131159ed0b6e9cf67c353ffdd010a5a634815aaa963'''))
vector('aes192', key[:24], iv, plaintext, unhex('''
e3dee5122edd3fec5fab95e7db8c784c0cb617103e2a406fba4ae3b4508dd608
4ff5723a670316cc91ed86e413c11b35557c56a6f5a7a2c660fc6ee603d73814
73a287645be0f297cdda97aef6c51faeb2392fec9d33adb65138d60f954babd9
8ee0daab0d1decaa8d1e07007c4a3c7b726948025f9fb72dd7de41f74f2f36b4
23ac6a5b4b6b39682ec74f57d9d300e547f3c3e467b77f5e4009923b2f94c903'''))
vector('aes256', key[:32], iv, plaintext, unhex('''
088c6d4d41997bea79c408925255266f6c32c03ea465a5f607c2f076ec98e725
7e0beed79609b3577c16ebdf17d7a63f8865278e72e859e2367de81b3b1fe9ab
8f045e1d008388a3cfc4ff87daffedbb47807260489ad48566dbe73256ce9dd4
ae1689770a883b29695928f5983f33e8d7aec4668f64722e943b0b671c365709
dfa86c648d5fb00544ff11bd29121baf822d867e32da942ba3a0d26299bcee13'''))
# Tests of SDCTR mode, one with a random IV and one with an IV
# about to wrap round. More vigorous tests of IV carry and
# wraparound behaviour are in the testAESSDCTR method.
sdctrIVs = [
unhex('38f87b0b9b736160bfc0cbd8447af6ee'),
unhex('fffffffffffffffffffffffffffffffe'),
]
vector('aes128_ctr', key[:16], sdctrIVs[0], plaintext[:64], unhex('''
d0061d7b6e8c4ef4fe5614b95683383f46cdd2766e66b6fb0b0f0b3a24520b2d
15d869b06cbf685ede064bcf8fb5fb6726cfd68de7016696a126e9e84420af38'''))
vector('aes128_ctr', key[:16], sdctrIVs[1], plaintext[:64], unhex('''
49ac67164fd9ce8701caddbbc9a2b06ac6524d4aa0fdac95253971974b8f3bc2
bb8d7c970f6bcd79b25218cc95582edf7711aae2384f6cf91d8d07c9d9b370bc'''))
vector('aes192_ctr', key[:24], sdctrIVs[0], plaintext[:64], unhex('''
0baa86acbe8580845f0671b7ebad4856ca11b74e5108f515e34e54fa90f87a9a
c6eee26686253c19156f9be64957f0dbc4f8ecd7cabb1f4e0afefe33888faeec'''))
vector('aes192_ctr', key[:24], sdctrIVs[1], plaintext[:64], unhex('''
2da1791250100dc0d1461afe1bbfad8fa0320253ba5d7905d837386ba0a3a41f
01965c770fcfe01cf307b5316afb3981e0e4aa59a6e755f0a5784d9accdc52be'''))
vector('aes256_ctr', key[:32], sdctrIVs[0], plaintext[:64], unhex('''
49c7b284222d408544c770137b6ef17ef770c47e24f61fa66e7e46cae4888882
f980a0f2446956bf47d2aed55ebd2e0694bfc46527ed1fd33efe708fec2f8b1f'''))
vector('aes256_ctr', key[:32], sdctrIVs[1], plaintext[:64], unhex('''
f1d013c3913ccb4fc0091e25d165804480fb0a1d5c741bf012bba144afda6db2
c512f3942018574bd7a8fdd88285a73d25ef81e621aebffb6e9b8ecc8e2549d4'''))
def testAESSDCTR(self):
# A thorough test of the IV-incrementing component of SDCTR
# mode. We set up an AES-SDCTR cipher object with the given
# input IV; we encrypt two all-zero blocks, expecting the
# return values to be the AES-ECB encryptions of the input IV
# and the incremented version. Then we decrypt each of them by
# feeding them to an AES-CBC cipher object with its IV set to
# zero.
def increment(keylen, iv):
key = b'\xab' * (keylen//8)
sdctr = ssh2_cipher_new("aes{}_ctr".format(keylen))
ssh2_cipher_setkey(sdctr, key)
cbc = ssh2_cipher_new("aes{}".format(keylen))
ssh2_cipher_setkey(cbc, key)
ssh2_cipher_setiv(sdctr, iv)
ec0 = ssh2_cipher_encrypt(sdctr, b'\x00' * 16)
ec1 = ssh2_cipher_encrypt(sdctr, b'\x00' * 16)
ssh2_cipher_setiv(cbc, b'\x00' * 16)
dc0 = ssh2_cipher_decrypt(cbc, ec0)
ssh2_cipher_setiv(cbc, b'\x00' * 16)
dc1 = ssh2_cipher_decrypt(cbc, ec1)
self.assertEqual(iv, dc0)
return dc1
def test(keylen, ivInteger):
mask = (1 << 128) - 1
ivInteger &= mask
ivBinary = unhex("{:032x}".format(ivInteger))
ivIntegerInc = (ivInteger + 1) & mask
ivBinaryInc = unhex("{:032x}".format((ivIntegerInc)))
actualResult = increment(keylen, ivBinary)
self.assertEqual(actualResult, ivBinaryInc)
# Check every input IV you can make by gluing together 32-bit
# pieces of the form 0, 1 or -1. This should test all the
# places where carry propagation within the 128-bit integer
# can go wrong.
#
# We also test this at all three AES key lengths, in case the
# core cipher routines are written separately for each one.
for keylen in [128, 192, 256]:
hexTestValues = ["00000000", "00000001", "ffffffff"]
for ivHexBytes in itertools.product(*([hexTestValues] * 4)):
ivInteger = int("".join(ivHexBytes), 16)
test(keylen, ivInteger)
class standard_test_vectors(unittest.TestCase):
def testAES(self):
def vector(cipher, key, plaintext, ciphertext):