1919from cryptography .hazmat .backends import default_backend
2020from cryptography .hazmat .primitives .asymmetric .dsa import DSAParameterNumbers , DSAPublicNumbers
2121from cryptography .hazmat .primitives .asymmetric .rsa import RSAPublicNumbers
22+ from urllib .parse import urlparse
2223
2324import base64
2425import binascii
@@ -204,7 +205,7 @@ def _parse_long(cls, data):
204205 def _split_key (self , data ):
205206 options_raw = None
206207 # Terribly inefficient way to remove options, but hey, it works.
207- if not data .startswith ("ssh-" ) and not data .startswith ("ecdsa-" ):
208+ if not data .startswith ("ssh-" ) and not data .startswith ("ecdsa-" ) and not data . startswith ( "sk-" ) :
208209 quote_open = False
209210 for i , character in enumerate (data ):
210211 if character == '"' : # only double quotes are allowed, no need to care about single quotes
@@ -388,6 +389,32 @@ def _process_ed25516(self, data):
388389 raise InvalidKeyLengthError ("ed25519 keys must be 256 bits (was %s bits)" % self .bits )
389390 return current_position
390391
392+ def _validate_application_string (self , application ):
393+ """Validates Application string.
394+
395+ Has to be an URL starting with "ssh:". See ssh-keygen(1)."""
396+
397+ try :
398+ parsed_url = urlparse (application )
399+ except ValueError as error :
400+ raise InvalidKeyError ("Application string: %s" % error )
401+ if parsed_url .scheme != b"ssh" :
402+ raise InvalidKeyError ('Application string must begin with "ssh:"' )
403+
404+ def _process_sk_ecdsa_sha (self , data ):
405+ """Parses sk_ecdsa-sha public keys."""
406+ current_position = self ._process_ecdsa_sha (data )
407+ current_position , application = self ._unpack_by_int (data , current_position )
408+ self ._validate_application_string (application )
409+ return current_position
410+
411+ def _process_sk_ed25519 (self , data ):
412+ """Parses sk_ed25519 public keys."""
413+ current_position = self ._process_ed25516 (data )
414+ current_position , application = self ._unpack_by_int (data , current_position )
415+ self ._validate_application_string (application )
416+ return current_position
417+
391418 def _process_key (self , data ):
392419 if self .key_type == b"ssh-rsa" :
393420 return self ._process_ssh_rsa (data )
@@ -397,6 +424,10 @@ def _process_key(self, data):
397424 return self ._process_ecdsa_sha (data )
398425 if self .key_type == b"ssh-ed25519" :
399426 return self ._process_ed25516 (data )
427+ if self .key_type .strip ().startswith (b"sk-ecdsa-sha" ):
428+ return self ._process_sk_ecdsa_sha (data )
429+ if self .key_type .strip ().startswith (b"sk-ssh-ed25519" ):
430+ return self ._process_sk_ed25519 (data )
400431 raise NotImplementedError ("Invalid key type: %s" % self .key_type .decode ())
401432
402433 def parse (self , keydata = None ):
0 commit comments