| import binascii |
| from . import der |
| from ._compat import compat26_str, int_to_bytes |
| |
| _SSH_ED25519 = b"ssh-ed25519" |
| _SK_MAGIC = b"openssh-key-v1\0" |
| _NONE = b"none" |
| |
| |
| def _get_key_type(name): |
| if name == "Ed25519": |
| return _SSH_ED25519 |
| else: |
| raise ValueError("Unsupported key type") |
| |
| |
| class _Serializer: |
| def __init__(self): |
| self.bytes = b"" |
| |
| def put_raw(self, val): |
| self.bytes += val |
| |
| def put_u32(self, val): |
| self.bytes += int_to_bytes(val, length=4, byteorder="big") |
| |
| def put_str(self, val): |
| self.put_u32(len(val)) |
| self.bytes += val |
| |
| def put_pad(self, blklen=8): |
| padlen = blklen - (len(self.bytes) % blklen) |
| self.put_raw(bytearray(range(1, 1 + padlen))) |
| |
| def encode(self): |
| return binascii.b2a_base64(compat26_str(self.bytes)) |
| |
| def tobytes(self): |
| return self.bytes |
| |
| def topem(self): |
| return der.topem(self.bytes, "OPENSSH PRIVATE KEY") |
| |
| |
| def serialize_public(name, pub): |
| serial = _Serializer() |
| ktype = _get_key_type(name) |
| serial.put_str(ktype) |
| serial.put_str(pub) |
| return b" ".join([ktype, serial.encode()]) |
| |
| |
| def serialize_private(name, pub, priv): |
| # encode public part |
| spub = _Serializer() |
| ktype = _get_key_type(name) |
| spub.put_str(ktype) |
| spub.put_str(pub) |
| |
| # encode private part |
| spriv = _Serializer() |
| checksum = 0 |
| spriv.put_u32(checksum) |
| spriv.put_u32(checksum) |
| spriv.put_raw(spub.tobytes()) |
| spriv.put_str(priv + pub) |
| comment = b"" |
| spriv.put_str(comment) |
| spriv.put_pad() |
| |
| # top-level structure |
| main = _Serializer() |
| main.put_raw(_SK_MAGIC) |
| ciphername = kdfname = _NONE |
| main.put_str(ciphername) |
| main.put_str(kdfname) |
| nokdf = 0 |
| main.put_u32(nokdf) |
| nkeys = 1 |
| main.put_u32(nkeys) |
| main.put_str(spub.tobytes()) |
| main.put_str(spriv.tobytes()) |
| return main.topem() |