Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/cryptography/hazmat/primitives/serialization/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ def _bcrypt_kdf(
_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521"
_CERT_SUFFIX = b"-cert-v01@openssh.com"

# U2F application string suffixed pubkey
_SK_SSH_ED25519 = b"sk-ssh-ed25519@openssh.com"
_SK_SSH_ECDSA_NISTP256 = b"sk-ecdsa-sha2-nistp256@openssh.com"

# These are not key types, only algorithms, so they cannot appear
# as a public key type
_SSH_RSA_SHA256 = b"rsa-sha2-256"
Expand Down Expand Up @@ -572,13 +576,65 @@ def encode_private(
f_priv.put_sshstr(f_keypair)


def load_application(data) -> tuple[memoryview, memoryview]:
"""
U2F application strings
"""
application, data = _get_sshstr(data)
if not application.tobytes().startswith(b"ssh:"):
raise ValueError(
"U2F application string does not start with b'ssh:' "
f"({application})"
)
return application, data


class _SSHFormatSKEd25519:
"""
The format of a sk-ssh-ed25519@openssh.com public key is:

string "sk-ssh-ed25519@openssh.com"
string public key
string application (user-specified, but typically "ssh:")
"""

def load_public(
self, data: memoryview
) -> tuple[ed25519.Ed25519PublicKey, memoryview]:
"""Make Ed25519 public key from data."""
public_key, data = _lookup_kformat(_SSH_ED25519).load_public(data)
application, data = load_application(data)
return public_key, data


class _SSHFormatSKECDSA:
"""
The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:

string "sk-ecdsa-sha2-nistp256@openssh.com"
string curve name
ec_point Q
string application (user-specified, but typically "ssh:")
"""

def load_public(
self, data: memoryview
) -> tuple[ec.EllipticCurvePublicKey, memoryview]:
"""Make Ed25519 public key from data."""
public_key, data = _lookup_kformat(_ECDSA_NISTP256).load_public(data)
application, data = load_application(data)
return public_key, data


_KEY_FORMATS = {
_SSH_RSA: _SSHFormatRSA(),
_SSH_DSA: _SSHFormatDSA(),
_SSH_ED25519: _SSHFormatEd25519(),
_ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()),
_ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()),
_ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()),
_SK_SSH_ED25519: _SSHFormatSKEd25519(),
_SK_SSH_ECDSA_NISTP256: _SSHFormatSKECDSA(),
}


Expand Down
33 changes: 29 additions & 4 deletions tests/hazmat/primitives/test_ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ class TestOpenSSHSerialization:
("ecdsa-nopsw.key.pub", "ecdsa-nopsw.key-cert.pub"),
("ed25519-psw.key.pub", None),
("ed25519-nopsw.key.pub", "ed25519-nopsw.key-cert.pub"),
("sk-ecdsa-psw.key.pub", None),
("sk-ecdsa-nopsw.key.pub", None),
("sk-ed25519-psw.key.pub", None),
("sk-ed25519-nopsw.key.pub", None),
],
)
def test_load_ssh_public_key(self, key_file, cert_file, backend):
Expand All @@ -80,10 +84,14 @@ def test_load_ssh_public_key(self, key_file, cert_file, backend):
)
else:
public_key = load_ssh_public_key(pub_data, backend)
assert (
public_key.public_bytes(Encoding.OpenSSH, PublicFormat.OpenSSH)
== nocomment_data
)
if not key_file.startswith("sk-"):
# SK keys do not round-trip
assert (
public_key.public_bytes(
Encoding.OpenSSH, PublicFormat.OpenSSH
)
== nocomment_data
)

self.run_partial_pubkey(pub_data, backend)

Expand Down Expand Up @@ -1800,3 +1808,20 @@ def test_sign_and_byte_compare_ed25519(self, monkeypatch, backend):
b"t8yRa8IRbxvOyA9TZYDGG1dRE3DiR0fuudU20v6vqfTd1gx0S5QyEdECXLl9ZI3"
b"AwZgc="
)


