import enum from collections import namedtuple import entry import hashing # Result(str/None, u16/None, str) Result = namedtuple('Result', ['domain', 'port', 'comment']) def check(entries, domain, port, fingerprint): """check([Entry], str, u16, bytes[32]) → ([Result]: successes, [Result]: fails, [Result]: same_fingerprint) Checks if the given host is found with the given fingerprint. successes contains ones where both the host and the fingerprint match. fails contains ones where host matches but the fingerprint doesn't. same_fingerprint contains ones where fingerprint matches but the host doesn't. Their .domain and .port will be None""" assert type(entries) == list and all(type(i) == entry.Entry for i in entries) assert type(domain) == str assert type(port) == int and 0 <= port <= (1<<16) - 1 assert type(fingerprint) == bytes and len(fingerprint) == 32 # Normalize the host here, so we don't have to do it every time we # check for a possible match normalized_hosts = {port: entry.normalize_host(domain, port)} # If we are looking at non-22 port, also check the general form of # the host without a port specifier. This seems to be how OpenSSH # does it too if port != 22: normalized_hosts[22] = entry.normalize_host(domain, 22) successes = [] fails = [] same_fingerprint = [] for possible_match in entries: any_host_matched = False for current_port, normalized_host in normalized_hosts.items(): hashed_host = hashing.hash_with_salt(normalized_host, possible_match.salt) if hashed_host == possible_match.hashed_host: if fingerprint == possible_match.fingerprint: # Fingerprint matches, it passes successes.append(Result(domain, current_port, possible_match.comment)) any_host_matched = True else: # Fingerprint different, it fails fails.append(Result(domain, current_port, possible_match.comment)) if not any_host_matched and fingerprint == possible_match.fingerprint: # Host is not the same, but the fingerprint # matches same_fingerprint.append(Result(None, None, possible_match.comment)) return successes, fails, same_fingerprint