示例#1
0
    def _encrypt(self):
        """
        Rebuild the master key from header settings and key-hash list. Encrypt
        the stream start bytes and the out-buffer formatted as hashed block
        stream with padding added as needed.
        """
        # rebuild master key from (possibly) updated header
        self._make_master_key()

        # make hashed block stream
        block_buffer = HashedBlockIO()
        block_buffer.write(self.out_buffer.read())
        # data is buffered in hashed block io, start a new one
        self.out_buffer = io.BytesIO()
        # write start bytes (for successful decrypt check)
        self.out_buffer.write(self.header.StreamStartBytes)
        # append blocked data to out-buffer
        block_buffer.write_block_stream(self.out_buffer)
        block_buffer.close()
        self.out_buffer.seek(0)

        # encrypt the whole thing with header settings and master key
        data = pad(self.out_buffer.read())
        self.out_buffer = aes_cbc_encrypt(data, self.master_key,
                                          self.header.EncryptionIV)
示例#2
0
文件: kdb4.py 项目: ifooth/libkeepass
    def _encrypt(self):
        """
        Rebuild the master key from header settings and key-hash list. Encrypt
        the stream start bytes and the out-buffer formatted as hashed block
        stream with padding added as needed.
        """
        # rebuild master key from (possibly) updated header
        self._make_master_key()

        # make hashed block stream
        block_buffer = HashedBlockIO()
        block_buffer.write(self.out_buffer.read())
        # data is buffered in hashed block io, start a new one
        self.out_buffer = io.BytesIO()
        # write start bytes (for successful decrypt check)
        self.out_buffer.write(self.header.StreamStartBytes)
        # append blocked data to out-buffer
        block_buffer.write_block_stream(self.out_buffer)
        block_buffer.close()
        self.out_buffer.seek(0)

        # encrypt the whole thing with header settings and master key
        data = pad(self.out_buffer.read())
        self.out_buffer = aes_cbc_encrypt(data, self.master_key,
            self.header.EncryptionIV)
示例#3
0
    def _decrypt(self, stream):
        """
        Build the master key from header settings and key-hash list.
        
        Start reading from `stream` after the header and decrypt all the data.
        Remove padding as needed and feed into hashed block reader, set as
        in-buffer.
        """
        super(KDB4File, self)._decrypt(stream)

        data = aes_cbc_decrypt(stream.read(), self.master_key,
                               self.header.EncryptionIV)
        data = unpad(data)

        length = len(self.header.StreamStartBytes)
        if self.header.StreamStartBytes == data[:length]:
            # skip startbytes and wrap data in a hashed block io
            self.in_buffer = HashedBlockIO(bytes=data[length:])
            # set successful decryption flag
            self.opened = True
        else:
            raise IOError('Master key invalid.')
示例#4
0
文件: kdb4.py 项目: ifooth/libkeepass
    def _decrypt(self, stream):
        """
        Build the master key from header settings and key-hash list.

        Start reading from `stream` after the header and decrypt all the data.
        Remove padding as needed and feed into hashed block reader, set as
        in-buffer.
        """
        super(KDB4File, self)._decrypt(stream)

        data = aes_cbc_decrypt(stream.read(), self.master_key,
            self.header.EncryptionIV)
        data = unpad(data)

        length = len(self.header.StreamStartBytes)
        if self.header.StreamStartBytes == data[:length]:
            # skip startbytes and wrap data in a hashed block io
            self.in_buffer = HashedBlockIO(bytes=data[length:])
            # set successful decryption flag
            self.opened = True
        else:
            raise IOError('Master key invalid.')