class TestSSHSK:
@staticmethod
def ssh_str(application):
data = (
len(application).to_bytes(length=4, byteorder="big")
+ application.encode()
)
return memoryview(data)

def test_load_application(self):
ssh.load_application(self.ssh_str("ssh:test"))

def test_load_application_valueerror(self):
with pytest.raises(ValueError):
ssh.load_application(self.ssh_str("hss:test"))
6 changes: 5 additions & 1 deletion vectors/cryptography_vectors/asymmetric/OpenSSH/gen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ getecbits() {
genkey() {
fn="$1"
args="-f $fn -C $fn"
sk="-O application=ssh:the-application-string"
case "$fn" in
sk-ecdsa-*) args="$args -t ecdsa-sk -b $(getecbits) $sk" ;;
ecdsa-*) args="$args -t ecdsa -b $(getecbits)" ;;
rsa-*) args="$args -t rsa" ;;
dsa-*) args="$args -t dsa" ;;
sk-ed25519-*) args="$args -t ed25519-sk $sk" ;;
ed25519-*) args="$args -t ed25519" ;;
esac
password=''
Expand All @@ -33,12 +36,13 @@ genkey() {
}

# generate private key files
for ktype in rsa dsa ecdsa ed25519; do
for ktype in rsa dsa ecdsa sk-ecdsa ed25519 sk-ed25519; do
for psw in nopsw psw; do
genkey "${ktype}-${psw}.key"
done
done


