1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
from pathlib import Path
from unittest.mock import patch, call
from pytest import raises
from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey
from paramiko import (
PKey,
Ed25519Key,
RSAKey,
UnknownKeyType,
Message,
PublicBlob,
)
from ._util import _support
class PKey_:
# NOTE: this is incidentally tested by a number of other tests, such as the
# agent.py test suite
class from_type_string:
def loads_from_type_and_bytes(self, keys):
obj = PKey.from_type_string(keys.full_type, keys.pkey.asbytes())
assert obj == keys.pkey
# TODO: exceptions
#
# TODO: passphrase? OTOH since this is aimed at the agent...irrelephant
class from_path:
def loads_from_Path(self, keys):
obj = PKey.from_path(keys.path)
assert obj == keys.pkey
def loads_from_str(self):
key = PKey.from_path(str(_support("rsa.key")))
assert isinstance(key, RSAKey)
@patch("paramiko.pkey.Path")
def expands_user(self, mPath):
# real key for guts that want a real key format
mykey = Path(_support("rsa.key"))
pathy = mPath.return_value.expanduser.return_value
# read_bytes for cryptography.io's loaders
pathy.read_bytes.return_value = mykey.read_bytes()
# open() for our own class loader
pathy.open.return_value = mykey.open()
# fake out exists() to avoid attempts to load cert
pathy.exists.return_value = False
PKey.from_path("whatever") # we're not testing expanduser itself
# Both key and cert paths
mPath.return_value.expanduser.assert_has_calls([call(), call()])
def raises_UnknownKeyType_for_unknown_types(self):
# I.e. a real, becomes a useful object via cryptography.io, key
# class that we do NOT support. Chose Ed448 randomly as OpenSSH
# doesn't seem to support it either, going by ssh-keygen...
keypath = _support("ed448.key")
with raises(UnknownKeyType) as exc:
PKey.from_path(keypath)
assert issubclass(exc.value.key_type, Ed448PrivateKey)
with open(keypath, "rb") as fd:
assert exc.value.key_bytes == fd.read()
def leaves_cryptography_exceptions_untouched(self):
# a Python file is not a private key!
with raises(ValueError):
PKey.from_path(__file__)
# TODO: passphrase support tested
class automatically_loads_certificates:
def existing_cert_loaded_when_given_key_path(self):
key = PKey.from_path(_support("rsa.key"))
# Public blob exists despite no .load_certificate call
assert key.public_blob is not None
assert (
key.public_blob.key_type == "ssh-rsa-cert-v01@openssh.com"
)
# And it's definitely the one we expected
assert key.public_blob == PublicBlob.from_file(
_support("rsa.key-cert.pub")
)
def can_be_given_cert_path_instead(self):
key = PKey.from_path(_support("rsa.key-cert.pub"))
# It's still a key, not a PublicBlob
assert isinstance(key, RSAKey)
# Public blob exists despite no .load_certificate call
assert key.public_blob is not None
assert (
key.public_blob.key_type == "ssh-rsa-cert-v01@openssh.com"
)
# And it's definitely the one we expected
assert key.public_blob == PublicBlob.from_file(
_support("rsa.key-cert.pub")
)
def no_cert_load_if_no_cert(self):
# This key exists (it's a copy of the regular one) but has no
# matching -cert.pub
key = PKey.from_path(_support("rsa-lonely.key"))
assert key.public_blob is None
def excepts_usefully_if_no_key_only_cert(self):
# TODO: is that truly an error condition? the cert is ~the
# pubkey and we still require the privkey for signing, yea?
# This cert exists (it's a copy of the regular one) but there's
# no rsa-missing.key to load.
with raises(FileNotFoundError) as info:
PKey.from_path(_support("rsa-missing.key-cert.pub"))
assert info.value.filename.endswith("rsa-missing.key")
class load_certificate:
def rsa_public_cert_blobs(self):
# Data to test signing with (arbitrary)
data = b"ice weasels"
# Load key w/o cert at first (so avoiding .from_path)
key = RSAKey.from_private_key_file(_support("rsa.key"))
assert key.public_blob is None
# Sign regular-style (using, arbitrarily, SHA2)
msg = key.sign_ssh_data(data, "rsa-sha2-256")
msg.rewind()
assert "rsa-sha2-256" == msg.get_text()
signed = msg.get_binary() # for comparison later
# Load cert and inspect its internals
key.load_certificate(_support("rsa.key-cert.pub"))
assert key.public_blob is not None
assert key.public_blob.key_type == "ssh-rsa-cert-v01@openssh.com"
assert key.public_blob.comment == "test_rsa.key.pub"
msg = Message(key.public_blob.key_blob)
# cert type
assert msg.get_text() == "ssh-rsa-cert-v01@openssh.com"
# nonce
msg.get_string()
# public numbers
assert msg.get_mpint() == key.public_numbers.e
assert msg.get_mpint() == key.public_numbers.n
# serial number
assert msg.get_int64() == 1234
# TODO: whoever wrote the OG tests didn't care about the remaining
# fields from
# https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys
# so neither do I, for now...
# Sign cert-style (still SHA256 - so this actually does almost
# exactly the same thing under the hood as the previous sign)
msg = key.sign_ssh_data(data, "rsa-sha2-256-cert-v01@openssh.com")
msg.rewind()
assert "rsa-sha2-256" == msg.get_text()
assert signed == msg.get_binary() # same signature as above
msg.rewind()
assert key.verify_ssh_sig(b"ice weasels", msg) # our data verified
def loading_cert_of_different_type_from_key_raises_ValueError(self):
edkey = Ed25519Key.from_private_key_file(_support("ed25519.key"))
err = "PublicBlob type ssh-rsa-cert-v01@openssh.com incompatible with key type ssh-ed25519" # noqa
with raises(ValueError, match=err):
edkey.load_certificate(_support("rsa.key-cert.pub"))
|