示例#5
0
class KDB4File(KDBFile):
    def __init__(self, stream=None, **credentials):
        self.header = KDB4Header()
        KDBFile.__init__(self, stream, **credentials)

    def set_compression(self, flag=1):
        """Dis- (0) or enable (default: 1) compression"""
        if flag not in [0, 1]:
            raise ValueError('Compression flag can be 0 or 1.')
        self.header.CompressionFlags = flag

    #def set_comment(self, comment):
    #    self.header.Comment = comment

    def read_from(self, stream):
        """
        Read, parse, decrypt, decompress a KeePass file from a stream.
        
        :arg stream: A file-like object (opened in 'rb' mode) or IO buffer
            containing a KeePass file.
        """
        super(KDB4File, self).read_from(stream)
        if self.header.CompressionFlags == 1:
            self._unzip()

    def write_to(self, stream):
        """
        Write the KeePass database back to a KeePass2 compatible file.
        
        :arg stream: A writeable file-like object or IO buffer.
        """
        if not (isinstance(stream, io.IOBase) or isinstance(stream, file)):
            raise TypeError('Stream does not have the buffer interface.')

        self._write_header(stream)

    def _read_header(self, stream):
        """
        Parse the header and write the values into self.header. Also sets
        self.header_length.
        """
        # KeePass 2.07 has version 1.01,
        # 2.08 has 1.02,
        # 2.09 has 2.00, 2.10 has 2.02, 2.11 has 2.04,
        # 2.15 has 3.00.
        # The first 2 bytes are critical (i.e. loading will fail, if the
        # file version is too high), the last 2 bytes are informational.
        #TODO implement version check

        # the first header field starts at byte 12 after the signature
        stream.seek(12)

        while True:
            # field_id is a single byte
            field_id = stream_unpack(stream, None, 1, 'b')

            # field_id >10 is undefined
            if not field_id in self.header.fields.values():
                raise IOError('Unknown header field found.')

            # two byte (short) length of field data
            length = stream_unpack(stream, None, 2, 'h')
            if length > 0:
                data = stream_unpack(stream, None, length,
                                     '{}s'.format(length))
                self.header.b[field_id] = data

            # set position in data stream of end of header
            if field_id == 0:
                self.header_length = stream.tell()
                break

    # def _write_header(self, stream):
    #     """Serialize the header fields from self.header into a byte stream, prefix
    #     with file signature and version before writing header and out-buffer
    #     to `stream`.

    #     Note, that `stream` is flushed, but not closed!"""
    #     # serialize header to stream
    #     header = bytearray()
    #     # write file signature
    #     header.extend(struct.pack('<II', *KDB4_SIGNATURE))
    #     # and version
    #     header.extend(struct.pack('<hh', 0, 3))

    #     field_ids = self.header.keys()
    #     field_ids.sort()
    #     field_ids.reverse() # field_id 0 must be last
    #     for field_id in field_ids:
    #         value = self.header.b[field_id]
    #         length = len(value)
    #         header.extend(struct.pack('<b', field_id))
    #         header.extend(struct.pack('<h', length))
    #         header.extend(struct.pack('{}s'.format(length), value))

    #     # write header to stream
    #     stream.write(header)

    #     headerHash = base64.b64encode(sha256(header))
    #     self.obj_root.Meta.HeaderHash = headerHash

    #     # create HeaderHash if it does not exist
    #     if len(self.obj_root.Meta.xpath("HeaderHash")) < 1:
    #         etree.SubElement(self.obj_root.Meta, "HeaderHash")

    #     # reload out_buffer because we just changed the HeaderHash
    #     self.protect()
    #     self.out_buffer = io.BytesIO(self.pretty_print())

    #     # zip or not according to header setting
    #     if self.header.CompressionFlags == 1:
    #         self._zip()

    #     self._encrypt();

    #     # write encrypted block to stream
    #     stream.write(self.out_buffer)
    #     stream.flush()

    def _decrypt(self, stream):
        """
        Build the master key from header settings and key-hash list.
        
        Start reading from `stream` after the header and decrypt all the data.
        Remove padding as needed and feed into hashed block reader, set as
        in-buffer.
        """
        super(KDB4File, self)._decrypt(stream)

        data = aes_cbc_decrypt(stream.read(), self.master_key,
                               self.header.EncryptionIV)
        data = unpad(data)

        length = len(self.header.StreamStartBytes)
        if self.header.StreamStartBytes == data[:length]:
            # skip startbytes and wrap data in a hashed block io
            self.in_buffer = HashedBlockIO(bytes=data[length:])
            # set successful decryption flag
            self.opened = True
        else:
            raise IOError('Master key invalid.')

    def _encrypt(self):
        """
        Rebuild the master key from header settings and key-hash list. Encrypt
        the stream start bytes and the out-buffer formatted as hashed block
        stream with padding added as needed.
        """
        # rebuild master key from (possibly) updated header
        self._make_master_key()

        # make hashed block stream
        block_buffer = HashedBlockIO()
        block_buffer.write(self.out_buffer.read())
        # data is buffered in hashed block io, start a new one
        self.out_buffer = io.BytesIO()
        # write start bytes (for successful decrypt check)
        self.out_buffer.write(self.header.StreamStartBytes)
        # append blocked data to out-buffer
        block_buffer.write_block_stream(self.out_buffer)
        block_buffer.close()
        self.out_buffer.seek(0)

        # encrypt the whole thing with header settings and master key
        data = pad(self.out_buffer.read())
        self.out_buffer = aes_cbc_encrypt(data, self.master_key,
                                          self.header.EncryptionIV)

    def _unzip(self):
        """
        Inplace decompress in-buffer. Read/write position is moved to 0.
        """
        self.in_buffer.seek(0)
        d = zlib.decompressobj(16 + zlib.MAX_WBITS)
        self.in_buffer = io.BytesIO(d.decompress(self.in_buffer.read()))
        self.in_buffer.seek(0)

    def _zip(self):
        """
        Inplace compress out-buffer. Read/write position is moved to 0.
        """
        data = self.out_buffer.read()
        self.out_buffer = io.BytesIO()
        # note: compresslevel=6 seems to be important for kdb4!
        gz = gzip.GzipFile(fileobj=self.out_buffer, mode='wb', compresslevel=6)
        gz.write(data)
        gz.close()
        self.out_buffer.seek(0)

    def _make_master_key(self):
        """
        Make the master key by (1) combining the credentials to create 
        a composite hash, (2) transforming the hash using the transform seed
        for a specific number of rounds and (3) finally hashing the result in 
        combination with the master seed.
        """
        super(KDB4File, self)._make_master_key()
        composite = sha256(''.join(self.keys))
        tkey = transform_key(composite, self.header.TransformSeed,
                             self.header.TransformRounds)
        self.master_key = sha256(self.header.MasterSeed + tkey)
