60 lines
3.0 KiB
Python
60 lines
3.0 KiB
Python
![]() |
#!/usr/bin/env python3
|
||
|
|
||
|
# Parse existing keys
|
||
|
|
||
|
class constructor(object):
|
||
|
# These are various struct formats for the "new"-style OpenSSH private keys.
|
||
|
# REF1: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=1.1
|
||
|
# REF2: https://github.com/openssh/openssh-portable/blob/94bc1e7ffba3cbdea8c7dcdab8376bf29283128f/sshkey.c
|
||
|
def __init__(self, ssh_keyblob):
|
||
|
# "keyblob" is the raw binary of an extracted (i.e. "---...---"-removed) and un-base64'd private key.
|
||
|
self.keyblob = ssh_keyblob
|
||
|
# 'encrypted' if it's encrypted, 'none' if plaintext. This is determined via processing.
|
||
|
self.enctype = 'none'
|
||
|
# This is the header. It is used by both encrypted and unencrypted keys.
|
||
|
self.header = ''.join((
|
||
|
'14cx', # "openssh-key-v1" and null byte (6f70656e7373682d6b65792d7631 00) ("magic bytes")
|
||
|
'i' # separator, always 00000004
|
||
|
))
|
||
|
# Only two cipher types that I know of that are used.
|
||
|
self.ciphertype = {
|
||
|
'none': '4c', # 6e6f6e65
|
||
|
'aes256-cbc': '10c'} # 6165733235362d636263 ("aes256-cbc")
|
||
|
# This separator is present in both.
|
||
|
self.sep1 = 'i'
|
||
|
# The name of the key encryption, if encrypted. These are the only two I know of that are used.
|
||
|
self.kdfname = {
|
||
|
'none': '4c', # 6e6f6e65 ("none")
|
||
|
'bcrypt': '6c'} # 626372797074 ("bcrypt")
|
||
|
########################################### ENCRYPTED KEYS ONLY ################################################
|
||
|
# KDF options
|
||
|
self.kdfopts = {
|
||
|
'none': '0i', # zero-length
|
||
|
'encrypted': '24i'} # 24-length int
|
||
|
# The length of the salt. Default is 16 (REF2:67)
|
||
|
self.saltlen = {
|
||
|
'none': '0i', # TODO: do unencrypted still have salts?
|
||
|
'encrypted': '4i'} # 16 bytes length?
|
||
|
# The key needs to be parsed incrementally to have these lengths adjusted.
|
||
|
self.salt = {
|
||
|
'none': '0c',
|
||
|
'encrypted': '4c'} # This value may change based on self.saltlen['encrypted']'s value.
|
||
|
# The number of rounds for the key; default is 16 (REF2:69).
|
||
|
self.rounds = {
|
||
|
'none': '0i', # TODO: do unencrypted still have rounds?
|
||
|
'encrypted': '16i'}
|
||
|
################################################################################################################
|
||
|
# Number of public keys.
|
||
|
self.numpub = 'i'
|
||
|
# That's all we can populate now. The rest is handled below via functions.
|
||
|
self._chkencrypt()
|
||
|
|
||
|
def _chkencrypt(self):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class legacy_constructor(object):
|
||
|
# These are various struct formats for the "old"-style OpenSSH private keys.
|
||
|
def __init__(self, keyblob):
|
||
|
self.keyblob = keyblob
|