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
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
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()
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()