示例#6
0
文件: kdb4.py 项目: ifooth/libkeepass
class KDB4File(KDBFile):
    def __init__(self, stream=None, **credentials):
        self.header = KDB4Header()
        KDBFile.__init__(self, stream, **credentials)

    def set_compression(self, flag=1):
        """Dis- (0) or enable (default: 1) compression"""
        if flag not in [0, 1]:
            raise ValueError('Compression flag can be 0 or 1.')
        self.header.CompressionFlags = flag

    #def set_comment(self, comment):
    #    self.header.Comment = comment

    def read_from(self, stream):
        """
        Read, parse, decrypt, decompress a KeePass file from a stream.

        :arg stream: A file-like object (opened in 'rb' mode) or IO buffer
            containing a KeePass file.
        """
        super(KDB4File, self).read_from(stream)
        if self.header.CompressionFlags == 1:
            self._unzip()

    def write_to(self, stream):
        """
        Write the KeePass database back to a KeePass2 compatible file.

        :arg stream: A writeable file-like object or IO buffer.
        """
        if not (isinstance(stream, io.IOBase) or isinstance(stream, file)):
            raise TypeError('Stream does not have the buffer interface.')

        self._write_header(stream)

    def _read_header(self, stream):
        """
        Parse the header and write the values into self.header. Also sets
        self.header_length.
        """
        # KeePass 2.07 has version 1.01,
        # 2.08 has 1.02,
        # 2.09 has 2.00, 2.10 has 2.02, 2.11 has 2.04,
        # 2.15 has 3.00.
        # The first 2 bytes are critical (i.e. loading will fail, if the
        # file version is too high), the last 2 bytes are informational.
        #TODO implement version check

        # the first header field starts at byte 12 after the signature
        stream.seek(12)

        while True:
            # field_id is a single byte
            field_id = stream_unpack(stream, None, 1, 'b')

            # field_id >10 is undefined
            if not field_id in self.header.fields.values():
                raise IOError('Unknown header field found.')

            # two byte (short) length of field data
            length = stream_unpack(stream, None, 2, 'h')
            if length > 0:
                data = stream_unpack(stream, None, length, '{}s'.format(length))
                self.header.b[field_id] = data

            # set position in data stream of end of header
            if field_id == 0:
                self.header_length = stream.tell()
                break

    def _write_header(self, stream):
        """Serialize the header fields from self.header into a byte stream, prefix
        with file signature and version before writing header and out-buffer
        to `stream`.

        Note, that `stream` is flushed, but not closed!"""
        # serialize header to stream
        header = bytearray()
        # write file signature
        header.extend(struct.pack('<II', *KDB4_SIGNATURE))
        # and version
        header.extend(struct.pack('<hh', 0, 3))

        field_ids = self.header.keys()
        field_ids.sort()
        field_ids.reverse() # field_id 0 must be last
        for field_id in field_ids:
            value = self.header.b[field_id]
            length = len(value)
            header.extend(struct.pack('<b', field_id))
            header.extend(struct.pack('<h', length))
            header.extend(struct.pack('{}s'.format(length), value))


        # write header to stream
        stream.write(header)

        headerHash = base64.b64encode(sha256(header))
        self.obj_root.Meta.HeaderHash = headerHash

        # create HeaderHash if it does not exist
        if len(self.obj_root.Meta.xpath("HeaderHash")) < 1:
            etree.SubElement(self.obj_root.Meta, "HeaderHash")

        # reload out_buffer because we just changed the HeaderHash
        self.protect()
        self.out_buffer = io.BytesIO(self.pretty_print())

        # zip or not according to header setting
        if self.header.CompressionFlags == 1:
            self._zip()

        self._encrypt();

        # write encrypted block to stream
        stream.write(self.out_buffer)
        stream.flush()

    def _decrypt(self, stream):
        """
        Build the master key from header settings and key-hash list.

        Start reading from `stream` after the header and decrypt all the data.
        Remove padding as needed and feed into hashed block reader, set as
        in-buffer.
        """
        super(KDB4File, self)._decrypt(stream)

        data = aes_cbc_decrypt(stream.read(), self.master_key,
            self.header.EncryptionIV)
        data = unpad(data)

        length = len(self.header.StreamStartBytes)
        if self.header.StreamStartBytes == data[:length]:
            # skip startbytes and wrap data in a hashed block io
            self.in_buffer = HashedBlockIO(bytes=data[length:])
            # set successful decryption flag
            self.opened = True
        else:
            raise IOError('Master key invalid.')

    def _encrypt(self):
        """
        Rebuild the master key from header settings and key-hash list. Encrypt
        the stream start bytes and the out-buffer formatted as hashed block
        stream with padding added as needed.
        """
        # rebuild master key from (possibly) updated header
        self._make_master_key()

        # make hashed block stream
        block_buffer = HashedBlockIO()
        block_buffer.write(self.out_buffer.read())
        # data is buffered in hashed block io, start a new one
        self.out_buffer = io.BytesIO()
        # write start bytes (for successful decrypt check)
        self.out_buffer.write(self.header.StreamStartBytes)
        # append blocked data to out-buffer
        block_buffer.write_block_stream(self.out_buffer)
        block_buffer.close()
        self.out_buffer.seek(0)

        # encrypt the whole thing with header settings and master key
        data = pad(self.out_buffer.read())
        self.out_buffer = aes_cbc_encrypt(data, self.master_key,
            self.header.EncryptionIV)

    def _unzip(self):
        """
        Inplace decompress in-buffer. Read/write position is moved to 0.
        """
        self.in_buffer.seek(0)
        d = zlib.decompressobj(16+zlib.MAX_WBITS)
        self.in_buffer = io.BytesIO(d.decompress(self.in_buffer.read()))
        self.in_buffer.seek(0)

    def _zip(self):
        """
        Inplace compress out-buffer. Read/write position is moved to 0.
        """
        data = self.out_buffer.read()
        self.out_buffer = io.BytesIO()
        # note: compresslevel=6 seems to be important for kdb4!
        gz = gzip.GzipFile(fileobj=self.out_buffer, mode='wb', compresslevel=6)
        gz.write(data)
        gz.close()
        self.out_buffer.seek(0)

    def _make_master_key(self):
        """
        Make the master key by (1) combining the credentials to create
        a composite hash, (2) transforming the hash using the transform seed
        for a specific number of rounds and (3) finally hashing the result in
        combination with the master seed.
        """
        super(KDB4File, self)._make_master_key()
        composite = sha256(''.join(self.keys))
        tkey = transform_key(composite,
            self.header.TransformSeed,
            self.header.TransformRounds)
        self.master_key = sha256(self.header.MasterSeed + tkey)