in pipe-cli/src/utilities/putty/kh2reg.py [0:0]
def handle_line(line, output_formatter, try_hosts):
try:
# Remove leading/trailing whitespace (should zap CR and LF)
line = line.strip()
# Skip blanks and comments
if line == '' or line[0] == '#':
raise BlankInputLine
# Split line on spaces.
fields = line.split(' ')
# Common fields
hostpat = fields[0]
keyparams = [] # placeholder
keytype = "" # placeholder
# Grotty heuristic to distinguish known_hosts from known_hosts2:
# is second field entirely decimal digits?
if re.match (r"\d*$", fields[1]):
# Treat as SSH-1-type host key.
# Format: hostpat bits10 exp10 mod10 comment...
# (PuTTY doesn't store the number of bits.)
keyparams = list(map(int, fields[2:4]))
keytype = "rsa"
else:
# Treat as SSH-2-type host key.
# Format: hostpat keytype keyblob64 comment...
sshkeytype, blob = fields[1], base64.decodebytes(
fields[2].encode("ASCII"))
# 'blob' consists of a number of
# uint32 N (big-endian)
# uint8[N] field_data
subfields = []
while blob:
sizefmt = ">L"
(size,) = struct.unpack (sizefmt, blob[0:4])
size = int(size) # req'd for slicage
(data,) = struct.unpack (">%lus" % size, blob[4:size+4])
subfields.append(data)
blob = blob [struct.calcsize(sizefmt) + size : ]
# The first field is keytype again.
if subfields[0].decode("ASCII") != sshkeytype:
raise KeyFormatError("""
outer and embedded key types do not match: '%s', '%s'
""" % (sshkeytype, subfields[1]))
# Translate key type string into something PuTTY can use, and
# munge the rest of the data.
if sshkeytype == "ssh-rsa":
keytype = "rsa2"
# The rest of the subfields we can treat as an opaque list
# of bignums (same numbers and order as stored by PuTTY).
keyparams = list(map(strtoint, subfields[1:]))
elif sshkeytype == "ssh-dss":
keytype = "dss"
# Same again.
keyparams = list(map(strtoint, subfields[1:]))
elif sshkeytype in nist_curves:
keytype = sshkeytype
# Have to parse this a bit.
if len(subfields) > 3:
raise KeyFormatError("too many subfields in blob")
(curvename, Q) = subfields[1:]
# First is yet another copy of the key name.
if not re.match("ecdsa-sha2-" + re.escape(
curvename.decode("ASCII")), sshkeytype):
raise KeyFormatError("key type mismatch ('%s' vs '%s')"
% (sshkeytype, curvename))
# Second contains key material X and Y (hopefully).
# First a magic octet indicating point compression.
point_type = struct.unpack_from("B", Q, 0)[0]
Qrest = Q[1:]
if point_type == 4:
# Then two equal-length bignums (X and Y).
bnlen = len(Qrest)
if (bnlen % 1) != 0:
raise KeyFormatError("odd-length X+Y")
bnlen = bnlen // 2
x = strtoint(Qrest[:bnlen])
y = strtoint(Qrest[bnlen:])
elif 2 <= point_type <= 3:
# A compressed point just specifies X, and leaves
# Y implicit except for parity, so we have to
# recover it from the curve equation.
curve = nist_curves[sshkeytype]
x = strtoint(Qrest)
yy = (x*x*x + curve.a*x + curve.b) % curve.p
y = SqrtModP.root(yy, curve.p)
if y % 2 != point_type % 2:
y = curve.p - y
keyparams = [curvename, x, y]
elif sshkeytype in { "ssh-ed25519", "ssh-ed448" }:
keytype = sshkeytype
if len(subfields) != 2:
raise KeyFormatError("wrong number of subfields in blob")
# Key material y, with the top bit being repurposed as
# the expected parity of the associated x (point
# compression).
y = strtoint_le(subfields[1])
x_parity = y >> 255
y &= ~(1 << 255)
# Curve parameters.
p, d, a = {
"ssh-ed25519": (2**255 - 19, 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3, -1),
"ssh-ed448": (2**448-2**224-1, -39081, +1),
}[sshkeytype]
# Recover x^2 = (y^2 - 1) / (d y^2 - a).
xx = (y*y - 1) * invert(d*y*y - a, p) % p
# Take the square root.
x = SqrtModP.root(xx, p)
# Pick the square root of the correct parity.
if (x % 2) != x_parity:
x = p - x
keyparams = [x, y]
else:
raise UnknownKeyType(sshkeytype)
# Now print out one line per host pattern, discarding wildcards.
for host in hostpat.split(','):
if re.search (r"[*?!]", host):
warn("skipping wildcard host pattern '%s'" % host)
continue
if re.match (r"\|", host):
for try_host in try_hosts:
if openssh_hashed_host_match(host.encode('ASCII'),
try_host.encode('UTF-8')):
host = try_host
break
else:
warn("unable to match hashed hostname '%s'" % host)
continue
m = re.match (r"\[([^]]*)\]:(\d*)$", host)
if m:
(host, port) = m.group(1,2)
port = int(port)
else:
port = 22
# Slightly bizarre output key format: 'type@port:hostname'
# XXX: does PuTTY do anything useful with literal IP[v4]s?
key = keytype + ("@%d:%s" % (port, host))
# Most of these are numbers, but there's the occasional
# string that needs passing through
value = ",".join(map(
lambda x: x if isinstance(x, str)
else x.decode('ASCII') if isinstance(x, bytes)
else inttohex(x), keyparams))
output_formatter.key(key, value)
except UnknownKeyType as k:
warn("unknown SSH key type '%s', skipping" % k.keytype)
except KeyFormatError as k:
warn("trouble parsing key (%s), skipping" % k.msg)
except BlankInputLine:
pass