From 31ff53dc7234ca5a86efa598a07b6818a67b15f3 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Dec 02 2016 15:24:05 +0000 Subject: Backport from a newercrytography the method to serialise public ssh key This allows converting a public key in a format that makes it available to use by OpenSSH --- diff --git a/pagure/hooks/mirror_hook.py b/pagure/hooks/mirror_hook.py index c4780cf..a7abcd9 100644 --- a/pagure/hooks/mirror_hook.py +++ b/pagure/hooks/mirror_hook.py @@ -10,12 +10,15 @@ import base64 import os +import struct import sqlalchemy as sa +import six import pygit2 import werkzeug import wtforms +from cryptography import utils from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization @@ -38,6 +41,60 @@ CONFIG_TPL = '''host %(name)s ''' +# Code from: +# https://github.com/pyca/cryptography/blob/master/src/cryptography/hazmat/primitives/serialization.py#L153 +def _ssh_write_string(data): + return struct.pack(">I", len(data)) + data + + +def _ssh_write_mpint(value): + data = utils.int_to_bytes(value) + if six.indexbytes(data, 0) & 0x80: + data = b"\x00" + data + return _ssh_write_string(data) + + +# Code from: +# https://github.com/pyca/cryptography/blob/master/src/cryptography/hazmat/backends/openssl/backend.py#L1660 +def serialize_public_ssh_key( key): + if isinstance(key, rsa.RSAPublicKey): + public_numbers = key.public_numbers() + return b"ssh-rsa " + base64.b64encode( + _ssh_write_string(b"ssh-rsa") + + _ssh_write_mpint(public_numbers.e) + + _ssh_write_mpint(public_numbers.n) + ) + elif isinstance(key, dsa.DSAPublicKey): + public_numbers = key.public_numbers() + parameter_numbers = public_numbers.parameter_numbers + return b"ssh-dss " + base64.b64encode( + _ssh_write_string(b"ssh-dss") + + _ssh_write_mpint(parameter_numbers.p) + + _ssh_write_mpint(parameter_numbers.q) + + _ssh_write_mpint(parameter_numbers.g) + + _ssh_write_mpint(public_numbers.y) + ) + else: + assert isinstance(key, ec.EllipticCurvePublicKey) + public_numbers = key.public_numbers() + try: + curve_name = { + ec.SECP256R1: b"nistp256", + ec.SECP384R1: b"nistp384", + ec.SECP521R1: b"nistp521", + }[type(public_numbers.curve)] + except KeyError: + raise ValueError( + "Only SECP256R1, SECP384R1, and SECP521R1 curves are " + "supported by the SSH public key format" + ) + return b"ecdsa-sha2-" + curve_name + b" " + base64.b64encode( + _ssh_write_string(b"ecdsa-sha2-" + curve_name) + + _ssh_write_string(curve_name) + + _ssh_write_string(public_numbers.encode_point()) + ) + + def split_target(target): ''' Check if the given target follows the expected model. ''' if target.startswith('http'): @@ -83,10 +140,7 @@ def create_ssh_key(keyfile): stream.write(private_pem) public_key = private_key.public_key() - public_pem = public_key.public_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo - ) + public_pem = serialize_public_ssh_key(public_key) with open(keyfile + '.pub', 'w') as stream: stream.write(public_pem)