Skip to content

Commit

Permalink
Fix composite key computation for BytesIO
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.
  • Loading branch information
janbrummer authored and A6GibKm committed Mar 26, 2024
1 parent e43ca6c commit 79dbb28
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 3 deletions.
12 changes: 9 additions & 3 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 @@ -127,7 +127,13 @@ def compute_key_composite(password=None, keyfile=None):
password_composite = b''
# hash the keyfile
if keyfile:
if hasattr(keyfile, "read"):
if (
isinstance(keyfile, io.BufferedIOBase)
or isinstance(keyfile, io.TextIOBase)
or isinstance(keyfile, io.RawIOBase)
):
if keyfile.seekable():
keyfile.seek(0)
keyfile_bytes = keyfile.read()
else:
with open(keyfile, 'rb') as f:
Expand Down Expand Up @@ -194,7 +200,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
13 changes: 13 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,19 @@ def test_fields(self):


class PyKeePassTests3(KDBX3Tests):
def test_consecutives_saves_with_stream(self):
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 79dbb28

Please sign in to comment.