mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-07-01 03:22:48 -05:00
Break up crypto modules containing HW acceleration.
This applies to all of AES, SHA-1, SHA-256 and SHA-512. All those source files previously contained multiple implementations of the algorithm, enabled or disabled by ifdefs detecting whether they would work on a given compiler. And in order to get advanced machine instructions like AES-NI or NEON crypto into the output file when the compile flags hadn't enabled them, we had to do nasty stuff with compiler-specific pragmas or attributes. Now we can do the detection at cmake time, and enable advanced instructions in the more sensible way, by compile-time flags. So I've broken up each of these modules into lots of sub-pieces: a file called (e.g.) 'foo-common.c' containing common definitions across all implementations (such as round constants), one called 'foo-select.c' containing the top-level vtable(s), and a separate file for each implementation exporting just the vtable(s) for that implementation. One advantage of this is that it depends a lot less on compiler- specific bodgery. My particular least favourite part of the previous setup was the part where I had to _manually_ define some Arm ACLE feature macros before including <arm_neon.h>, so that it would define the intrinsics I wanted. Now I'm enabling interesting architecture features in the normal way, on the compiler command line, there's no need for that kind of trick: the right feature macros are already defined and <arm_neon.h> does the right thing. Another change in this reorganisation is that I've stopped assuming there's just one hardware implementation per platform. Previously, the accelerated vtables were called things like sha256_hw, and varied between FOO-NI and NEON depending on platform; and the selection code would simply ask 'is hw available? if so, use hw, else sw'. Now, each HW acceleration strategy names its vtable its own way, and the selection vtable has a whole list of possibilities to iterate over looking for a supported one. So if someone feels like writing a second accelerated implementation of something for a given platform - for example, I've heard you can use plain NEON to speed up AES somewhat even without the crypto extension - then it will now have somewhere to drop in alongside the existing ones.
This commit is contained in:
@ -141,6 +141,14 @@ def mac_str(alg, key, message, cipher=None):
|
||||
def lcm(a, b):
|
||||
return a * b // gcd(a, b)
|
||||
|
||||
def get_implementations(alg):
|
||||
return get_implementations_commasep(alg).decode("ASCII").split(",")
|
||||
|
||||
def get_aes_impls():
|
||||
return [impl.rsplit("_", 1)[-1]
|
||||
for impl in get_implementations("aes128_cbc")
|
||||
if impl.startswith("aes128_cbc_")]
|
||||
|
||||
class MyTestBase(unittest.TestCase):
|
||||
"Intermediate class that adds useful helper methods."
|
||||
def assertEqualBin(self, x, y):
|
||||
@ -1181,9 +1189,9 @@ class crypt(MyTestBase):
|
||||
# reference implementation of AES in Python. ('Mostly'
|
||||
# independent in that it was written by me.)
|
||||
|
||||
def vector(cipher, key, iv, plaintext, ciphertext):
|
||||
for suffix in "hw", "sw":
|
||||
c = ssh_cipher_new("{}_{}".format(cipher, suffix))
|
||||
def vector(cipherbase, key, iv, plaintext, ciphertext):
|
||||
for cipher in get_implementations(cipherbase):
|
||||
c = ssh_cipher_new(cipher)
|
||||
if c is None: return # skip test if HW AES not available
|
||||
ssh_cipher_setkey(c, key)
|
||||
ssh_cipher_setiv(c, iv)
|
||||
@ -1302,7 +1310,7 @@ 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 suffix in "hw", "sw":
|
||||
for suffix in get_aes_impls():
|
||||
for keylen in [128, 192, 256]:
|
||||
hexTestValues = ["00000000", "00000001", "ffffffff"]
|
||||
for ivHexBytes in itertools.product(*([hexTestValues] * 4)):
|
||||
@ -1325,7 +1333,7 @@ class crypt(MyTestBase):
|
||||
for keylen in [128, 192, 256]:
|
||||
decryptions = []
|
||||
|
||||
for suffix in "hw", "sw":
|
||||
for suffix in get_aes_impls():
|
||||
c = ssh_cipher_new("aes{:d}_cbc_{}".format(keylen, suffix))
|
||||
if c is None: continue
|
||||
ssh_cipher_setkey(c, test_key[:keylen//8])
|
||||
@ -1493,23 +1501,11 @@ class crypt(MyTestBase):
|
||||
("3des_ssh1", 24, 8, False, unhex('d5f1cc25b8fbc62de63590b9b92344adf6dd72753273ff0fb32d4dbc6af858529129f34242f3d557eed3a5c84204eb4f868474294964cf70df5d8f45dfccfc45')),
|
||||
("des_cbc", 8, 8, True, unhex('051524e77fb40e109d9fffeceacf0f28c940e2f8415ddccc117020bdd2612af5036490b12085d0e46129919b8e499f51cb82a4b341d7a1a1ea3e65201ef248f6')),
|
||||
("aes256_ctr", 32, 16, False, unhex('b87b35e819f60f0f398a37b05d7bcf0b04ad4ebe570bd08e8bfa8606bafb0db2cfcd82baf2ccceae5de1a3c1ae08a8b8fdd884fdc5092031ea8ce53333e62976')),
|
||||
("aes256_ctr_hw", 32, 16, False, unhex('b87b35e819f60f0f398a37b05d7bcf0b04ad4ebe570bd08e8bfa8606bafb0db2cfcd82baf2ccceae5de1a3c1ae08a8b8fdd884fdc5092031ea8ce53333e62976')),
|
||||
("aes256_ctr_sw", 32, 16, False, unhex('b87b35e819f60f0f398a37b05d7bcf0b04ad4ebe570bd08e8bfa8606bafb0db2cfcd82baf2ccceae5de1a3c1ae08a8b8fdd884fdc5092031ea8ce53333e62976')),
|
||||
("aes256_cbc", 32, 16, True, unhex('381cbb2fbcc48118d0094540242bd990dd6af5b9a9890edd013d5cad2d904f34b9261c623a452f32ea60e5402919a77165df12862742f1059f8c4a862f0827c5')),
|
||||
("aes256_cbc_hw", 32, 16, True, unhex('381cbb2fbcc48118d0094540242bd990dd6af5b9a9890edd013d5cad2d904f34b9261c623a452f32ea60e5402919a77165df12862742f1059f8c4a862f0827c5')),
|
||||
("aes256_cbc_sw", 32, 16, True, unhex('381cbb2fbcc48118d0094540242bd990dd6af5b9a9890edd013d5cad2d904f34b9261c623a452f32ea60e5402919a77165df12862742f1059f8c4a862f0827c5')),
|
||||
("aes192_ctr", 24, 16, False, unhex('06bcfa7ccf075d723e12b724695a571a0fad67c56287ea609c410ac12749c51bb96e27fa7e1c7ea3b14792bbbb8856efb0617ebec24a8e4a87340d820cf347b8')),
|
||||
("aes192_ctr_hw", 24, 16, False, unhex('06bcfa7ccf075d723e12b724695a571a0fad67c56287ea609c410ac12749c51bb96e27fa7e1c7ea3b14792bbbb8856efb0617ebec24a8e4a87340d820cf347b8')),
|
||||
("aes192_ctr_sw", 24, 16, False, unhex('06bcfa7ccf075d723e12b724695a571a0fad67c56287ea609c410ac12749c51bb96e27fa7e1c7ea3b14792bbbb8856efb0617ebec24a8e4a87340d820cf347b8')),
|
||||
("aes192_cbc", 24, 16, True, unhex('ac97f8698170f9c05341214bd7624d5d2efef8311596163dc597d9fe6c868971bd7557389974612cbf49ea4e7cc6cc302d4cc90519478dd88a4f09b530c141f3')),
|
||||
("aes192_cbc_hw", 24, 16, True, unhex('ac97f8698170f9c05341214bd7624d5d2efef8311596163dc597d9fe6c868971bd7557389974612cbf49ea4e7cc6cc302d4cc90519478dd88a4f09b530c141f3')),
|
||||
("aes192_cbc_sw", 24, 16, True, unhex('ac97f8698170f9c05341214bd7624d5d2efef8311596163dc597d9fe6c868971bd7557389974612cbf49ea4e7cc6cc302d4cc90519478dd88a4f09b530c141f3')),
|
||||
("aes128_ctr", 16, 16, False, unhex('0ad4ddfd2360ec59d77dcb9a981f92109437c68c5e7f02f92017d9f424f89ab7850473ac0e19274125e740f252c84ad1f6ad138b6020a03bdaba2f3a7378ce1e')),
|
||||
("aes128_ctr_hw", 16, 16, False, unhex('0ad4ddfd2360ec59d77dcb9a981f92109437c68c5e7f02f92017d9f424f89ab7850473ac0e19274125e740f252c84ad1f6ad138b6020a03bdaba2f3a7378ce1e')),
|
||||
("aes128_ctr_sw", 16, 16, False, unhex('0ad4ddfd2360ec59d77dcb9a981f92109437c68c5e7f02f92017d9f424f89ab7850473ac0e19274125e740f252c84ad1f6ad138b6020a03bdaba2f3a7378ce1e')),
|
||||
("aes128_cbc", 16, 16, True, unhex('36de36917fb7955a711c8b0bf149b29120a77524f393ae3490f4ce5b1d5ca2a0d7064ce3c38e267807438d12c0e40cd0d84134647f9f4a5b11804a0cc5070e62')),
|
||||
("aes128_cbc_hw", 16, 16, True, unhex('36de36917fb7955a711c8b0bf149b29120a77524f393ae3490f4ce5b1d5ca2a0d7064ce3c38e267807438d12c0e40cd0d84134647f9f4a5b11804a0cc5070e62')),
|
||||
("aes128_cbc_sw", 16, 16, True, unhex('36de36917fb7955a711c8b0bf149b29120a77524f393ae3490f4ce5b1d5ca2a0d7064ce3c38e267807438d12c0e40cd0d84134647f9f4a5b11804a0cc5070e62')),
|
||||
("blowfish_ctr", 32, 8, False, unhex('079daf0f859363ccf72e975764d709232ec48adc74f88ccd1f342683f0bfa89ca0e8dbfccc8d4d99005d6b61e9cc4e6eaa2fd2a8163271b94bf08ef212129f01')),
|
||||
("blowfish_ssh2", 16, 8, True, unhex('e986b7b01f17dfe80ee34cac81fa029b771ec0f859ae21ae3ec3df1674bc4ceb54a184c6c56c17dd2863c3e9c068e76fd9aef5673465995f0d648b0bb848017f')),
|
||||
("blowfish_ssh1", 32, 8, True, unhex('d44092a9035d895acf564ba0365d19570fbb4f125d5a4fd2a1812ee6c8a1911a51bb181fbf7d1a261253cab71ee19346eb477b3e7ecf1d95dd941e635c1a4fbf')),
|
||||
@ -1517,36 +1513,37 @@ class crypt(MyTestBase):
|
||||
("arcfour128", 16, None, False, unhex('fd4af54c5642cb29629e50a15d22e4944e21ffba77d0543b27590eafffe3886686d1aefae0484afc9e67edc0e67eb176bbb5340af1919ea39adfe866d066dd05')),
|
||||
]
|
||||
|
||||
for alg, keylen, ivlen, simple_cbc, c in ciphers:
|
||||
cipher = ssh_cipher_new(alg)
|
||||
if cipher is None:
|
||||
continue # hardware-accelerated cipher not available
|
||||
for algbase, keylen, ivlen, simple_cbc, c in ciphers:
|
||||
for alg in get_implementations(algbase):
|
||||
cipher = ssh_cipher_new(alg)
|
||||
if cipher is None:
|
||||
continue # hardware-accelerated cipher not available
|
||||
|
||||
ssh_cipher_setkey(cipher, k[:keylen])
|
||||
if ivlen is not None:
|
||||
ssh_cipher_setiv(cipher, iv[:ivlen])
|
||||
self.assertEqualBin(ssh_cipher_encrypt(cipher, p), c)
|
||||
|
||||
ssh_cipher_setkey(cipher, k[:keylen])
|
||||
if ivlen is not None:
|
||||
ssh_cipher_setiv(cipher, iv[:ivlen])
|
||||
self.assertEqualBin(ssh_cipher_decrypt(cipher, c), p)
|
||||
|
||||
if simple_cbc:
|
||||
# CBC ciphers (other than the three-layered CBC used
|
||||
# by SSH-1 3DES) have more specific semantics for
|
||||
# their IV than 'some kind of starting state for the
|
||||
# cipher mode': the IV is specifically supposed to
|
||||
# represent the previous block of ciphertext. So we
|
||||
# can check that, by supplying the IV _as_ a
|
||||
# ciphertext block via a call to decrypt(), and seeing
|
||||
# if that causes our test ciphertext to decrypt the
|
||||
# same way as when we provided the same IV via
|
||||
# setiv().
|
||||
ssh_cipher_setkey(cipher, k[:keylen])
|
||||
ssh_cipher_decrypt(cipher, iv[:ivlen])
|
||||
if ivlen is not None:
|
||||
ssh_cipher_setiv(cipher, iv[:ivlen])
|
||||
self.assertEqualBin(ssh_cipher_encrypt(cipher, p), c)
|
||||
|
||||
ssh_cipher_setkey(cipher, k[:keylen])
|
||||
if ivlen is not None:
|
||||
ssh_cipher_setiv(cipher, iv[:ivlen])
|
||||
self.assertEqualBin(ssh_cipher_decrypt(cipher, c), p)
|
||||
|
||||
if simple_cbc:
|
||||
# CBC ciphers (other than the three-layered CBC used
|
||||
# by SSH-1 3DES) have more specific semantics for
|
||||
# their IV than 'some kind of starting state for the
|
||||
# cipher mode': the IV is specifically supposed to
|
||||
# represent the previous block of ciphertext. So we
|
||||
# can check that, by supplying the IV _as_ a
|
||||
# ciphertext block via a call to decrypt(), and seeing
|
||||
# if that causes our test ciphertext to decrypt the
|
||||
# same way as when we provided the same IV via
|
||||
# setiv().
|
||||
ssh_cipher_setkey(cipher, k[:keylen])
|
||||
ssh_cipher_decrypt(cipher, iv[:ivlen])
|
||||
self.assertEqualBin(ssh_cipher_decrypt(cipher, c), p)
|
||||
|
||||
def testRSAKex(self):
|
||||
# Round-trip test of the RSA key exchange functions, plus a
|
||||
# hardcoded plain/ciphertext pair to guard against the
|
||||
@ -2324,7 +2321,7 @@ Private-MAC: 5b1f6f4cc43eb0060d2c3e181bc0129343adba2b
|
||||
class standard_test_vectors(MyTestBase):
|
||||
def testAES(self):
|
||||
def vector(cipher, key, plaintext, ciphertext):
|
||||
for suffix in "hw", "sw":
|
||||
for suffix in get_aes_impls():
|
||||
c = ssh_cipher_new("{}_{}".format(cipher, suffix))
|
||||
if c is None: return # skip test if HW AES not available
|
||||
ssh_cipher_setkey(c, key)
|
||||
@ -2540,7 +2537,7 @@ class standard_test_vectors(MyTestBase):
|
||||
unhex('56be34521d144c88dbb8c733f0e8b3f6'))
|
||||
|
||||
def testSHA1(self):
|
||||
for hashname in ['sha1_sw', 'sha1_hw']:
|
||||
for hashname in get_implementations("sha1"):
|
||||
if ssh_hash_new(hashname) is None:
|
||||
continue # skip testing of unavailable HW implementation
|
||||
|
||||
@ -2577,7 +2574,7 @@ class standard_test_vectors(MyTestBase):
|
||||
"cb0082c8f197d260991ba6a460e76e202bad27b3"))
|
||||
|
||||
def testSHA256(self):
|
||||
for hashname in ['sha256_sw', 'sha256_hw']:
|
||||
for hashname in get_implementations("sha256"):
|
||||
if ssh_hash_new(hashname) is None:
|
||||
continue # skip testing of unavailable HW implementation
|
||||
|
||||
@ -2621,7 +2618,7 @@ class standard_test_vectors(MyTestBase):
|
||||
"8ad3361763f7e9b2d95f4f0da6e1ccbc"))
|
||||
|
||||
def testSHA384(self):
|
||||
for hashname in ['sha384_sw', 'sha384_hw']:
|
||||
for hashname in get_implementations("sha384"):
|
||||
if ssh_hash_new(hashname) is None:
|
||||
continue # skip testing of unavailable HW implementation
|
||||
|
||||
@ -2663,7 +2660,7 @@ class standard_test_vectors(MyTestBase):
|
||||
'38e42b5c4de660f5de8fb2a5b2fbd2a3cbffd20cff1288c0'))
|
||||
|
||||
def testSHA512(self):
|
||||
for hashname in ['sha512_sw', 'sha512_hw']:
|
||||
for hashname in get_implementations("sha512"):
|
||||
if ssh_hash_new(hashname) is None:
|
||||
continue # skip testing of unavailable HW implementation
|
||||
|
||||
|
Reference in New Issue
Block a user