diff --git a/pykeepass/__init__.py b/pykeepass/__init__.py index 4b97e7fb..72135b4c 100644 --- a/pykeepass/__init__.py +++ b/pykeepass/__init__.py @@ -1,5 +1,4 @@ from .pykeepass import PyKeePass, create_database - from .version import __version__ __all__ = ["PyKeePass", "create_database", "__version__"] diff --git a/pykeepass/attachment.py b/pykeepass/attachment.py index 2599ddd8..95ebf51f 100644 --- a/pykeepass/attachment.py +++ b/pykeepass/attachment.py @@ -1,6 +1,7 @@ from . import entry from .exceptions import BinaryError + class Attachment: def __init__(self, element=None, kp=None, id=None, filename=None): self._element = element diff --git a/pykeepass/baseelement.py b/pykeepass/baseelement.py index 611aef07..ec870eff 100644 --- a/pykeepass/baseelement.py +++ b/pykeepass/baseelement.py @@ -1,8 +1,9 @@ import base64 import uuid +from datetime import datetime + from lxml import etree from lxml.builder import E -from datetime import datetime class BaseElement: diff --git a/pykeepass/group.py b/pykeepass/group.py index c22f74f5..2ce16b71 100644 --- a/pykeepass/group.py +++ b/pykeepass/group.py @@ -2,8 +2,8 @@ from lxml.etree import Element, _Element from lxml.objectify import ObjectifiedElement -from .entry import Entry from .baseelement import BaseElement +from .entry import Entry class Group(BaseElement): diff --git a/pykeepass/kdbx_parsing/common.py b/pykeepass/kdbx_parsing/common.py index fa0ea074..8b24bcb9 100644 --- a/pykeepass/kdbx_parsing/common.py +++ b/pykeepass/kdbx_parsing/common.py @@ -1,20 +1,32 @@ -from Cryptodome.Cipher import AES, ChaCha20, Salsa20 -from .twofish import Twofish -from Cryptodome.Util import Padding as CryptoPadding +import base64 +import codecs import hashlib +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, BitStruct, BitsSwapped, Container, Flag, Padding, ListContainer, Mapping, GreedyBytes, Int32ul, Switch + Adapter, + BitsSwapped, + BitStruct, + Container, + Flag, + GreedyBytes, + Int32ul, + ListContainer, + Mapping, + Padding, + Switch, ) +from Cryptodome.Cipher import AES, ChaCha20, Salsa20 +from Cryptodome.Util import Padding as CryptoPadding from lxml import etree -from copy import deepcopy -import base64 -from binascii import Error as BinasciiError -import zlib -import re -import codecs -from io import BytesIO -from collections import OrderedDict -import logging + +from .twofish import Twofish log = logging.getLogger(__name__) diff --git a/pykeepass/kdbx_parsing/kdbx.py b/pykeepass/kdbx_parsing/kdbx.py index 3a43f7cf..935da4ae 100644 --- a/pykeepass/kdbx_parsing/kdbx.py +++ b/pykeepass/kdbx_parsing/kdbx.py @@ -1,8 +1,10 @@ -from construct import Struct, Switch, Bytes, Int16ul, RawCopy, Check, this -from .kdbx3 import DynamicHeader as DynamicHeader3 +from construct import Bytes, Check, Int16ul, RawCopy, Struct, Switch, this + from .kdbx3 import Body as Body3 -from .kdbx4 import DynamicHeader as DynamicHeader4 +from .kdbx3 import DynamicHeader as DynamicHeader3 from .kdbx4 import Body as Body4 +from .kdbx4 import DynamicHeader as DynamicHeader4 + # verify file signature def check_signature(ctx): diff --git a/pykeepass/kdbx_parsing/kdbx3.py b/pykeepass/kdbx_parsing/kdbx3.py index 87db0f31..16ef911c 100644 --- a/pykeepass/kdbx_parsing/kdbx3.py +++ b/pykeepass/kdbx_parsing/kdbx3.py @@ -2,18 +2,48 @@ # keepass decrypt experimentation import hashlib + from construct import ( - Byte, Bytes, Int16ul, Int32ul, Int64ul, RepeatUntil, GreedyBytes, Struct, - this, Mapping, Switch, Prefixed, Padding, Checksum, Computed, IfThenElse, - Pointer, Tell, len_, If + Byte, + Bytes, + Checksum, + Computed, + GreedyBytes, + If, + IfThenElse, + Int16ul, + Int32ul, + Int64ul, + Mapping, + Padding, + Pointer, + Prefixed, + RepeatUntil, + Struct, + Switch, + Tell, + len_, + this, ) + from .common import ( - aes_kdf, AES256Payload, ChaCha20Payload, TwoFishPayload, Concatenated, - DynamicDict, compute_key_composite, Decompressed, Reparsed, - compute_master, CompressionFlags, XML, CipherId, ProtectedStreamId, Unprotect + XML, + AES256Payload, + ChaCha20Payload, + CipherId, + CompressionFlags, + Concatenated, + Decompressed, + DynamicDict, + ProtectedStreamId, + Reparsed, + TwoFishPayload, + Unprotect, + aes_kdf, + compute_key_composite, + compute_master, ) - # -------------------- Key Derivation -------------------- # https://github.com/keepassxreboot/keepassxc/blob/8324d03f0a015e62b6182843b4478226a5197090/src/format/KeePass2.cpp#L24-L26 diff --git a/pykeepass/kdbx_parsing/kdbx4.py b/pykeepass/kdbx_parsing/kdbx4.py index 41ce7a89..426d3ac7 100644 --- a/pykeepass/kdbx_parsing/kdbx4.py +++ b/pykeepass/kdbx_parsing/kdbx4.py @@ -1,22 +1,55 @@ # Evan Widloski - 2018-04-11 # keepass decrypt experimentation -import struct import hashlib -import argon2 import hmac +import struct + +import argon2 from construct import ( - Byte, Bytes, Int32ul, RepeatUntil, GreedyBytes, Struct, this, Mapping, - Switch, Flag, Prefixed, Int64ul, Int32sl, Int64sl, GreedyString, Padding, - Peek, Checksum, Computed, IfThenElse, Pointer, Tell, If + Byte, + Bytes, + Checksum, + Computed, + Flag, + GreedyBytes, + GreedyString, + If, + IfThenElse, + Int32sl, + Int32ul, + Int64sl, + Int64ul, + Mapping, + Padding, + Peek, + Pointer, + Prefixed, + RepeatUntil, + Struct, + Switch, + Tell, + this, ) + from .common import ( - aes_kdf, Concatenated, AES256Payload, ChaCha20Payload, TwoFishPayload, - DynamicDict, compute_key_composite, Reparsed, Decompressed, - compute_master, CompressionFlags, XML, CipherId, ProtectedStreamId, Unprotect + XML, + AES256Payload, + ChaCha20Payload, + CipherId, + CompressionFlags, + Concatenated, + Decompressed, + DynamicDict, + ProtectedStreamId, + Reparsed, + TwoFishPayload, + Unprotect, + aes_kdf, + compute_key_composite, + compute_master, ) - # -------------------- Key Derivation -------------------- # https://github.com/keepassxreboot/keepassxc/blob/bc55974ff304794e53c925442784c50a2fdaf6ee/src/format/KeePass2.cpp#L30-L33 diff --git a/pykeepass/kdbx_parsing/pytwofish.py b/pykeepass/kdbx_parsing/pytwofish.py index 44671867..92553771 100644 --- a/pykeepass/kdbx_parsing/pytwofish.py +++ b/pykeepass/kdbx_parsing/pytwofish.py @@ -138,6 +138,7 @@ def get_key_size(self): import struct + def rotr32(x, n): return (x >> n) | ((x << (32 - n)) & 0xFFFFFFFF) diff --git a/pykeepass/kdbx_parsing/twofish.py b/pykeepass/kdbx_parsing/twofish.py index c4dc7a7e..99db21a5 100644 --- a/pykeepass/kdbx_parsing/twofish.py +++ b/pykeepass/kdbx_parsing/twofish.py @@ -24,9 +24,10 @@ __all__ = ['Twofish'] -from . import pytwofish -from Cryptodome.Util.strxor import strxor from Cryptodome.Util.Padding import pad +from Cryptodome.Util.strxor import strxor + +from . import pytwofish MODE_ECB = 1 MODE_CBC = 2 diff --git a/pykeepass/pykeepass.py b/pykeepass/pykeepass.py index 23af0475..61a4fb22 100644 --- a/pykeepass/pykeepass.py +++ b/pykeepass/pykeepass.py @@ -6,18 +6,24 @@ import struct import uuid import zlib - from binascii import Error as BinasciiError -from construct import Container, ChecksumError, CheckError -from dateutil import parser, tz from datetime import datetime, timedelta +from pathlib import Path + +from construct import CheckError, ChecksumError, Container +from dateutil import parser, tz from lxml import etree from lxml.builder import E -from pathlib import Path from .attachment import Attachment from .entry import Entry -from .exceptions import BinaryError, CredentialsError, HeaderChecksumError, PayloadChecksumError, UnableToSendToRecycleBin +from .exceptions import ( + BinaryError, + CredentialsError, + HeaderChecksumError, + PayloadChecksumError, + UnableToSendToRecycleBin, +) from .group import Group from .kdbx_parsing import KDBX, kdf_uuids from .xpath import attachment_xp, entry_xp, group_xp, path_xp @@ -411,8 +417,10 @@ def _can_be_moved_to_recyclebin(self, entry_or_group): # ---------- Groups ---------- from .deprecated import ( - find_groups_by_name, find_groups_by_path, find_groups_by_uuid, - find_groups_by_notes + find_groups_by_name, + find_groups_by_notes, + find_groups_by_path, + find_groups_by_uuid, ) def find_groups(self, recursive=True, path=None, group=None, **kwargs): @@ -496,9 +504,14 @@ def empty_group(self, group): from .deprecated import ( - find_entries_by_title, find_entries_by_username, find_entries_by_password, - find_entries_by_url, find_entries_by_path, find_entries_by_notes, - find_entries_by_string, find_entries_by_uuid + find_entries_by_notes, + find_entries_by_password, + find_entries_by_path, + find_entries_by_string, + find_entries_by_title, + find_entries_by_url, + find_entries_by_username, + find_entries_by_uuid, ) def find_entries(self, recursive=True, path=None, group=None, **kwargs): diff --git a/tests/tests.py b/tests/tests.py index 169f3957..595f0a08 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -4,16 +4,15 @@ import unittest import uuid from datetime import datetime, timedelta - -from dateutil import tz +from io import BytesIO from pathlib import Path -from io import BytesIO +from dateutil import tz from pykeepass import PyKeePass, icons from pykeepass.entry import Entry -from pykeepass.group import Group from pykeepass.exceptions import BinaryError, CredentialsError, HeaderChecksumError +from pykeepass.group import Group """ Missing Tests: