def update_from_sync(self, password): """ Call this method to pull settings from the sync server. :param password: the masterpassword :type password: str """ pull_successful, data = self.sync_manager.pull() if not pull_successful: print("Sync failed: No connection to the server.") return False if not len(data) > 0: return False binary_data = b64decode(data) data_version = binary_data[:1] if data_version == b'\x00': encryption_salt = binary_data[1:33] encrypted_data = binary_data[33:] crypter = Crypter(encryption_salt, password) self.remote_data = json.loads( str(Packer.decompress(crypter.decrypt(encrypted_data)), encoding='utf-8')) self.update_remote = False for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] found = False i = 0 while i < len(self.settings): setting = self.settings[i] if setting.get_domain() == domain_name: found = True if datetime.strptime(data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > setting.get_m_date(): if 'deleted' in data_set and data_set['deleted']: self.settings.pop(i) else: setting.load_from_dict(data_set) setting.set_synced(True) self.update_remote = True i += 1 else: i += 1 else: i += 1 if not found: new_setting = PasswordSetting(domain_name) new_setting.load_from_dict(data_set) new_setting.set_synced(True) self.settings.append(new_setting) for setting in self.settings: found = False for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] if setting.get_domain() == domain_name: found = True if setting.get_m_date() >= datetime.strptime(data_set['mDate'], "%Y-%m-%dT%H:%M:%S"): self.update_remote = True if not found: self.update_remote = True else: print("Unknown data format version! Could not update.")
def update_from_export_data(self, password, data): """ This takes a base64 encoded string of encrypted settings (a blob) and updates the internal list of settings. :param str password: the masterpassword :param str data: base64 encoded data """ binary_data = b64decode(data) data_version = binary_data[:1] if data_version == b'\x00': encryption_salt = binary_data[1:33] encrypted_data = binary_data[:33] crypter = Crypter(encryption_salt, password) self.remote_data = json.loads(str(Packer.decompress(crypter.decrypt(encrypted_data)), encoding='utf-8')) update_remote = False for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] found = False i = 0 while i < len(self.settings): setting = self.settings[i] if setting.get_domain() == domain_name: found = True if datetime.strptime(data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > setting.get_m_date(): if 'deleted' in data_set and data_set['deleted']: self.settings.pop(i) else: setting.load_from_dict(data_set) setting.set_synced(True) i += 1 else: i += 1 update_remote = True else: i += 1 if not found: new_setting = PasswordSetting(domain_name) new_setting.load_from_dict(data_set) new_setting.set_synced(True) self.settings.append(new_setting) for setting in self.settings: found = False for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] if setting.get_domain() == domain_name: found = True if setting.get_m_date() >= datetime.strptime(data_set['mDate'], "%Y-%m-%dT%H:%M:%S"): update_remote = True if not found: update_remote = True return update_remote else: print("Unknown data format version! Could not update.") return False
def test_save_settings_to_file(self): self.manager.get_setting('abc.de') new_setting = PasswordSetting('hugo.com') new_setting.set_length(12) self.manager.save_setting(new_setting) self.manager.save_settings_to_file('xyz') f = open(os.path.expanduser('~/.ctSESAM_test.pws'), 'br') crypter = Crypter('xyz') data = json.loads(Packer.decompress(crypter.decrypt(f.read())).decode('utf8')) f.close() self.assertEqual('abc.de', data['settings']['abc.de']['domain']) self.assertEqual(10, data['settings']['abc.de']['length']) self.assertEqual('hugo.com', data['settings']['hugo.com']['domain']) self.assertEqual(12, data['settings']['hugo.com']['length'])
def test_save_settings_to_file(self): self.manager.get_setting('abc.de') new_setting = PasswordSetting('hugo.com') new_setting.set_length(12) self.manager.set_setting(new_setting) self.manager.save_settings_to_file('xyz') with open(os.path.expanduser('~/.ctSESAM_test.pws'), 'br') as f: data = f.read() crypter = Crypter(data[:32], 'xyz') sync_settings_len = struct.unpack('!I', data[32:36])[0] data = json.loads(Packer.decompress(crypter.decrypt(data[36+sync_settings_len:])).decode('utf8')) self.assertEqual('abc.de', data['settings']['abc.de']['domain']) self.assertEqual(10, data['settings']['abc.de']['length']) self.assertEqual('hugo.com', data['settings']['hugo.com']['domain']) self.assertEqual(12, data['settings']['hugo.com']['length'])
def test_save_settings_to_file(self): self.manager.get_setting('abc.de') new_setting = PasswordSetting('hugo.com') new_setting.set_length(12) self.manager.save_setting(new_setting) self.manager.save_settings_to_file('xyz') f = open(os.path.expanduser('~/.ctSESAM_test.pws'), 'br') crypter = Crypter('xyz') data = json.loads( Packer.decompress(crypter.decrypt(f.read())).decode('utf8')) f.close() self.assertEqual('abc.de', data['settings']['abc.de']['domain']) self.assertEqual(10, data['settings']['abc.de']['length']) self.assertEqual('hugo.com', data['settings']['hugo.com']['domain']) self.assertEqual(12, data['settings']['hugo.com']['length'])
def load_settings_from_file(self, password): """ This loads the saved settings. It is a good idea to call this method the minute you have a password. :param str password: """ if os.path.isfile(self.settings_file): file = open(self.settings_file, 'br') data = file.read() crypter = Crypter(data[:32], password) saved_settings = json.loads( str(Packer.decompress(crypter.decrypt(data[32:])), encoding='utf-8')) for domain_name in saved_settings['settings'].keys(): data_set = saved_settings['settings'][domain_name] found = False i = 0 while i < len(self.settings): setting = self.settings[i] if setting.get_domain() == domain_name: found = True if datetime.strptime( data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > setting.get_m_date(): setting.load_from_dict(data_set) setting.set_synced(setting.get_domain() in saved_settings['synced']) i += 1 if not found: new_setting = PasswordSetting(domain_name) new_setting.load_from_dict(data_set) new_setting.set_synced( new_setting.get_domain() in saved_settings['synced']) self.settings.append(new_setting) file.close()
def test_get_character_set(self): s = PasswordSetting("unit.test") self.assertEqual("c", s.get_character_set()[2]) s.set_custom_character_set("axFLp0") self.assertEqual(6, len(s.get_character_set())) self.assertEqual("F", s.get_character_set()[2]) self.assertEqual("0", s.get_character_set()[5])
def test_salt(self): expected = 'pepper'.encode('utf-8') s = PasswordSetting("unit.test") self.assertEqual(len(expected), len(s.get_salt())) for i in range(len(expected)): self.assertEqual(expected[i], s.get_salt()[i]) s.set_salt("somethingelse".encode('utf-8')) expected = "somethingelse".encode('utf-8') self.assertEqual(len(expected), len(s.get_salt())) for i in range(len(expected)): self.assertEqual(expected[i], s.get_salt()[i])
def load_settings_from_file(self, password): """ This loads the saved settings. It is a good idea to call this method the minute you have a password. :param str password: """ if os.path.isfile(self.settings_file): file = open(self.settings_file, 'br') data = file.read() crypter = Crypter(data[:32], password) saved_settings = json.loads(str(Packer.decompress(crypter.decrypt(data[32:])), encoding='utf-8')) for domain_name in saved_settings['settings'].keys(): data_set = saved_settings['settings'][domain_name] found = False i = 0 while i < len(self.settings): setting = self.settings[i] if setting.get_domain() == domain_name: found = True if datetime.strptime(data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > setting.get_m_date(): setting.load_from_dict(data_set) setting.set_synced(setting.get_domain() in saved_settings['synced']) i += 1 if not found: new_setting = PasswordSetting(domain_name) new_setting.load_from_dict(data_set) new_setting.set_synced(new_setting.get_domain() in saved_settings['synced']) self.settings.append(new_setting) file.close()
def update_from_export_data(self, password, data): """ This takes a base64 encoded string of encrypted settings (a blob) and updates the internal list of settings. :param str password: the masterpassword :param str data: base64 encoded data """ binary_data = b64decode(data) data_version = binary_data[:1] if data_version == b'\x00': encryption_salt = binary_data[1:33] encrypted_data = binary_data[:33] crypter = Crypter(encryption_salt, password) self.remote_data = json.loads( str(Packer.decompress(crypter.decrypt(encrypted_data)), encoding='utf-8')) update_remote = False for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] found = False i = 0 while i < len(self.settings): setting = self.settings[i] if setting.get_domain() == domain_name: found = True if datetime.strptime( data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > setting.get_m_date(): if 'deleted' in data_set and data_set['deleted']: self.settings.pop(i) else: setting.load_from_dict(data_set) setting.set_synced(True) i += 1 else: i += 1 update_remote = True else: i += 1 if not found: new_setting = PasswordSetting(domain_name) new_setting.load_from_dict(data_set) new_setting.set_synced(True) self.settings.append(new_setting) for setting in self.settings: found = False for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] if setting.get_domain() == domain_name: found = True if setting.get_m_date() >= datetime.strptime( data_set['mDate'], "%Y-%m-%dT%H:%M:%S"): update_remote = True if not found: update_remote = True return update_remote else: print("Unknown data format version! Could not update.") return False
def get_setting(self, domain): """ This function always returns a setting. If no setting was stored for the given domain a new PasswordSetting object is created. :param str domain: :return: a setting object :rtype: PasswordSetting """ for setting in self.settings: if setting.get_domain() == domain: return setting setting = PasswordSetting(domain) self.settings.append(setting) return setting
def load_settings_from_file(self, password, omit_sync_settings_questions=False): """ This loads the saved settings. It is a good idea to call this method the minute you have a password. :param str password: masterpassword :param bool omit_sync_settings_questions: do not ask for questions? (Defalut: False) :type password: str """ if os.path.isfile(self.settings_file): file = open(self.settings_file, 'br') data = file.read() crypter = Crypter(data[:32], password) sync_settings_len = struct.unpack('!I', data[32:36])[0] if sync_settings_len > 0: self.sync_manager.load_binary_sync_settings( crypter.decrypt(data[36:36 + sync_settings_len])) saved_settings = json.loads( str(Packer.decompress( crypter.decrypt(data[36 + sync_settings_len:])), encoding='utf-8')) for domain_name in saved_settings['settings'].keys(): data_set = saved_settings['settings'][domain_name] found = False i = 0 while i < len(self.settings): setting = self.settings[i] if setting.get_domain() == domain_name: found = True if datetime.strptime( data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > setting.get_m_date(): setting.load_from_dict(data_set) setting.set_synced(setting.get_domain() in saved_settings['synced']) i += 1 if not found: new_setting = PasswordSetting(domain_name) new_setting.load_from_dict(data_set) new_setting.set_synced( new_setting.get_domain() in saved_settings['synced']) self.settings.append(new_setting) file.close() else: if not omit_sync_settings_questions: self.sync_manager.ask_for_sync_settings()
def load_settings_from_file(self, password, omit_sync_settings_questions=False): """ This loads the saved settings. It is a good idea to call this method the minute you have a password. :param str password: masterpassword :param bool omit_sync_settings_questions: do not ask for questions? (Defalut: False) :type password: str """ if os.path.isfile(self.settings_file): file = open(self.settings_file, 'br') data = file.read() crypter = Crypter(data[:32], password) sync_settings_len = struct.unpack('!I', data[32:36])[0] if sync_settings_len > 0: self.sync_manager.load_binary_sync_settings(crypter.decrypt(data[36:36+sync_settings_len])) saved_settings = json.loads(str(Packer.decompress(crypter.decrypt(data[36+sync_settings_len:])), encoding='utf-8')) for domain_name in saved_settings['settings'].keys(): data_set = saved_settings['settings'][domain_name] found = False i = 0 while i < len(self.settings): setting = self.settings[i] if setting.get_domain() == domain_name: found = True if datetime.strptime(data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > setting.get_m_date(): setting.load_from_dict(data_set) setting.set_synced(setting.get_domain() in saved_settings['synced']) i += 1 if not found: new_setting = PasswordSetting(domain_name) new_setting.load_from_dict(data_set) new_setting.set_synced(new_setting.get_domain() in saved_settings['synced']) self.settings.append(new_setting) file.close() else: if not omit_sync_settings_questions: self.sync_manager.ask_for_sync_settings()
def update_from_sync(self, password): """ Call this method to pull settings from the sync server. :param password: the masterpassword :type password: str """ pull_successful, data = self.sync_manager.pull() if not pull_successful: print("Sync failed: No connection to the server.") return False if not len(data) > 0: return False binary_data = b64decode(data) data_version = binary_data[:1] if data_version == b'\x00': encryption_salt = binary_data[1:33] encrypted_data = binary_data[33:] crypter = Crypter(encryption_salt, password) self.remote_data = json.loads( str(Packer.decompress(crypter.decrypt(encrypted_data)), encoding='utf-8')) self.update_remote = False for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] found = False i = 0 while i < len(self.settings): setting = self.settings[i] if setting.get_domain() == domain_name: found = True if datetime.strptime( data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > setting.get_m_date(): if 'deleted' in data_set and data_set['deleted']: self.settings.pop(i) else: setting.load_from_dict(data_set) setting.set_synced(True) self.update_remote = True i += 1 else: i += 1 else: i += 1 if not found: new_setting = PasswordSetting(domain_name) new_setting.load_from_dict(data_set) new_setting.set_synced(True) self.settings.append(new_setting) for setting in self.settings: found = False for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] if setting.get_domain() == domain_name: found = True if setting.get_m_date() >= datetime.strptime( data_set['mDate'], "%Y-%m-%dT%H:%M:%S"): self.update_remote = True if not found: self.update_remote = True else: print("Unknown data format version! Could not update.")
def test_to_json(self): s = PasswordSetting("unit.test") s.set_modification_date("2005-01-01T01:14:12") s.set_creation_date("2001-01-01T02:14:12") s.set_salt("something".encode('utf-8')) s.set_iterations(213) s.set_length(14) s.set_custom_character_set("XVLCWKHGFQUIAEOSNRTDYÜÖÄPZBMJ") s.set_notes("Some note.") self.assertIn("domain", s.to_dict()) self.assertEqual("unit.test", s.to_dict()["domain"]) self.assertIn("cDate", s.to_dict()) self.assertEqual("2001-01-01T02:14:12", s.to_dict()["cDate"]) self.assertIn("mDate", s.to_dict()) self.assertEqual("2005-01-01T01:14:12", s.to_dict()["mDate"]) self.assertIn("salt", s.to_dict()) self.assertEqual(str(b64encode("something".encode('utf-8')), encoding='utf-8'), s.to_dict()["salt"]) self.assertIn("iterations", s.to_dict()) self.assertEqual(213, s.to_dict()["iterations"]) self.assertIn("length", s.to_dict()) self.assertEqual(14, s.to_dict()["length"]) self.assertIn("usedCharacters", s.to_dict()) self.assertEqual("XVLCWKHGFQUIAEOSNRTDYÜÖÄPZBMJ", s.to_dict()["usedCharacters"]) self.assertIn("notes", s.to_dict()) self.assertEqual("Some note.", s.to_dict()["notes"])
def test_username(self): s = PasswordSetting("unit.test") self.assertEqual("", s.get_username()) s.set_username("Hugo") self.assertEqual("Hugo", s.get_username())
def test_notes(self): s = PasswordSetting("unit.test") self.assertEqual("", s.get_notes()) s.set_notes("Beware of the password!") self.assertEqual("Beware of the password!", s.get_notes())
def test_set_modification_date(self): s = PasswordSetting("unit.test") s.set_creation_date("2007-01-01T02:14:12") s.set_modification_date("2005-01-01T01:14:12") self.assertEqual("2005-01-01T01:14:12", s.get_creation_date()) self.assertEqual("2005-01-01T01:14:12", s.get_modification_date())
def test_load_from_json(self): json_str = "{\"domain\": \"unit.test\", \"username\": \"testilinius\", " +\ "\"notes\": \"interesting note\", \"legacyPassword\": \"rtSr?bS,mi\", " +\ "\"usedCharacters\": \"AEIOUaeiou\", \"iterations\": 5341, " +\ "\"length\": 16, \"salt\": \"ZmFzY2luYXRpbmc=\", " +\ "\"cDate\": \"2001-01-01T02:14:12\", \"mDate\": \"2005-01-01T01:14:12\"}" s = PasswordSetting(json.loads(json_str)["domain"]) s.load_from_dict(json.loads(json_str)) self.assertEquals("unit.test", s.get_domain()) self.assertEquals("testilinius", s.get_username()) self.assertEquals("interesting note", s.get_notes()) self.assertEquals("rtSr?bS,mi", s.get_legacy_password()) self.assertFalse(s.use_lower_case()) self.assertFalse(s.use_upper_case()) self.assertFalse(s.use_digits()) self.assertFalse(s.use_extra()) self.assertTrue(s.use_custom_character_set()) self.assertEquals("AEIOUaeiou", s.get_character_set()) self.assertEquals(5341, s.get_iterations()) self.assertEquals(16, s.get_length()) expected_salt = "fascinating".encode('utf-8') self.assertEqual(len(expected_salt), len(s.get_salt())) for i in range(len(expected_salt)): self.assertEqual(expected_salt[i], s.get_salt()[i]) self.assertEquals("2001-01-01T02:14:12", s.get_creation_date()) self.assertEquals("2005-01-01T01:14:12", s.get_modification_date())
def test_legacy_password(self): s = PasswordSetting("unit.test") self.assertEqual("", s.get_legacy_password()) s.set_legacy_password("K6x/vyG9(p") self.assertEqual("K6x/vyG9(p", s.get_legacy_password())
def test_character_set(self): s = PasswordSetting("unit.test") self.assertFalse(s.use_custom_character_set()) self.assertEqual("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHJKLMNPQRTUVWXYZ" + "0123456789" + "#!\"§$%&/()[]{}=-_+*<>;:.", s.get_character_set()) s.set_custom_character_set("&=Oo0wWsS$#uUvVzZ") self.assertTrue(s.use_custom_character_set()) self.assertEqual("&=Oo0wWsS$#uUvVzZ", s.get_character_set()) s.set_custom_character_set( "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHJKLMNPQRTUVWXYZ" + "0123456789" + "#!\"§$%&/()[]{}=-_+*<>;:.") self.assertFalse(s.use_custom_character_set()) self.assertEqual("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHJKLMNPQRTUVWXYZ" + "0123456789" + "#!\"§$%&/()[]{}=-_+*<>;:.", s.get_character_set()) s.set_use_letters(False) self.assertEqual("0123456789#!\"§$%&/()[]{}=-_+*<>;:.", s.get_character_set()) s.set_use_letters(True) s.set_use_digits(False) s.set_use_extra(False) self.assertEqual("abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRTUVWXYZ", s.get_character_set())