[Cryptography-dev] ssh public key processing

Ron Frederick ronf at timeheart.net
Thu Aug 18 20:41:13 EDT 2016


I’ve implemented all of this in AsyncSSH, and there I chose to break out the authorized_key parsing from public key parsing. More specifically, AsyncSSH provides the following functions for reading SSH public keys:
import_public_key <http://asyncssh.readthedocs.io/en/latest/api.html#import-public-key>
asyncssh.import_public_key(data)[source] <http://asyncssh.readthedocs.io/en/latest/_modules/asyncssh/public_key.html#import_public_key> <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.import_public_key>
Import a public key

This function imports a public key encoded in OpenSSH, RFC4716, or PKCS#1 or PKCS#8 DER or PEM format.

Parameters:	data (bytes or ASCII string) – The data to import.
Returns:	An SSHKey <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.SSHKey> public key
read_public_key <http://asyncssh.readthedocs.io/en/latest/api.html#read-public-key>
asyncssh.read_public_key(filename)[source] <http://asyncssh.readthedocs.io/en/latest/_modules/asyncssh/public_key.html#read_public_key> <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.read_public_key>
Read a public key from a file

This function reads a public key from a file. See the function import_public_key() <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.import_public_key> for information about the formats supported.

Parameters:	filename (str <https://docs.python.org/3/library/stdtypes.html#str>) – The file to read the key from.
Returns:	An SSHKey <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.SSHKey> public key
read_public_key_list <http://asyncssh.readthedocs.io/en/latest/api.html#read-public-key-list>
asyncssh.read_public_key_list(filename)[source] <http://asyncssh.readthedocs.io/en/latest/_modules/asyncssh/public_key.html#read_public_key_list> <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.read_public_key_list>
Read a list of public keys from a file

This function reads a list of public keys from a file. See the function import_public_key() <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.import_public_key> for information about the formats supported.

Parameters:	filename (str <https://docs.python.org/3/library/stdtypes.html#str>) – The file to read the keys from.
Returns:	A list of SSHKey <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.SSHKey> public keys

Similar functions are also available for operating on SSH private keys and certificates.

Then, I have a separate set of functions for operating on data in authorized_key format:
import_authorized_keys <http://asyncssh.readthedocs.io/en/latest/api.html#import-authorized-keys>
asyncssh.import_authorized_keys(data)[source] <http://asyncssh.readthedocs.io/en/latest/_modules/asyncssh/auth_keys.html#import_authorized_keys> <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.import_authorized_keys>
Import SSH authorized keys

This function imports public keys and associated options in OpenSSH authorized keys format.

Parameters:	data (str <https://docs.python.org/3/library/stdtypes.html#str>) – The key data to import.
Returns:	An SSHAuthorizedKeys <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.SSHAuthorizedKeys> object
read_authorized_keys <http://asyncssh.readthedocs.io/en/latest/api.html#read-authorized-keys>
asyncssh.read_authorized_keys(filename)[source] <http://asyncssh.readthedocs.io/en/latest/_modules/asyncssh/auth_keys.html#read_authorized_keys> <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.read_authorized_keys>
Read SSH authorized keys from a file

This function reads public keys and associated options in OpenSSH authorized_keys format from a file.

Parameters:	filename (str <https://docs.python.org/3/library/stdtypes.html#str>) – The file to read the keys from.
Returns:	An SSHAuthorizedKeys <http://asyncssh.readthedocs.io/en/latest/api.html#asyncssh.SSHAuthorizedKeys> object
Internally, the authorized_key functions use the public key functions, but only after stripping any prefix like “cert-authority” or the various options for limiting when the keys can be used or what SSH actions are allowed when using that key. Then, given an SSH key and some other parameters like the client IP and certificate information the resulting SSHAuthorizedKeys object can then perform matching against all these constraints and return if a key is allowed to be used and what permissions it grants if so.


On Aug 18, 2016, at 4:04 PM, Chris Hines <chris.hines at monash.edu> wrote:
> Hi Paul,
> Options are specified in the sshd man page
> 
> https://www.freebsd.org/cgi/man.cgi?sshd(8) <https://www.freebsd.org/cgi/man.cgi?sshd(8)>
> 
> Under the section Authorized Keys File Format.
> 
> Technically options are not part of the public key (so are not covered by  RFC4253) but are part of the OpenSSHD authorized_keys file format (which includes everything that can appear in an id_rsa.pub etc and extends it with the options defined in the man page).
> 
> Sadly I don't think there is an RFC for the authorized_keys file (I suspect its OpenSSH specific) but I think we all assume its a de-facto standard :-)
> 
> So at question is whether python-cryptography primitives support only the RFC4253 spec for public key formats or more generally processes lines from an authorized_keys file.
> 
> The fix should be relatively simple. When you split the data into  fields (key-type, data and comment) don't assume the first field is key-type, but instead search all fields.
> 
> The particular option *I* am most concerned with is the cert-authority option (a relatively new feature in OpenSSH, but a pretty awesome one IMHO). It gets pre-pended to the key before going in authorized_keys, so a typical entry looks like:
> 
> cert-authority ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMnxAxjwoCQsFJ1SC7+LJeSQCmUi8plJ9nKVmAjKlDr5Z240doRVIBr7+6veFJDPkaFzDxxa4dDn4O1ITXXVrr/JUCQbo4nF3ln4LSGWdDniJxH5uhc7PDYe2FDiiUYLrl4I+n1cB5YSOTDjxERcvXORKKGjPoyYNVuv343a5n7ygIOVTnI9VwTlxKj8gNyMm7wRg+aIJ8yiDwaDqestL9qTGMc+bb8Q0w0OJn8KZoGBYGl7LvS0QNyXsp+5J0GGEuE1c0lp1d5HpgvqRdWeWrlGY5alyQ2BchfJLbUbWQGBP/+kmVZCR022jbEo13/SznECr8ym8cXZzYg+hC1Err hines at tun
> 
> This particular option brings some of the functionality of x509 certificates used for TLS communications (i.e. certifying authorities generating certificates that can be trusted becase you trust the CA) to SSH without bringing the full attack surface of x509 to ssh (at least that was the justification from the OpenSSH authors for not just using x509 :-)
> 
> Other interesting options include command="..." and no-agent-forwarding,no-port-forwarding
> 
> Ironically I'm not actually certain if this support should be added to python-cryptography, or if it should be added downstream in openstack nova. Technically its part of the authorized_keys file format, not part of the pub key format, and the function in question is pretty clearly named "load_ssh_public_key". On the other hand, it is clear there is some confusion in the wider community of users on the difference between a public key and a line in an authorized_keys file, and perhaps it would be a nice feature to add.
> 
> Cheers,
> --
> Chris.
> 
> On 18 August 2016 at 23:08, Paul Kehrer <paul.l.kehrer at gmail.com <mailto:paul.l.kehrer at gmail.com>> wrote:
> Hi Chris,
> 
> I don't think we've tried to specifically bound it. In general the assumption has been that the keys it loads would be OpenSSH public keys in the form that you get from an "id_rsa.pub" file (for example).
> 
> What do the options look like? Are they put into the line at the end as comments?
> 
> -Paul (reaperhulk)
> 
> 
> On August 18, 2016 at 8:15:16 AM, Chris Hines (chris.hines at monash.edu <mailto:chris.hines at monash.edu>) wrote:
> 
>> Hi List,
>> I have a question about the function
>> cryptography.hazmat.primatives.serialization.load_ssh_public_key
>> 
>> Basically is the function inteornded to load only the public key or is it intended that it be able to process any like out of an authorized_keys_file
>> 
>> Source code shows that the function is prepared to strip of the key-type (eg ssh-rsa) and use it for comparison against the inner_key_type but is not prepared to strip off any options that can be passed in an authorized_keys file (For example SSH_FORCE_COMMAND or no-port-forwarding).
>> 
>> I ask because the downstream project OpenStack Nova uses load_ssh_public_key to verify contents intended for authorized_keys is valid. Its easy enough to remove ssh options in Nova before passing to load_ssh_public_key, but I though if load_ssh_public_key already deals with the key-type header, perhaps it should also deal with the other options.
>> 
>> I can create issues and merge requests if that is helpful, just looking for clarification on the intention (i.e. does load_ssh_public_key load contents intended for authorized_keys or just the public key part)

-- 
Ron Frederick
ronf at timeheart.net



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/cryptography-dev/attachments/20160818/19f44e4a/attachment-0001.html>


More information about the Cryptography-dev mailing list