summaryrefslogtreecommitdiff
path: root/foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py
diff options
context:
space:
mode:
Diffstat (limited to 'foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py')
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py114
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