Control: tags -1 + patch Wrote a patch to handle RFC 6594 and draft-moonesamy-sshfp-ed25519-01 (assuming IANA assignment for Ed25519 is type '4').
-- Gerald Turner <gtur...@unzane.com> Encrypted mail preferred! OpenPGP: 4096R / CA89 B27A 30FA 66C5 1B80 3858 EC94 2276 FDB8 716D
diff --git a/sshfp b/sshfp index b1dd9d9..564f594 100755 --- a/sshfp +++ b/sshfp @@ -12,6 +12,7 @@ import subprocess import optparse import base64 import time +import hashlib # www.dnspython.org try: import dns.resolver @@ -24,14 +25,6 @@ except ImportError: print >> sys.stderr, "openSUSE: zypper in python-dnspython" sys.exit(1) -try: - import hashlib - digest = hashlib.sha1 -except ImportError: - import sha - digest = sha.new - - global all_hosts global khfile global hostnames @@ -46,7 +39,7 @@ DEFAULT_KNOWN_HOSTS_FILE = "~/.ssh/known_hosts" def show_version(): print >> sys.stderr, "sshfp version: " + VERSION -def create_sshfp(hostname, keytype, keyblob): +def create_sshfp(hostname, keytype, keyblob, digesttype): """Creates an SSH fingerprint""" if keytype == "ssh-rsa": @@ -55,13 +48,29 @@ def create_sshfp(hostname, keytype, keyblob): if keytype == "ssh-dss": keytype = "2" else: + if "ecdsa" in keytype: + keytype = "3" + else: + if keytype == "ssh-ed25519": + # TBD http://tools.ietf.org/html/draft-moonesamy-sshfp-ed25519-01 + keytype = "4" + else: + return "" + if digesttype == "sha1": + digest = hashlib.sha1 + digesttype = "1" + else: + if digesttype == "sha256": + digest = hashlib.sha256 + digesttype = "2" + else: return "" try: rawkey = base64.b64decode(keyblob) except TypeError: print >> sys.stderr, "FAILED on hostname "+hostname+" with keyblob "+keyblob return "ERROR" - fpsha1 = digest(rawkey).hexdigest().upper() + fp = digest(rawkey).hexdigest().upper() # check for Reverse entries reverse = 1 parts = hostname.split(".", 3) @@ -76,7 +85,7 @@ def create_sshfp(hostname, keytype, keyblob): if trailing and not reverse: if hostname[-1:] != ".": hostname = hostname + "." - return hostname + " IN SSHFP " + keytype + " 1 " + fpsha1 + return hostname + " IN SSHFP " + keytype + " " + digesttype + " " + fp def get_known_host_entry(known_hosts, host): """Get a single entry out of a known_hosts file @@ -127,20 +136,20 @@ def sshfp_from_file(khfile, wantedHosts): fingerprints.append(process_records(data, wantedHosts)) return "\n".join(fingerprints) -def check_keytype(keytype): +def check_keytype(keytype, hostname): global algos for algo in algos: - if "ssh-%s" % algo[:-1] == keytype[:-1]: + if algo in keytype: return True if not quiet: print >> sys.stderr, "Could only find key type %s for %s" % (keytype, hostname) return False -def process_record(record, hostname): +def process_record(record, hostname, digesttype): (host, keytype, key) = record.split(" ") key = key.rstrip() - if check_keytype(keytype): - record = create_sshfp(hostname, keytype, key) + if check_keytype(keytype, hostname): + record = create_sshfp(hostname, keytype, key, digesttype) return record return "" @@ -153,6 +162,7 @@ def process_records(data, hostnames): If "all_hosts is False and hostnames is non-empty, return only the items in hostnames """ + global digests all_records = [] for record in data.split("\n"): if record.startswith("#") or not record: @@ -166,9 +176,10 @@ def process_records(data, hostnames): if "," in host: host = host.split(",")[0] if all_hosts or host in hostnames or host == hostnames: - if not check_keytype(keytype): + if not check_keytype(keytype, host): continue - all_records.append(create_sshfp(host, keytype, key)) + for digesttype in digests: + all_records.append(create_sshfp(host, keytype, key, digesttype)) if all_records: all_records.sort() return "\n".join(all_records) @@ -250,6 +261,7 @@ def main(): global port global timeout global algos + global digests parser = optparse.OptionParser() parser.add_option("-k", "--knownhosts", "--known-hosts", @@ -299,9 +311,16 @@ def main(): action="append", type="choice", dest="algo", - choices=["rsa", "dsa"], + choices=["dsa", "ecdsa", "ed25519", "rsa"], + default=[], + help="key type to fetch (may be specified more than once, default dsa,ecdsa,ed25519,rsa)") + parser.add_option("--digest", + action="append", + type="choice", + dest="digest", + choices=["sha1", "sha256"], default=[], - help="key type to fetch (may be specified more than once, default dsa,rsa)") + help="fingerprint hash function (may be specified more than once, default sha1,sha256)") parser.add_option("-n", "--nameserver", action="store", type="string", @@ -320,7 +339,8 @@ def main(): data = "" trailing = options.trailing_dot timeout = options.timeout - algos = options.algo or ["dsa", "rsa"] + algos = options.algo or ["dsa", "ecdsa", "ed25519", "rsa"] + digests = options.digest or ["sha1", "sha256"] all_hosts = options.all_hosts port = options.port hostnames = ()
pgpTlEBoj9Y_w.pgp
Description: PGP signature