Skip to content

Commit

Permalink
Fix composite key computation for BytesIO (#388)
Browse files Browse the repository at this point in the history
In case a keyfile as BytesIO has been read before, the
next read will be empty. We need to ensure that we
are reading the data from the beginning.

Add seek to start to fix it.

Co-authored-by: Jan-Michael Brummer <jan-michael.brummer1@volkswagen.de>
  • Loading branch information
janbrummer and Jan-Michael Brummer committed Apr 9, 2024
1 parent e43ca6c commit 6e591b4
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 2 deletions.
6 changes: 4 additions & 2 deletions pykeepass/kdbx_parsing/common.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import base64
import codecs
import hashlib
import io
import logging
import re
import zlib
from binascii import Error as BinasciiError
from collections import OrderedDict
from copy import deepcopy
from io import BytesIO

from construct import (
Adapter,
Expand Down Expand Up @@ -128,6 +128,8 @@ def compute_key_composite(password=None, keyfile=None):
# hash the keyfile
if keyfile:
if hasattr(keyfile, "read"):
if hasattr(keyfile, "seekable") and keyfile.seekable():
keyfile.seek(0)
keyfile_bytes = keyfile.read()
else:
with open(keyfile, 'rb') as f:
Expand Down Expand Up @@ -194,7 +196,7 @@ class XML(Adapter):

def _decode(self, data, con, path):
parser = etree.XMLParser(remove_blank_text=True)
return etree.parse(BytesIO(data), parser)
return etree.parse(io.BytesIO(data), parser)

def _encode(self, tree, con, path):
return etree.tostring(tree)
Expand Down
14 changes: 14 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,20 @@ def test_fields(self):


class PyKeePassTests3(KDBX3Tests):
def test_consecutives_saves_with_stream(self):
# https://github.com/libkeepass/pykeepass/pull/388
self.setUp()

with open(base_dir / self.keyfile_tmp, 'rb') as f:
keyfile = BytesIO(f.read())

for _i in range(5):
with PyKeePass(
base_dir / self.database_tmp,
password=self.password,
keyfile=keyfile,
) as kp:
kp.save()

def test_set_credentials(self):
self.kp_tmp.password = 'f00bar'
Expand Down

0 comments on commit 6e591b4

Please sign in to comment.