Example #1
0
    def create_group(self, title, parent=None, icon=1, expires=None):
        """
        This method creates a new group.

        A group title is needed or no group will be created.

        If a parent is given, the group will be created as a sub-group.

        title must be a string, image an unsigned int >0 and parent a Group.
        
        :return: The newly created group.
        :rtype: :class:`keepassdb.model.Group`
        """
        if parent and not isinstance(parent, Group):
            raise TypeError("Parent must be of type Group")
        
        if expires is None:
            expires = const.NEVER
        
        if self.groups:
            group_id = max([g.id for g in self.groups]) + 1
        else:
            group_id = 1
        
        group = Group(id=group_id, title=title, icon=icon, db=self, 
                      created=util.now(), modified=util.now(), accessed=util.now(),
                      expires=expires)
        
        # If no parent is given, just append the new group at the end
        if parent is None:
            group.parent = self.root
            self.root.children.append(group)
            group.level = 0
            self.groups.append(group)
            
        # Else insert the group behind the parent
        else:
            if parent not in self.groups:
                raise ValueError("Group doesn't exist / is not bound to this database.")
            parent.children.append(group)
            group.parent = parent
            group.level = parent.level + 1
            self.groups.insert(self.groups.index(parent) + 1, group)
                
        return group
Example #2
0
    def create_group(self, title, parent=None, icon=1, expires=None):
        """
        This method creates a new group.

        A group title is needed or no group will be created.

        If a parent is given, the group will be created as a sub-group.

        title must be a string, image an unsigned int >0 and parent a Group.
        
        :return: The newly created group.
        :rtype: :class:`keepassdb.model.Group`
        """
        if parent and not isinstance(parent, Group):
            raise TypeError("Parent must be of type Group")

        if expires is None:
            expires = const.NEVER

        if self.groups:
            group_id = max([g.id for g in self.groups]) + 1
        else:
            group_id = 1

        group = Group(id=group_id,
                      title=title,
                      icon=icon,
                      db=self,
                      created=util.now(),
                      modified=util.now(),
                      accessed=util.now(),
                      expires=expires)

        # If no parent is given, just append the new group at the end
        if parent is None:
            group.parent = self.root
            self.root.children.append(group)
            group.level = 0
            self.groups.append(group)

        # Else insert the group behind the parent
        else:
            if parent not in self.groups:
                raise ValueError(
                    "Group doesn't exist / is not bound to this database.")
            parent.children.append(group)
            group.parent = parent
            group.level = parent.level + 1
            self.groups.insert(self.groups.index(parent) + 1, group)

        return group
Example #3
0
    def load_from_buffer(self, buf, password=None, keyfile=None, readonly=False):
        """
        Load a database from passed-in buffer (bytes).

        :param buf: A string (bytes) of the database contents.
        :type buf: str
        :param password: The password for the database.
        :type password: str
        :param keyfile: Path to a keyfile (or a stream) that can be used instead of or in conjunction with password for database.
        :type keyfile: str or file-like object
        :param readonly: Whether to open the database read-only.
        :type readonly: bool
        """
        if password is None and keyfile is None:
            raise ValueError("Password and/or keyfile is required.")
        
        # Save these to use as defaults when saving the database
        self.password = password
        self.keyfile = keyfile
        
        # The header is 124 bytes long, the rest is content
        hdr_len = HeaderStruct.length
        header_bytes = buf[:hdr_len]
        crypted_content = buf[hdr_len:]
        
        self.header = HeaderStruct(header_bytes)
        
        self.log.debug("Extracted header: {0}".format(self.header))
        # Check if the database is supported
        if self.header.version & const.DB_SUPPORTED_VERSION_MASK != const.DB_SUPPORTED_VERSION & const.DB_SUPPORTED_VERSION_MASK:
            raise exc.UnsupportedDatabaseVersion('Unsupported file version: {0}'.format(hex(self.header.version)))
            
        #Actually, only AES is supported.
        if not self.header.flags & HeaderStruct.AES:
            raise exc.UnsupportedDatabaseEncryption('Only AES encryption is supported.')
        
        final_key = util.derive_key(seed_key=self.header.seed_key,
                                    seed_rand=self.header.seed_rand,
                                    rounds=self.header.key_enc_rounds,
                                    password=password, keyfile=keyfile)
        
        # FIXME: Remove this once we've tracked down issues.
        self.log.debug("(load) Final key: {0!r}, pass={1}".format(final_key, password))
        
        decrypted_content = util.decrypt_aes_cbc(crypted_content, key=final_key, iv=self.header.encryption_iv)
        
        # Check if decryption failed
        if ((len(decrypted_content) > const.DB_MAX_CONTENT_LEN) or
            (len(decrypted_content) == 0 and self.header.ngroups > 0)):
            raise exc.IncorrectKey("Decryption failed! The key is wrong or the file is damaged.")
        
        if not self.header.contents_hash == hashlib.sha256(decrypted_content).digest():
            self.log.debug("Decrypted content: {0!r}".format(decrypted_content))
            self.log.error("Hash mismatch. Header hash = {0!r}, hash of contents = {1!r}".format(self.header.contents_hash,                                                                                                 hashlib.sha256(decrypted_content).digest()))
            raise exc.AuthenticationError("Hash test failed. The key is wrong or the file is damaged.")
            
        # First thing (after header) are the group definitions.
        for _i in range(self.header.ngroups):
            gstruct = GroupStruct(decrypted_content)
            self.groups.append(Group.from_struct(gstruct))
            length = len(gstruct)
            decrypted_content = decrypted_content[length:]
        
        # Next come the entry definitions.
        for _i in range(self.header.nentries):
            estruct = EntryStruct(decrypted_content)
            self.entries.append(Entry.from_struct(estruct))
            length = len(estruct)
            decrypted_content = decrypted_content[length:]
            
        # Sets up the hierarchy, relates the group/entry model objects.
        self._bind_model()
