diff options
author | AL-LCL <alvin@alvinhavel.com> | 2023-05-19 11:01:49 +0200 |
---|---|---|
committer | AL-LCL <alvin@alvinhavel.com> | 2023-05-19 11:01:49 +0200 |
commit | 20dbeb2f38684c65ff0a4b99012c161295708e88 (patch) | |
tree | a5b8445f55da2fbbb92443b68e9d7354a290c598 /foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py |
Diffstat (limited to 'foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py')
-rw-r--r-- | foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py b/foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py new file mode 100644 index 0000000..ade6e96 --- /dev/null +++ b/foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +import hashlib +import io +import struct + +# default from KeePass2 source +BLOCK_LENGTH = 1024 * 1024 + +try: + file_types = (file, io.IOBase) +except NameError: + file_types = (io.IOBase,) + +# HEADER_LENGTH = 4+32+4 + +def read_int(stream, length): + try: + return struct.unpack('<I', stream.read(length))[0] + except Exception: + return None + + +class HashedBlockIO(io.BytesIO): + """ + The data is stored in hashed blocks. Each block consists of a block index (4 + bytes), the hash (32 bytes) and the block length (4 bytes), followed by the + block data. The block index starts counting at 0. The block hash is a + SHA-256 hash of the block data. A block has a maximum length of + BLOCK_LENGTH, but can be shorter. + + Provide a I/O stream containing the hashed block data as the `block_stream` + argument when creating a HashedBlockReader. Alternatively the `bytes` + argument can be used to hand over data as a string/bytearray/etc. The data + is verified upon initialization and an IOError is raised when a hash does + not match. + + HashedBlockReader is a subclass of io.BytesIO. The inherited read, seek, ... + functions shall be used to access the verified data. + """ + + def __init__(self, block_stream=None, bytes=None): + io.BytesIO.__init__(self) + input_stream = None + if block_stream is not None: + if not (isinstance(block_stream, io.IOBase) or isinstance(block_stream, file_types)): + raise TypeError('Stream does not have the buffer interface.') + input_stream = block_stream + elif bytes is not None: + input_stream = io.BytesIO(bytes) + if input_stream is not None: + self.read_block_stream(input_stream) + + def read_block_stream(self, block_stream): + """ + Read the whole block stream into the self-BytesIO. + """ + if not (isinstance(block_stream, io.IOBase) or isinstance(block_stream, file_types)): + raise TypeError('Stream does not have the buffer interface.') + while True: + data = self._next_block(block_stream) + if not self.write(data): + break + self.seek(0) + + def _next_block(self, block_stream): + """ + Read the next block and verify the data. + Raises an IOError if the hash does not match. + """ + index = read_int(block_stream, 4) + bhash = block_stream.read(32) + length = read_int(block_stream, 4) + + if length > 0: + data = block_stream.read(length) + if hashlib.sha256(data).digest() == bhash: + return data + else: + raise IOError('Block hash mismatch error.') + return bytes() + + def write_block_stream(self, stream, block_length=BLOCK_LENGTH): + """ + Write all data in this buffer, starting at stream position 0, formatted + in hashed blocks to the given `stream`. + + For example, writing data from one file into another as hashed blocks:: + + # create new hashed block io without input stream or data + hb = HashedBlockIO() + # read from a file, write into the empty hb + with open('sample.dat', 'rb') as infile: + hb.write(infile.read()) + # write from the hb into a new file + with open('hb_sample.dat', 'w') as outfile: + hb.write_block_stream(outfile) + """ + if not (isinstance(stream, io.IOBase) or isinstance(stream, file_types)): + raise TypeError('Stream does not have the buffer interface.') + index = 0 + self.seek(0) + while True: + data = self.read(block_length) + if data: + stream.write(struct.pack('<I', index)) + stream.write(hashlib.sha256(data).digest()) + stream.write(struct.pack('<I', len(data))) + stream.write(data) + index += 1 + else: + stream.write(struct.pack('<I', index)) + stream.write('\x00' * 32) + stream.write(struct.pack('<I', 0)) + break |