def create_entry(self, group = None, title = "", image = 1, url = "", username = "", password = "", comment = "", y = 2999, mon = 12, d = 28, h = 23, min_ = 59, s = 59): """This method creates a new entry. The group which should hold the entry is needed. image must be an unsigned int >0, group a v1Group. It is possible to give an expire date in the following way: - y is the year between 1 and 9999 inclusive - mon is the month between 1 and 12 - d is a day in the given month - h is a hour between 0 and 23 - min_ is a minute between 0 and 59 - s is a second between 0 and 59 The special date 2999-12-28 23:59:59 means that entry expires never. """ if (type(title) is not str or type(image) is not int or image < 0 or type(url) is not str or type(username) is not str or type(password) is not str or type(comment) is not str or type(y) is not int or type(mon) is not int or type(d) is not int or type(h) is not int or type(min_) is not int or type(s) is not int or type(group) is not v1Group): raise KPError("One argument has not a valid type.") elif group not in self.groups: raise KPError("Group doesn't exist.") elif (y > 9999 or y < 1 or mon > 12 or mon < 1 or d > 31 or d < 1 or h > 23 or h < 0 or min_ > 59 or min_ < 0 or s > 59 or s < 0): raise KPError("No legal date") elif (((mon == 1 or mon == 3 or mon == 5 or mon == 7 or mon == 8 or mon == 10 or mon == 12) and d > 31) or ((mon == 4 or mon == 6 or mon == 9 or mon == 11) and d > 30) or (mon == 2 and d > 28)): raise KPError("Given day doesn't exist in given month") Random.atfork() uuid = Random.get_random_bytes(16) entry = v1Entry(group.id_, group, image, title, url, username, password, comment, datetime.now().replace(microsecond = 0), datetime.now().replace(microsecond = 0), datetime.now().replace(microsecond = 0), datetime(y, mon, d, h, min_, s), uuid) self.entries.append(entry) group.entries.append(entry) self._num_entries += 1 return True
def load(self, buf = None): """This method opens an existing database. self.password/self.keyfile and self.filepath must be set. """ if self.password is None and self.keyfile is None: raise KPError('Need a password or keyfile') elif self.filepath is None and buf is None: raise KPError('Can only load an existing database!') if buf is None: buf = self.read_buf() # The header is 124 bytes long, the rest is content header = buf[:124] crypted_content = buf[124:] del buf # The header holds two signatures if not (struct.unpack('<I', header[:4])[0] == 0x9AA2D903 and struct.unpack('<I', header[4:8])[0] == 0xB54BFB65): del crypted_content del header raise KPError('Wrong signatures!') # Unpack the header self._enc_flag = struct.unpack('<I', header[8:12])[0] self._version = struct.unpack('<I', header[12:16])[0] self._final_randomseed = struct.unpack('<16s', header[16:32])[0] self._enc_iv = struct.unpack('<16s', header[32:48])[0] self._num_groups = struct.unpack('<I', header[48:52])[0] self._num_entries = struct.unpack('<I', header[52:56])[0] self._contents_hash = struct.unpack('<32s', header[56:88])[0] self._transf_randomseed = struct.unpack('<32s', header[88:120])[0] self._key_transf_rounds = struct.unpack('<I', header[120:124])[0] del header # Check if the database is supported if self._version & 0xFFFFFF00 != 0x00030002 & 0xFFFFFF00: del crypted_content raise KPError('Unsupported file version!') #Actually, only AES is supported. elif not self._enc_flag & 2: del crypted_content raise KPError('Unsupported file encryption!') if self.password is None: masterkey = self._get_filekey() elif self.password is not None and self.keyfile is not None: passwordkey = self._get_passwordkey() filekey = self._get_filekey() sha = SHA256.new() sha.update(passwordkey+filekey) masterkey = sha.digest() else: masterkey = self._get_passwordkey() # Create the key that is needed to... final_key = self._transform_key(masterkey) # ...decrypt the content decrypted_content = self._cbc_decrypt(final_key, crypted_content) # Check if decryption failed if ((len(decrypted_content) > 2147483446) or (len(decrypted_content) == 0 and self._num_groups > 0)): del decrypted_content del crypted_content raise KPError("Decryption failed!\nThe key is wrong or the file is" " damaged.") sha_obj = SHA256.new() sha_obj.update(decrypted_content) if not self._contents_hash == sha_obj.digest(): del masterkey del final_key raise KPError("Hash test failed.\nThe key is wrong or the file is " "damaged.") del masterkey del final_key # Read out the groups pos = 0 levels = [] cur_group = 0 group = v1Group() while cur_group < self._num_groups: # Every group is made up of single fields field_type = struct.unpack('<H', decrypted_content[:2])[0] decrypted_content = decrypted_content[2:] pos += 2 # Check if offset is alright if pos >= len(crypted_content)+124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G1]') field_size = struct.unpack('<I', decrypted_content[:4])[0] decrypted_content = decrypted_content[4:] pos += 4 if pos >= len(crypted_content)+124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G2]') # Finally read out the content b_ret = self._read_group_field(group, levels, field_type, field_size, decrypted_content) # If the end of a group is reached append it to the groups array if field_type == 0xFFFF and b_ret == True: group.db = self self.groups.append(group) group = v1Group() cur_group += 1 decrypted_content = decrypted_content[field_size:] if pos >= len(crypted_content)+124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G1]') # Now the same with the entries cur_entry = 0 entry = v1Entry() while cur_entry < self._num_entries: field_type = struct.unpack('<H', decrypted_content[:2])[0] decrypted_content = decrypted_content[2:] pos += 2 if pos >= len(crypted_content)+124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G1]') field_size = struct.unpack('<I', decrypted_content[:4])[0] decrypted_content = decrypted_content[4:] pos += 4 if pos >= len(crypted_content)+124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G2]') b_ret = self._read_entry_field(entry, field_type, field_size, decrypted_content) if field_type == 0xFFFF and b_ret == True: self.entries.append(entry) if entry.group_id is None: del decrypted_content del crypted_content raise KPError("Found entry without group!") entry = v1Entry() cur_entry += 1 decrypted_content = decrypted_content[field_size:] pos += field_size if pos >= len(crypted_content)+124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G1]') if self._create_group_tree(levels) is False: del decrypted_content del crypted_content return False del decrypted_content del crypted_content if self.filepath is not None: with open(self.filepath+'.lock', 'w') as handler: handler.write('') return True
def load(self, buf=None): """This method opens an existing database. self.password/self.keyfile and self.filepath must be set. """ if self.password is None and self.keyfile is None: raise KPError('Need a password or keyfile') elif self.filepath is None and buf is None: raise KPError('Can only load an existing database!') if buf is None: buf = self.read_buf() # The header is 124 bytes long, the rest is content header = buf[:124] crypted_content = buf[124:] del buf # The header holds two signatures if not (struct.unpack('<I', header[:4])[0] == 0x9AA2D903 and struct.unpack('<I', header[4:8])[0] == 0xB54BFB65): del crypted_content del header raise KPError('Wrong signatures!') # Unpack the header self._enc_flag = struct.unpack('<I', header[8:12])[0] self._version = struct.unpack('<I', header[12:16])[0] self._final_randomseed = struct.unpack('<16s', header[16:32])[0] self._enc_iv = struct.unpack('<16s', header[32:48])[0] self._num_groups = struct.unpack('<I', header[48:52])[0] self._num_entries = struct.unpack('<I', header[52:56])[0] self._contents_hash = struct.unpack('<32s', header[56:88])[0] self._transf_randomseed = struct.unpack('<32s', header[88:120])[0] self._key_transf_rounds = struct.unpack('<I', header[120:124])[0] del header # Check if the database is supported if self._version & 0xFFFFFF00 != 0x00030002 & 0xFFFFFF00: del crypted_content raise KPError('Unsupported file version!') #Actually, only AES is supported. elif not self._enc_flag & 2: del crypted_content raise KPError('Unsupported file encryption!') if self.password is None: masterkey = self._get_filekey() elif self.password is not None and self.keyfile is not None: passwordkey = self._get_passwordkey() filekey = self._get_filekey() sha = SHA256.new() sha.update(passwordkey + filekey) masterkey = sha.digest() else: masterkey = self._get_passwordkey() # Create the key that is needed to... final_key = self._transform_key(masterkey) # ...decrypt the content decrypted_content = self._cbc_decrypt(final_key, crypted_content) # Check if decryption failed if ((len(decrypted_content) > 2147483446) or (len(decrypted_content) == 0 and self._num_groups > 0)): del decrypted_content del crypted_content raise KPError("Decryption failed!\nThe key is wrong or the file is" " damaged.") sha_obj = SHA256.new() sha_obj.update(decrypted_content) if not self._contents_hash == sha_obj.digest(): del masterkey del final_key raise KPError("Hash test failed.\nThe key is wrong or the file is " "damaged.") del masterkey del final_key # Read out the groups pos = 0 levels = [] cur_group = 0 group = v1Group() while cur_group < self._num_groups: # Every group is made up of single fields field_type = struct.unpack('<H', decrypted_content[:2])[0] decrypted_content = decrypted_content[2:] pos += 2 # Check if offset is alright if pos >= len(crypted_content) + 124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G1]') field_size = struct.unpack('<I', decrypted_content[:4])[0] decrypted_content = decrypted_content[4:] pos += 4 if pos >= len(crypted_content) + 124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G2]') # Finally read out the content b_ret = self._read_group_field(group, levels, field_type, field_size, decrypted_content) # If the end of a group is reached append it to the groups array if field_type == 0xFFFF and b_ret == True: group.db = self self.groups.append(group) group = v1Group() cur_group += 1 decrypted_content = decrypted_content[field_size:] if pos >= len(crypted_content) + 124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G1]') # Now the same with the entries cur_entry = 0 entry = v1Entry() while cur_entry < self._num_entries: field_type = struct.unpack('<H', decrypted_content[:2])[0] decrypted_content = decrypted_content[2:] pos += 2 if pos >= len(crypted_content) + 124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G1]') field_size = struct.unpack('<I', decrypted_content[:4])[0] decrypted_content = decrypted_content[4:] pos += 4 if pos >= len(crypted_content) + 124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G2]') b_ret = self._read_entry_field(entry, field_type, field_size, decrypted_content) if field_type == 0xFFFF and b_ret == True: self.entries.append(entry) if entry.group_id is None: del decrypted_content del crypted_content raise KPError("Found entry without group!") entry = v1Entry() cur_entry += 1 decrypted_content = decrypted_content[field_size:] pos += field_size if pos >= len(crypted_content) + 124: del decrypted_content del crypted_content raise KPError('Unexpected error: Offset is out of range.[G1]') if self._create_group_tree(levels) is False: del decrypted_content del crypted_content return False del decrypted_content del crypted_content if self.filepath is not None: with open(self.filepath + '.lock', 'w') as handler: handler.write('') return True