Example #4
0
    def load_from_buffer(self,
                         buf,
                         password=None,
                         keyfile=None,
                         readonly=False):
        """
        Load a database from passed-in buffer (bytes).

        :param buf: A string (bytes) of the database contents.
        :type buf: str
        :param password: The password for the database.
        :type password: str
        :param keyfile: Path to a keyfile (or a stream) that can be used instead of or in conjunction with password for database.
        :type keyfile: str or file-like object
        :param readonly: Whether to open the database read-only.
        :type readonly: bool
        """
        if password is None and keyfile is None:
            raise ValueError("Password and/or keyfile is required.")

        # Save these to use as defaults when saving the database
        self.password = password
        self.keyfile = keyfile

        # The header is 124 bytes long, the rest is content
        hdr_len = HeaderStruct.length
        header_bytes = buf[:hdr_len]
        crypted_content = buf[hdr_len:]

        self.header = HeaderStruct(header_bytes)

        self.log.debug("Extracted header: {0}".format(self.header))
        # Check if the database is supported
        if self.header.version & const.DB_SUPPORTED_VERSION_MASK != const.DB_SUPPORTED_VERSION & const.DB_SUPPORTED_VERSION_MASK:
            raise exc.UnsupportedDatabaseVersion(
                'Unsupported file version: {0}'.format(hex(
                    self.header.version)))

        #Actually, only AES is supported.
        if not self.header.flags & HeaderStruct.AES:
            raise exc.UnsupportedDatabaseEncryption(
                'Only AES encryption is supported.')

        final_key = util.derive_key(seed_key=self.header.seed_key,
                                    seed_rand=self.header.seed_rand,
                                    rounds=self.header.key_enc_rounds,
                                    password=password,
                                    keyfile=keyfile)

        # FIXME: Remove this once we've tracked down issues.
        self.log.debug("(load) Final key: {0!r}, pass={1}".format(
            final_key, password))

        decrypted_content = util.decrypt_aes_cbc(crypted_content,
                                                 key=final_key,
                                                 iv=self.header.encryption_iv)

        # Check if decryption failed
        if ((len(decrypted_content) > const.DB_MAX_CONTENT_LEN)
                or (len(decrypted_content) == 0 and self.header.ngroups > 0)):
            raise exc.IncorrectKey(
                "Decryption failed! The key is wrong or the file is damaged.")

        if not self.header.contents_hash == hashlib.sha256(
                decrypted_content).digest():
            self.log.debug(
                "Decrypted content: {0!r}".format(decrypted_content))
            self.log.error(
                "Hash mismatch. Header hash = {0!r}, hash of contents = {1!r}".
                format(self.header.contents_hash,
                       hashlib.sha256(decrypted_content).digest()))
            raise exc.AuthenticationError(
                "Hash test failed. The key is wrong or the file is damaged.")

        # First thing (after header) are the group definitions.
        for _i in range(self.header.ngroups):
            gstruct = GroupStruct(decrypted_content)
            self.groups.append(Group.from_struct(gstruct))
            length = len(gstruct)
            decrypted_content = decrypted_content[length:]

        # Next come the entry definitions.
        for _i in range(self.header.nentries):
            estruct = EntryStruct(decrypted_content)
            self.entries.append(Entry.from_struct(estruct))
            length = len(estruct)
            decrypted_content = decrypted_content[length:]

        # Sets up the hierarchy, relates the group/entry model objects.
        self._bind_model()