From c507e9c964f08e6233721408918f1178c1f13692 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 13 Jan 2019 13:48:19 +0000 Subject: [PATCH] testcrypt: test both hardware and software AES. The new explicit vtables for the hardware and software implementations are now exposed by name in the testcrypt protocol, and cryptsuite.py runs all the AES tests separately on both. (When hardware AES is compiled out, ssh2_cipher_new("aes128_hw") and similar calls will return None, and cryptsuite.py will respond by skipping those tests.) --- ssh.h | 12 +++++++++ test/cryptsuite.py | 67 +++++++++++++++++++++++++++------------------- testcrypt.c | 12 +++++++++ 3 files changed, 63 insertions(+), 28 deletions(-) diff --git a/ssh.h b/ssh.h index 03c3294f..afb2c606 100644 --- a/ssh.h +++ b/ssh.h @@ -860,11 +860,23 @@ extern const ssh2_cipheralg ssh_3des_ssh2; extern const ssh2_cipheralg ssh_des_ssh2; extern const ssh2_cipheralg ssh_des_sshcom_ssh2; extern const ssh2_cipheralg ssh_aes256_sdctr; +extern const ssh2_cipheralg ssh_aes256_sdctr_hw; +extern const ssh2_cipheralg ssh_aes256_sdctr_sw; extern const ssh2_cipheralg ssh_aes256_cbc; +extern const ssh2_cipheralg ssh_aes256_cbc_hw; +extern const ssh2_cipheralg ssh_aes256_cbc_sw; extern const ssh2_cipheralg ssh_aes192_sdctr; +extern const ssh2_cipheralg ssh_aes192_sdctr_hw; +extern const ssh2_cipheralg ssh_aes192_sdctr_sw; extern const ssh2_cipheralg ssh_aes192_cbc; +extern const ssh2_cipheralg ssh_aes192_cbc_hw; +extern const ssh2_cipheralg ssh_aes192_cbc_sw; extern const ssh2_cipheralg ssh_aes128_sdctr; +extern const ssh2_cipheralg ssh_aes128_sdctr_hw; +extern const ssh2_cipheralg ssh_aes128_sdctr_sw; extern const ssh2_cipheralg ssh_aes128_cbc; +extern const ssh2_cipheralg ssh_aes128_cbc_hw; +extern const ssh2_cipheralg ssh_aes128_cbc_sw; extern const ssh2_cipheralg ssh_blowfish_ssh2_ctr; extern const ssh2_cipheralg ssh_blowfish_ssh2; extern const ssh2_cipheralg ssh_arcfour256_ssh2; diff --git a/test/cryptsuite.py b/test/cryptsuite.py index f8815fde..65a413a7 100755 --- a/test/cryptsuite.py +++ b/test/cryptsuite.py @@ -768,12 +768,16 @@ class crypt(MyTestBase): # 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.assertEqualBin(ssh2_cipher_encrypt(c, plaintext), ciphertext) - ssh2_cipher_setiv(c, iv) - self.assertEqualBin(ssh2_cipher_decrypt(c, ciphertext), plaintext) + for suffix in "hw", "sw": + c = ssh2_cipher_new("{}_{}".format(cipher, suffix)) + if c is None: return # skip test if HW AES not available + ssh2_cipher_setkey(c, key) + ssh2_cipher_setiv(c, iv) + self.assertEqualBin( + ssh2_cipher_encrypt(c, plaintext), ciphertext) + ssh2_cipher_setiv(c, iv) + self.assertEqualBin( + ssh2_cipher_decrypt(c, ciphertext), plaintext) # Tests of CBC mode. @@ -848,11 +852,12 @@ class crypt(MyTestBase): # feeding them to an AES-CBC cipher object with its IV set to # zero. - def increment(keylen, iv): + def increment(keylen, suffix, iv): key = b'\xab' * (keylen//8) - sdctr = ssh2_cipher_new("aes{}_ctr".format(keylen)) + sdctr = ssh2_cipher_new("aes{}_ctr_{}".format(keylen, suffix)) + if sdctr is None: return # skip test if HW AES not available ssh2_cipher_setkey(sdctr, key) - cbc = ssh2_cipher_new("aes{}".format(keylen)) + cbc = ssh2_cipher_new("aes{}_{}".format(keylen, suffix)) ssh2_cipher_setkey(cbc, key) ssh2_cipher_setiv(sdctr, iv) @@ -865,14 +870,15 @@ class crypt(MyTestBase): self.assertEqualBin(iv, dc0) return dc1 - def test(keylen, ivInteger): + def test(keylen, suffix, 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.assertEqualBin(actualResult, ivBinaryInc) + actualResult = increment(keylen, suffix, ivBinary) + if actualResult is not None: + self.assertEqualBin(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 @@ -882,29 +888,34 @@ class crypt(MyTestBase): # 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) + for suffix in "hw", "sw": + for keylen in [128, 192, 256]: + hexTestValues = ["00000000", "00000001", "ffffffff"] + for ivHexBytes in itertools.product(*([hexTestValues] * 4)): + ivInteger = int("".join(ivHexBytes), 16) + test(keylen, suffix, ivInteger) class standard_test_vectors(MyTestBase): def testAES(self): def vector(cipher, key, plaintext, ciphertext): - c = ssh2_cipher_new(cipher) - ssh2_cipher_setkey(c, key) + for suffix in "hw", "sw": + c = ssh2_cipher_new("{}_{}".format(cipher, suffix)) + if c is None: return # skip test if HW AES not available + ssh2_cipher_setkey(c, key) - # The AES test vectors are implicitly in ECB mode, because - # they're testing the cipher primitive rather than any - # mode layered on top of it. We fake this by using PuTTY's - # CBC setting, and clearing the IV to all zeroes before - # each operation. + # The AES test vectors are implicitly in ECB mode, + # because they're testing the cipher primitive rather + # than any mode layered on top of it. We fake this by + # using PuTTY's CBC setting, and clearing the IV to + # all zeroes before each operation. - ssh2_cipher_setiv(c, b'\x00' * 16) - self.assertEqualBin(ssh2_cipher_encrypt(c, plaintext), ciphertext) + ssh2_cipher_setiv(c, b'\x00' * 16) + self.assertEqualBin( + ssh2_cipher_encrypt(c, plaintext), ciphertext) - ssh2_cipher_setiv(c, b'\x00' * 16) - self.assertEqualBin(ssh2_cipher_decrypt(c, ciphertext), plaintext) + ssh2_cipher_setiv(c, b'\x00' * 16) + self.assertEqualBin( + ssh2_cipher_decrypt(c, ciphertext), plaintext) # The test vectors from FIPS 197 appendix C: the key bytes go # 00 01 02 03 ... for as long as needed, and the plaintext diff --git a/testcrypt.c b/testcrypt.c index 16494fa4..30f068e7 100644 --- a/testcrypt.c +++ b/testcrypt.c @@ -270,11 +270,23 @@ static const ssh2_cipheralg *get_ssh2_cipheralg(BinarySource *in) {"des", &ssh_des_ssh2}, {"des_sshcom", &ssh_des_sshcom_ssh2}, {"aes256_ctr", &ssh_aes256_sdctr}, + {"aes256_ctr_hw", &ssh_aes256_sdctr_hw}, + {"aes256_ctr_sw", &ssh_aes256_sdctr_sw}, {"aes256", &ssh_aes256_cbc}, + {"aes256_hw", &ssh_aes256_cbc_hw}, + {"aes256_sw", &ssh_aes256_cbc_sw}, {"aes192_ctr", &ssh_aes192_sdctr}, + {"aes192_ctr_hw", &ssh_aes192_sdctr_hw}, + {"aes192_ctr_sw", &ssh_aes192_sdctr_sw}, {"aes192", &ssh_aes192_cbc}, + {"aes192_hw", &ssh_aes192_cbc_hw}, + {"aes192_sw", &ssh_aes192_cbc_sw}, {"aes128_ctr", &ssh_aes128_sdctr}, + {"aes128_ctr_hw", &ssh_aes128_sdctr_hw}, + {"aes128_ctr_sw", &ssh_aes128_sdctr_sw}, {"aes128", &ssh_aes128_cbc}, + {"aes128_hw", &ssh_aes128_cbc_hw}, + {"aes128_sw", &ssh_aes128_cbc_sw}, {"blowfish", &ssh_blowfish_ssh2_ctr}, {"blowfish", &ssh_blowfish_ssh2}, {"arcfour256", &ssh_arcfour256_ssh2},