import entry class FileFormatError(Exception): pass class VersionMismatch(Exception): pass def check_header(f): """check_header(file(rb)) Throw an error if the header isn't good""" # Magic is b'WOT' magic = f.read(3) if magic != b'WOT': raise FileFormatError('Invalid magic') # Version 0 is the current one version = f.read(1) if version == b'': raise FileFormatError('Unexpected end of file') if version != b'\0': raise VersionMismatch('Version %i not supported' % version[0]) def read_entry(f): """read_entry(file(rb)) → Entry / None Returns None if the end of file has been reached""" # u8[32]: salt salt = f.read(32) if len(salt) == 0: # End of file has been reached, return None to mark that return None elif len(salt) != 32: raise FileFormatError('Unexpected end of file') # u8[32]: hashed_host hashed_host = f.read(32) if len(hashed_host) != 32: raise FileFormatError('Unexpected end of file') # u8[32]: fingerprint fingerprint = f.read(32) if len(fingerprint) != 32: raise FileFormatError('Unexpected end of file') # u16le: comment_length length_bytes = f.read(2) if len(length_bytes) != 2: raise FileFormatError('Unexpected end of file') comment_length = length_bytes[0] | length_bytes[1] << 8 # u8[comment_length]: comment comment = f.read(comment_length) if len(comment) != comment_length: raise FileFormatError('Unexpected end of file') return entry.Entry(salt, hashed_host, fingerprint, comment) def read(f): """read_file(file(rb)) → [Entry]""" check_header(f) entries = [] while True: # Read until we reach the end of file entry = read_entry(f) if entry is None: break entries.append(entry) return entries