# generate public key files
for fn in *.key; do
ssh-keygen -q -y -f "$fn" > /dev/null
Expand Down
11 changes: 11 additions & 0 deletions vectors/cryptography_vectors/asymmetric/OpenSSH/sk-ecdsa-nopsw.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlQAAACJzay1lY2
RzYS1zaGEyLW5pc3RwMjU2QG9wZW5zc2guY29tAAAACG5pc3RwMjU2AAAAQQQ7XunI8QRf
myT0PKWJXtaE0lA6+Hy5HTfIDfHexsZV68AGAj0nYyf2+mAK/vPp6IyVBALJqdzdJYiyeX
p/3neLAAAAGnNzaDp0aGUtYXBwbGljYXRpb24tc3RyaW5nAAABAOGdI7jhnSO4AAAAInNr
LWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBDte6c
jxBF+bJPQ8pYle1oTSUDr4fLkdN8gN8d7GxlXrwAYCPSdjJ/b6YAr+8+nojJUEAsmp3N0l
iLJ5en/ed4sAAAAac3NoOnRoZS1hcHBsaWNhdGlvbi1zdHJpbmcBAAAAQDkL+WvhalaEJi
Lf/MaFsFeYzwvC06GZVqUXgCnzyutZzMB9a1deF9uFke1ib56tgZR9iVsskIJeWuwiAIg0
es4AAAAAAAAAEnNrLWVjZHNhLW5vcHN3LmtleQECAwQ=
-----END OPENSSH PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBDte6cjxBF+bJPQ8pYle1oTSUDr4fLkdN8gN8d7GxlXrwAYCPSdjJ/b6YAr+8+nojJUEAsmp3N0liLJ5en/ed4sAAAAac3NoOnRoZS1hcHBsaWNhdGlvbi1zdHJpbmc= sk-ecdsa-nopsw.key
12 changes: 12 additions & 0 deletions vectors/cryptography_vectors/asymmetric/OpenSSH/sk-ecdsa-psw.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDIj2qUG3
LdljUMp0/4zuFuAAAAEAAAAAEAAACVAAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3Bl
bnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBACdJuKxgDLk+a1NeeCtRqCropd0hXume/cTdO
vV/B4lmupr9viNQsUT09wbKRflnOc9jxPAiQOzZbXTkmnV8kkAAAAac3NoOnRoZS1hcHBs
aWNhdGlvbi1zdHJpbmcAAAEAO6Vsfb59XIe524NKbXMjA0xleAi3lcZ5EF0dF48yRO2LfA
12B948LzsKOrgo+Cdq7BMLkCCA1z2811yvKtvy/7cR3D/p31cW7VEun4OAn+QoPCHmv25r
WVfUAv5PC5Ofdm7dtExTcMmyNUMcziovirTyhnlpc/wHD+wgp2oQGpcm+rjQlqX96cLJ7H
PM3wls38biP3wh2QWkoKWPyq7tMR4PiJOw9h6YNeZY3M1JnC9b2b0iHD6Ra/5LBBqV/Uyu
irkHWLB7ASchamexxRqu4fLFK4tjijhLV8hc/XLsQGeDNBHf4QSvZJP0usSSP37F1Ai+XM
stjM1iCsk1UEV9aA==
-----END OPENSSH PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBACdJuKxgDLk+a1NeeCtRqCropd0hXume/cTdOvV/B4lmupr9viNQsUT09wbKRflnOc9jxPAiQOzZbXTkmnV8kkAAAAac3NoOnRoZS1hcHBsaWNhdGlvbi1zdHJpbmc= sk-ecdsa-psw.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAYAAAABpzay1zc2
gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAACB6auRr7BwVOqTawgDOxUpaUFcN8SZ7SWzoR2Vs
ubbk3wAAABpzc2g6dGhlLWFwcGxpY2F0aW9uLXN0cmluZwAAARCWIPLyliDy8gAAABpzay
1zc2gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAACB6auRr7BwVOqTawgDOxUpaUFcN8SZ7SWzo
R2Vsubbk3wAAABpzc2g6dGhlLWFwcGxpY2F0aW9uLXN0cmluZwEAAACAQPv/aY2F3YN1kD
1FHPa1HpEHOGAbsYj/2b6h8Rn+N4pU6hdTD5v19Efdz5jlt8Y84c61+8HKDPCI/g5Cbcvd
3uuGHuFUdgiarOZqKyuwBj3Kll9Whb/yV4wGo/NVXtCHa2SnWr2wjYtRTGPNNCgGPsLU05
/KTNCStsNhEcsNDjEAAAAAAAAAFHNrLWVkMjU1MTktbm9wc3cua2V5AQIDBAUGBw==
-----END OPENSSH PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIHpq5GvsHBU6pNrCAM7FSlpQVw3xJntJbOhHZWy5tuTfAAAAGnNzaDp0aGUtYXBwbGljYXRpb24tc3RyaW5n sk-ed25519-nopsw.key
11 changes: 11 additions & 0 deletions vectors/cryptography_vectors/asymmetric/OpenSSH/sk-ed25519-psw.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBZQIE5S+
fq0J5esB3Jo4smAAAAEAAAAAEAAABgAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29t
AAAAIHf0iiNQTiR7NNAbeAwY+READVx9G0mP6idSAZ7bPTrMAAAAGnNzaDp0aGUtYXBwbG
ljYXRpb24tc3RyaW5nAAABEEeyENyjnVry24AKkT0cC6nRakzHeBY7nSmDiy3MX7sQNRze
illy4uWLZyv022QlMR4GqnXwnQ9bPqcPD0S/SAhuYnFRWI6PPUXkNqiqiS/ZsMkaSKDvBS
UKv5EXjBBk3Sh9IjNXXK8tt0+WIIR973hVEtolcgxvFZpc1IJuRl9gkpKlQFNzwcANTuwB
kr6t0qad/fp0bZldBL/zRtqfgMHTSFzNoITTaxA8ZQZ1Zm585u0NIX4ZDrTaoZVaO8t7Z5
3r1784oCk6h/lomf9Qsg2eBf6CHMGlTHVFPop5VtGDKFVlgIxQCdwt0V1e6dWK6j5zOzBh
mNA7qT0q3quRLBqUADN698q5fLRFR1PzQ5bx
-----END OPENSSH PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIHf0iiNQTiR7NNAbeAwY+READVx9G0mP6idSAZ7bPTrMAAAAGnNzaDp0aGUtYXBwbGljYXRpb24tc3RyaW5n sk-ed25519-psw.key