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:
parent
254635a2a1
commit
36d40febed
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user