1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-04-10 07:38:06 -05:00

Add cryptsuite test of certificate handling.

This uses the test-CA code to construct a series of certificates with
various properties so as to check all the error cases of certificate
validation. It also tests the various different key types, and all the
RSA signature flags on both the certified key and the certifying one.
This commit is contained in:
Simon Tatham 2022-04-25 13:27:53 +01:00
parent 254635a2a1
commit 36d40febed

View File

@ -18,6 +18,7 @@ except ImportError:
from eccref import *
from testcrypt import *
from ssh import *
from ca import CertType, make_signature_preimage, sign_cert_via_testcrypt
assert sys.version_info[:2] >= (3,0), "This is Python 3 code"
@ -2557,6 +2558,223 @@ Private-MAC: 5b1f6f4cc43eb0060d2c3e181bc0129343adba2b
self.assertEqual(rsa1_save_sb(k2, comment, pp),
input_encrypted_key)
def testOpenSSHCert(self):
def per_base_keytype_tests(alg, run_validation_tests=False,
ca_signflags=None):
cert_pub = sign_cert_via_testcrypt(
make_signature_preimage(
key_to_certify = base_key.public_blob(),
ca_key = ca_key,
certtype = CertType.user,
keyid = b'id',
serial = 111,
principals = [b'username'],
valid_after = 1000,
valid_before = 2000), ca_key, signflags=ca_signflags)
certified_key = ssh_key_new_priv(alg + '-cert', cert_pub,
base_key.private_blob())
# Check the simple certificate methods
self.assertEqual(certified_key.cert_id_string(), b'id')
self.assertEqual(certified_key.ca_public_blob(),
ca_key.public_blob())
recovered_base_key = certified_key.base_key()
self.assertEqual(recovered_base_key.public_blob(),
base_key.public_blob())
self.assertEqual(recovered_base_key.private_blob(),
base_key.private_blob())
# Check that an ordinary key also supports base_key()
redundant_base_key = base_key.base_key()
self.assertEqual(redundant_base_key.public_blob(),
base_key.public_blob())
self.assertEqual(redundant_base_key.private_blob(),
base_key.private_blob())
# Test signing and verifying using the certified key type
test_string = b'hello, world'
base_sig = base_key.sign(test_string, 0)
certified_sig = certified_key.sign(test_string, 0)
self.assertEqual(base_sig, certified_sig)
self.assertEqual(certified_key.verify(base_sig, test_string), True)
# Check a successful certificate verification
result, err = certified_key.check_cert(False, b'username', 1000)
self.assertEqual(result, True)
# That's the end of the tests we need to repeat for all
# the key types. Now we move on to detailed tests of the
# validation, which are independent of key type, so we
# only need to test this part once.
if not run_validation_tests:
return
# Check cert verification at the other end of the valid
# time range
result, err = certified_key.check_cert(False, b'username', 1999)
self.assertEqual(result, True)
# Oops, wrong certificate type
result, err = certified_key.check_cert(True, b'username', 1000)
self.assertEqual(result, False)
self.assertEqual(err, b'Certificate type is user; expected host')
# Oops, wrong username
result, err = certified_key.check_cert(False, b'someoneelse', 1000)
self.assertEqual(result, False)
self.assertEqual(err, b'Certificate\'s username list ["username"] '
b'does not contain expected username "someoneelse"')
# Oops, time is wrong. (But we can't check the full error
# message including the translated start/end times, because
# those vary with LC_TIME.)
result, err = certified_key.check_cert(False, b'someoneelse', 999)
self.assertEqual(result, False)
self.assertEqual(err[:30], b'Certificate is not valid until')
result, err = certified_key.check_cert(False, b'someoneelse', 2000)
self.assertEqual(result, False)
self.assertEqual(err[:22], b'Certificate expired at')
# Modify the certificate so that the signature doesn't validate
username_position = cert_pub.index(b'username')
bytelist = list(cert_pub)
bytelist[username_position] ^= 1
miscertified_key = ssh_key_new_priv(alg + '-cert', bytes(bytelist),
base_key.private_blob())
result, err = miscertified_key.check_cert(False, b'username', 1000)
self.assertEqual(result, False)
self.assertEqual(err, b"Certificate's signature is invalid")
# Make a certificate containing a critical option, to test we
# reject it
cert_pub = sign_cert_via_testcrypt(
make_signature_preimage(
key_to_certify = base_key.public_blob(),
ca_key = ca_key,
certtype = CertType.user,
keyid = b'id',
serial = 112,
principals = [b'username'],
critical_options = {b'unknown-option': b'yikes!'}), ca_key)
certified_key = ssh_key_new_priv(alg + '-cert', cert_pub,
base_key.private_blob())
result, err = certified_key.check_cert(False, b'username', 1000)
self.assertEqual(result, False)
self.assertEqual(err, b'Certificate specifies an unsupported '
b'critical option "unknown-option"')
# Make a certificate containing a non-critical extension, to
# test we _accept_ it
cert_pub = sign_cert_via_testcrypt(
make_signature_preimage(
key_to_certify = base_key.public_blob(),
ca_key = ca_key,
certtype = CertType.user,
keyid = b'id',
serial = 113,
principals = [b'username'],
extensions = {b'unknown-ext': b'whatever, dude'}), ca_key)
certified_key = ssh_key_new_priv(alg + '-cert', cert_pub,
base_key.private_blob())
result, err = certified_key.check_cert(False, b'username', 1000)
self.assertEqual(result, True)
# Now try a host certificate. We don't need to do _all_ the
# checks over again, but at least make sure that setting
# CertType.host leads to the certificate validating with
# host=True and not with host=False.
#
# Also, in this test, give two hostnames.
cert_pub = sign_cert_via_testcrypt(
make_signature_preimage(
key_to_certify = base_key.public_blob(),
ca_key = ca_key,
certtype = CertType.host,
keyid = b'id',
serial = 114,
principals = [b'hostname.example.com',
b'hostname2.example.com'],
valid_after = 1000,
valid_before = 2000), ca_key)
certified_key = ssh_key_new_priv(alg + '-cert', cert_pub,
base_key.private_blob())
# Check certificate type
result, err = certified_key.check_cert(
True, b'hostname.example.com', 1000)
self.assertEqual(result, True)
result, err = certified_key.check_cert(
False, b'hostname.example.com', 1000)
self.assertEqual(result, False)
self.assertEqual(err, b'Certificate type is host; expected user')
# Check the second hostname and an unknown one
result, err = certified_key.check_cert(
True, b'hostname2.example.com', 1000)
self.assertEqual(result, True)
result, err = certified_key.check_cert(
True, b'hostname3.example.com', 1000)
self.assertEqual(result, False)
self.assertEqual(err, b'Certificate\'s hostname list ['
b'"hostname.example.com", "hostname2.example.com"] '
b'does not contain expected hostname '
b'"hostname3.example.com"')
# And just for luck, try a totally unknown certificate type,
# making sure that it's rejected in both modes and gives the
# right error message
cert_pub = sign_cert_via_testcrypt(
make_signature_preimage(
key_to_certify = base_key.public_blob(),
ca_key = ca_key,
certtype = 12345,
keyid = b'id',
serial = 114,
principals = [b'username', b'hostname.example.com'],
valid_after = 1000,
valid_before = 2000), ca_key)
certified_key = ssh_key_new_priv(alg + '-cert', cert_pub,
base_key.private_blob())
result, err = certified_key.check_cert(
False, b'username', 1000)
self.assertEqual(result, False)
self.assertEqual(err, b'Certificate type is unknown value 12345; '
b'expected user')
result, err = certified_key.check_cert(
True, b'hostname.example.com', 1000)
self.assertEqual(result, False)
self.assertEqual(err, b'Certificate type is unknown value 12345; '
b'expected host')
ca_key = ssh_key_new_priv('ed25519', b64('AAAAC3NzaC1lZDI1NTE5AAAAIMUJEFAmSV/qtoxSmVOHUgTMKYjqkDy8fTfsfCKV+sN7'), b64('AAAAIK4STyaf63xHidqhvUop9/OKiYqSh/YEWLCp1lL5Vs4u'))
base_key = ssh_key_new_priv('ed25519', b64('AAAAC3NzaC1lZDI1NTE5AAAAIMt0/CMBL+64GQ/r/JyGxo6oHs86i9bOHhMJYbDbxEJf'), b64('AAAAIB38jy02ZWYb4EXrJG9RIljEhqidrG5DdhZvMvoeOTZs'))
per_base_keytype_tests('ed25519', run_validation_tests=True)
base_key = ssh_key_new_priv('p256', b64('AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGc8VXplXScdWckJgAw6Hag5PP7g0JEVdLY5lP2ujvVxU5GwwquYLbX3yyj1zY5h2n9GoXrnRxzR5+5g8wsNjTA='), b64('AAAAICVRicPD5MyOHfKdnC/8IP84t+nQ4bqmMUyX7NHyCKjS'))
per_base_keytype_tests('p256')
base_key = ssh_key_new_priv('p384', b64('AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLITujAbKwHDEzVDFqWtA+CleAhN/Y+53mHbEoTpU0aof9L+2lHeUshXdxHDLxY69wO5+WfqWJCwSY58PuXIZzIisQkvIKq6LhpzK6C5JpWJ8Kbv7su+qZPf5sYoxx0xZg=='), b64('AAAAMHyQTQYcIA/bR4ZvWS86ohb5Lu0MhzjD8bUb3q8jnROOe3BrE9I8oJcx+l1lddPouA=='))
per_base_keytype_tests('p384')
base_key = ssh_key_new_priv('p521', b64('AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADButwMRGdLkFhWcSDsLhRhgyrLQq1/A0M8x4GgEmesh4iydo4tGKZR14GhHvx150IWTE1Tre4wyH+1FsTfAlpUBgBDQjsZE0D3u3SLp4qjjhzyrJGhEUDd9J6lsr6JrXbTefz5+LkM9m5l86y9PoAgT+F25OiTYlfvR5qx/pzIPoCnpA=='), b64('AAAAQgFV8xBXC7XZNxdW1oWg6yCZjys2AX4beZVehE9A2R/4m11dHnfqoE1FzbRxj9xqwKvHZRhMOJ//DYuhtcG6+6yHsA=='))
per_base_keytype_tests('p521')
base_key = ssh_key_new_priv('dsa', b64('AAAAB3NzaC1kc3MAAABCAXgDrF9Fw/Ty+QcoljAGjGL/Ph5+NBQqUYADm4wxF+aazjQXLuZ0VW9OdYBisgDZlYDj/w7y9NxCBgax2BSkhDNxAAAAFQC/YwnFzcom6cRRHPXtOUDLi2I29QAAAEIAqGOUYpfFPwzhgAmYXwWKdK8ouSUplNE29FOpv6NYjyf7k+tLSWF3b8oZdtw6XP8lr4vcKXC9Ik0YpKYKM7iKfb8AAABCAUDCcojlDLQmLHg8HhFCtT/CpayNh4OfmSrP8XOwJnFD/eBaSGuPB5EvGd+m6gr+Pc0RSAlWP1aIzUbYkQ33Yk58'), b64('AAAAFQChVuOTNrCwLSJygxlRQhDwHozwSg=='))
per_base_keytype_tests('dsa')
base_key = ssh_key_new_priv('rsa', b64('AAAAB3NzaC1yc2EAAAADAQABAAAAgQDXLnqGPQLL9byoHFQWPiF5Uzcd0KedMRRJmuwyCAWprlh8EN43mL2F7q27Uv54m/ztqW4DsVtiCN6cDYvB9QPNYFR5npwsEAJ06Ro4s9ZpFsZVOvitqeoYIs+jkS8vq5V8X4hwLlJ8vXYPD6rHJhOz6HFpImHmVu40Mu5lq+MCQQ=='), b64('AAAAgH5dBwrJzVilKHK4oBCnz9SFr7pMjAHdjoJi/g2rdFfe0IubBEQ16CY8sb1t0Y5WXEPc2YRFpNp/RurxcX8nOWFPzgNJXEtkKpKO9Juqu5hL4xcf8QKC2aJFk3EXrn/M6dXEdjqN4UhsT6iFTsHKU4b8T6VTtgKzwkOdic/YotaBAAAAQQD6liDTlzTKzLhbypI6l+y2BGA3Kkzz71Y2o7XH/6bZ6HJOFgHuJeL3eNQptzd8Q+ctfvR0fa2PItYydDOlVUeZAAAAQQDb1IsO1/fkflDZhPQT2XOxtrjgQhotKjr6CSmJtDNmo1mOCN+mOgxtDfJ0PNEEM1P9CO2Ia3njtkxt4Ep2EpjpAAAAQQClRxLEHsRK9nMPZ4HW45iyw5dHhYar9pYUql2VnixWQxrHy13ZIaWxi6xwWjuPglrdBgEQfYwH9KGmlFmZXT/Z'))
per_base_keytype_tests('rsa')
# Now switch to an RSA certifying key, and test different RSA
# signature subtypes being used to sign the certificate
ca_key = ssh_key_new_priv('rsa', b64('AAAAB3NzaC1yc2EAAAADAQABAAAAgQCKHiavhtnAZQLUPtYlzlQmVTHSKq2ChCKZP0cLNtN2YSS0/f4D1hi8W04Qh/JuSXZAdUThTAVjxDmxpiOMNwa/2WDXMuqip47dzZSQxtSdvTfeL9TVC/M1NaOzy8bqFx6pzi37zPATETT4PP1Zt/Pd23ZJYhwjxSyTlqj7529v0w=='), b64('AAAAgCwTZyEIlaCyG28EBm7WI0CAW3/IIsrNxATHjrJjcqQKaB5iF5e90PL66DSaTaEoTFZRlgOXsPiffBHXBO0P+lTyZ2jlq2J2zgeofRH3Yong4BT4xDtqBKtxixgC1MAHmrOnRXjAcDUiLxIGgU0YKSv0uAlgARsUwDsk0GEvK+jBAAAAQQDMi7liRBQ4/Z6a4wDL/rVnIJ9x+2h2UPK9J8U7f97x/THIBtfkbf9O7nDP6onValuSr86tMR24DJZsEXaGPwjDAAAAQQCs3J3D3jNVwwk16oySRSjA5x3tKCEITYMluyXX06cvFew8ldgRCYl1sh8RYAfbBKXhnJD77qIxtVNaF1yl/guxAAAAQFTRdKRUF2wLu/K/Rr34trwKrV6aW0GWyHlLuWvF7FUB85aDmtqYI2BSk92mVCKHBNw2T3cJMabN9JOznjtADiM='))
per_base_keytype_tests('rsa')
per_base_keytype_tests('rsa', ca_signflags=2)
per_base_keytype_tests('rsa', ca_signflags=4)
class standard_test_vectors(MyTestBase):
def testAES(self):
def vector(cipher, key, plaintext, ciphertext):