def test_load_settings_from_file(self): settings = { 'settings': { 'unit.test': { 'domain': 'unit.test', 'length': 11, 'iterations': 5000, 'notes': 'Nice note!', 'cDate': '2011-02-12T11:07:31', 'mDate': '2011-02-12T11:07:32' }, 'some.domain': { 'domain': 'some.domain', 'length': 4, 'usedCharacters': '6478593021', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:12' } }, 'synced': [] } salt = os.urandom(32) crypter = Crypter(salt, 'xyz') f = open(os.path.expanduser('~/.ctSESAM_test.pws'), 'bw') f.write(salt + struct.pack('!I', 0) + crypter.encrypt(Packer.compress(json.dumps(settings).encode('utf-8')))) f.close() self.manager.load_settings_from_file('xyz') self.assertIn('unit.test', self.manager.get_domain_list()) self.assertIn('some.domain', self.manager.get_domain_list()) self.assertEqual(11, self.manager.get_setting('unit.test').get_length()) self.assertEqual(5000, self.manager.get_setting('unit.test').get_iterations()) self.assertEqual('Nice note!', self.manager.get_setting('unit.test').get_notes()) self.assertEqual(4, self.manager.get_setting('some.domain').get_length()) self.assertEqual('6478593021', self.manager.get_setting('some.domain').get_character_set())
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 save_settings_to_file(self, password): """ This actually saves the settings to a file on the disk. The file is encrypted so you need to supply the password. :param password: masterpassword :type password: str """ salt = os.urandom(32) crypter = Crypter(salt, password) file = open(self.settings_file, 'bw') encrypted_sync_settings = crypter.encrypt( self.sync_manager.get_binary_sync_settings()) file.write( salt + struct.pack('!I', len(encrypted_sync_settings)) + encrypted_sync_settings + crypter.encrypt( Packer.compress(json.dumps(self.get_settings_as_dict())))) file.close() try: import win32con import win32api win32api.SetFileAttributes(self.settings_file, win32con.FILE_ATTRIBUTE_HIDDEN) except ImportError: pass
def get_export_data(self, password): """ This gives you a base64 encoded string of encrypted settings data (the blob). :param str password: :return: encrypted settings blob :rtype: str """ settings_list = self.get_settings_as_dict()['settings'] if self.remote_data: for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] if 'deleted' in data_set and data_set['deleted']: for i, setting_dict in enumerate(settings_list): if setting_dict['domain'] == setting_dict['domain'] and datetime.strptime( data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > datetime.strptime( setting_dict['mDate'], "%Y-%m-%dT%H:%M:%S"): settings_list[i] = data_set if domain_name not in settings_list.keys(): settings_list[domain_name] = { 'mDate': datetime.now(), 'deleted': True } salt = os.urandom(32) crypter = Crypter(salt, password) return b64encode(b'\x00' + salt + crypter.encrypt(Packer.compress(json.dumps(settings_list))))
def get_export_data(self, password, salt=None): """ This gives you a base64 encoded string of encrypted settings data (the blob). :param password: masterpassword :type password: str :param salt: salt for the encryption: This is for testing only! Do not set it normally! :type salt: bytes :return: encrypted settings blob :rtype: str """ settings_list = self.get_settings_as_dict()['settings'] if self.remote_data: for domain_name in self.remote_data.keys(): data_set = self.remote_data[domain_name] if 'deleted' in data_set and data_set['deleted']: for i, setting_dict in enumerate(settings_list): if setting_dict['domain'] == setting_dict[ 'domain'] and datetime.strptime( data_set['mDate'], "%Y-%m-%dT%H:%M:%S") > datetime.strptime( setting_dict['mDate'], "%Y-%m-%dT%H:%M:%S"): settings_list[i] = data_set if domain_name not in settings_list.keys(): settings_list[domain_name] = { 'mDate': datetime.now(), 'deleted': True } if not salt: salt = os.urandom(32) crypter = Crypter(salt, password) return b64encode( b'\x00' + salt + crypter.encrypt(Packer.compress(json.dumps(settings_list))))
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_export_data(self): settings = { 'settings': { 'unit.test': { 'domain': 'unit.test', 'length': 11, 'iterations': 5000, 'notes': 'Nice note!', 'salt': 'cGVwcGVy', 'usedCharacters': 'abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRTUVWXYZ0123456789' + '#!"§$%&/()[]{}=-_+*<>;:.', 'cDate': '2011-02-12T11:07:31', 'mDate': '2011-02-12T11:07:32' }, 'some.domain': { 'domain': 'some.domain', 'length': 4, 'iterations': 4096, 'salt': 'cGVwcGVy', 'usedCharacters': '6478593021', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:12' } }, 'synced': [] } crypter = Crypter('xyz') f = open(os.path.expanduser('~/.ctSESAM_test.pws'), 'bw') f.write(crypter.encrypt(Packer.compress(json.dumps(settings).encode('utf-8')))) f.close() self.manager.load_settings_from_file('xyz') self.assertEqual( b64encode(b'\x00' + crypter.encrypt(Packer.compress(json.dumps(settings['settings']).encode('utf-8')))), self.manager.get_export_data('xyz') )
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 decrypt(): # get the raw post data (it will be parsed in decrypt below) request.get_data() data = request.data # create the crypter object crypter = Crypter(Config.MASTER_SECRET) # decrypt and return the plaintext return crypter.decrypt(data)
def encrypt(): # get the raw post data request.get_data() data = request.data # create the crypter object crypter = Crypter(Config.MASTER_SECRET) # encrypt and return a string holding the ciphertext, iv and salt return crypter.encrypt(data)
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_decrypt(self): ciphertext = b'1lsDkebMaZZeO+/DnvVAUYPmXrQOdCAFQ79C3sElpwamOLtX444tRMiecg4/a9394w51dbmKo89CYKpw19nOaKkbF8Dy' + \ b'll9MQJSRUXZEoc3aoaBvgGBCy4rVM62hEQLfpOUdBcrJTPAU3l8zM8V+AN560z7Rj9gXoGkXsotIpEjNg0+hwdmcVRAw' + \ b'JAiDnAbH7K1Q0olPdkM187tbF5A9OEzCU5M36qzUyr/68a1oGL65JCaMAGoHTpQa2i4DlTEkVF1xPkB40ZF167jo360lEQ==' self.assertEqual(0, len(b64decode(ciphertext)) % 16) password = "******" crypter = Crypter("pepper".encode('utf-8'), password) self.assertEqual(b'Important information with quite some length. ' + b'This message is as long as this because otherwise only one cipher block would ' + b'be encrypted. This long message insures that more than one block is needed.', crypter.decrypt(b64decode(ciphertext)))
def test_decrypt(self): cyphertext = "FRQFCWa38eSIrPnhELojAPrOb8oKzs2yoAbNqVONBEuac3OhUKY12mP+TNyZs1MRUbY9hnqvIG18" + \ "7MqTAVTzI0fCJhmR4stc/k4YpS+HptmzcTgEfXeli56davPUkmJ59yz2vvF3t/pCUOk0qWNQ2vv9" + \ "dU2sJhvOdQ7RVKzbw2DJAFtEM2BxJq8Oqa4mB4sBC/GpIP3xtNxANJPyN8xTSL2F4Ktt5hIcX3AV" + \ "UrnGYSjGeDHGua8iKNFohYtaPj3vvzaSVpGyzAfmlVEdN5/8zQ==" self.assertEqual(0, len(b64decode(cyphertext)) % 16) password = "******" crypter = Crypter(password) self.assertEqual(b'Important information with quite some length. ' + b'This message is as long as this because otherwise only one cipher block would ' + b'be encrypted. This long message insures that more than one block is needed.', crypter.decrypt(b64decode(cyphertext)))
def test_decrypt(self): ciphertext = b'1lsDkebMaZZeO+/DnvVAUYPmXrQOdCAFQ79C3sElpwamOLtX444tRMiecg4/a9394w51dbmKo89CYKpw19nOaKkbF8Dy' + \ b'll9MQJSRUXZEoc3aoaBvgGBCy4rVM62hEQLfpOUdBcrJTPAU3l8zM8V+AN560z7Rj9gXoGkXsotIpEjNg0+hwdmcVRAw' + \ b'JAiDnAbH7K1Q0olPdkM187tbF5A9OEzCU5M36qzUyr/68a1oGL65JCaMAGoHTpQa2i4DlTEkVF1xPkB40ZF167jo360lEQ==' self.assertEqual(0, len(b64decode(ciphertext)) % 16) password = "******" crypter = Crypter("pepper".encode('utf-8'), password) self.assertEqual( b'Important information with quite some length. ' + b'This message is as long as this because otherwise only one cipher block would ' + b'be encrypted. This long message insures that more than one block is needed.', crypter.decrypt(b64decode(ciphertext)))
def test_encrypt(self): message_string = "Important information with quite some length. " + \ "This message is as long as this because otherwise only one cipher block would " + \ "be encrypted. This long message insures that more than one block is needed." password = "******" message = message_string.encode('utf-8') crypter = Crypter(password) ciphertext = crypter.encrypt(message) self.assertEqual( b'FRQFCWa38eSIrPnhELojAPrOb8oKzs2yoAbNqVONBEuac3OhUKY12mP+TNyZs1MRUbY9hnqvIG18' + b'7MqTAVTzI0fCJhmR4stc/k4YpS+HptmzcTgEfXeli56davPUkmJ59yz2vvF3t/pCUOk0qWNQ2vv9' + b'dU2sJhvOdQ7RVKzbw2DJAFtEM2BxJq8Oqa4mB4sBC/GpIP3xtNxANJPyN8xTSL2F4Ktt5hIcX3AV' + b'UrnGYSjGeDHGua8iKNFohYtaPj3vvzaSVpGyzAfmlVEdN5/8zQ==', b64encode(ciphertext))
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_decrypt(self): cyphertext = "FRQFCWa38eSIrPnhELojAPrOb8oKzs2yoAbNqVONBEuac3OhUKY12mP+TNyZs1MRUbY9hnqvIG18" + \ "7MqTAVTzI0fCJhmR4stc/k4YpS+HptmzcTgEfXeli56davPUkmJ59yz2vvF3t/pCUOk0qWNQ2vv9" + \ "dU2sJhvOdQ7RVKzbw2DJAFtEM2BxJq8Oqa4mB4sBC/GpIP3xtNxANJPyN8xTSL2F4Ktt5hIcX3AV" + \ "UrnGYSjGeDHGua8iKNFohYtaPj3vvzaSVpGyzAfmlVEdN5/8zQ==" self.assertEqual(0, len(b64decode(cyphertext)) % 16) password = "******" crypter = Crypter(password) self.assertEqual( b'Important information with quite some length. ' + b'This message is as long as this because otherwise only one cipher block would ' + b'be encrypted. This long message insures that more than one block is needed.', crypter.decrypt(b64decode(cyphertext)))
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_get_domain_list(self): settings = { 'settings': { 'unit.test': { 'domain': 'unit.test', 'length': 11, 'iterations': 5000, 'notes': 'Nice note!', 'salt': 'cGVwcGVy', 'usedCharacters': 'abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRTUVWXYZ0123456789' + '#!"§$%&/()[]{}=-_+*<>;:.', 'cDate': '2011-02-12T11:07:31', 'mDate': '2011-02-12T11:07:32' }, 'some.domain': { 'domain': 'some.domain', 'length': 4, 'iterations': 4096, 'salt': 'cGVwcGVy', 'usedCharacters': '6478593021', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:12' } }, 'synced': [] } crypter = Crypter('xyz') f = open(os.path.expanduser('~/.ctSESAM_test.pws'), 'bw') f.write( crypter.encrypt( Packer.compress(json.dumps(settings).encode('utf-8')))) f.close() self.manager.load_settings_from_file('xyz') self.assertEqual( settings['settings']['unit.test'], self.manager.get_settings_as_dict()['settings']['unit.test']) self.assertEqual( settings['settings']['some.domain'], self.manager.get_settings_as_dict()['settings']['some.domain']) self.assertEqual(settings, self.manager.get_settings_as_dict())
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 save_settings_to_file(self, password): """ This actually saves the settings to a file on the disk. The file is encrypted so you need to supply the password. :param str password: """ salt = os.urandom(32) crypter = Crypter(salt, password) file = open(self.settings_file, 'bw') file.write(salt + crypter.encrypt(Packer.compress(json.dumps(self.get_settings_as_dict())))) file.close() try: import win32con import win32api win32api.SetFileAttributes(self.settings_file, win32con.FILE_ATTRIBUTE_HIDDEN) except ImportError: pass
def save_settings_to_file(self, password): """ This actually saves the settings to a file on the disk. The file is encrypted so you need to supply the password. :param str password: """ salt = os.urandom(32) crypter = Crypter(salt, password) file = open(self.settings_file, 'bw') file.write(salt + crypter.encrypt( Packer.compress(json.dumps(self.get_settings_as_dict())))) file.close() try: import win32con import win32api win32api.SetFileAttributes(self.settings_file, win32con.FILE_ATTRIBUTE_HIDDEN) except ImportError: pass
def test_update_from_sync(self): settings = { 'settings': { 'unit.test': { 'domain': 'unit.test', 'length': 11, 'iterations': 5000, 'notes': 'Nice note!', 'salt': 'cGVwcGVy', 'usedCharacters': 'abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRTUVWXYZ0123456789' + '#!"§$%&/()[]{}=-_+*<>;:.', 'cDate': '2011-02-12T11:07:31', 'mDate': '2011-02-12T11:07:32' }, 'some.domain': { 'domain': 'some.domain', 'length': 4, 'iterations': 4096, 'salt': 'cGVwcGVy', 'usedCharacters': '6478593021', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:12' } }, 'synced': [] } salt = os.urandom(32) crypter = Crypter(salt, 'xyz') f = open(os.path.expanduser('~/.ctSESAM_test.pws'), 'bw') f.write(salt + struct.pack('!I', 0) + crypter.encrypt( Packer.compress(json.dumps(settings).encode('utf-8')))) f.close() self.manager.load_settings_from_file('xyz') self.manager.sync_manager = MockSyncManager() self.manager.update_from_sync('xyz') self.assertIn('unit.test', self.manager.get_domain_list()) self.assertIn('some.domain', self.manager.get_domain_list()) self.assertIn('third.domain', self.manager.get_domain_list()) self.assertEqual(5001, self.manager.get_setting('unit.test').get_iterations()) self.assertEqual(4096, self.manager.get_setting('some.domain').get_iterations()) self.assertEqual(4098, self.manager.get_setting('third.domain').get_iterations())
def pull(self): """ Returns some mock data tor the sync test. :return: base64 mock data blob :rtype: (bool, str) """ remote_data = { 'unit.test': { 'domain': 'unit.test', 'length': 12, 'iterations': 5001, 'notes': 'another note!', 'salt': 'cGVwcGVy', 'usedCharacters': 'abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRTUVWXYZ0123456789', 'cDate': '2011-02-12T11:07:31', 'mDate': '2013-07-12T14:46:11' }, 'some.domain': { 'domain': 'some.domain', 'length': 4, 'iterations': 4097, 'salt': 'cGVwcGVy', 'usedCharacters': '6478593021', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:11' }, 'third.domain': { 'domain': 'third.domain', 'length': 10, 'iterations': 4098, 'salt': 'cGVwcGVy', 'usedCharacters': 'aeiou', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:11' } } salt = os.urandom(32) crypter = Crypter(salt, 'xyz') return True, str(b64encode(b'\x00' + salt + crypter.encrypt( Packer.compress(json.dumps(remote_data).encode('utf-8')))), encoding='utf-8')
def save_settings_to_file(self, password): """ This actually saves the settings to a file on the disk. The file is encrypted so you need to supply the password. :param password: masterpassword :type password: str """ salt = os.urandom(32) crypter = Crypter(salt, password) file = open(self.settings_file, 'bw') encrypted_sync_settings = crypter.encrypt(self.sync_manager.get_binary_sync_settings()) file.write(salt + struct.pack('!I', len(encrypted_sync_settings)) + encrypted_sync_settings + crypter.encrypt(Packer.compress(json.dumps(self.get_settings_as_dict())))) file.close() try: import win32con import win32api win32api.SetFileAttributes(self.settings_file, win32con.FILE_ATTRIBUTE_HIDDEN) except ImportError: pass
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 __init__(self,domain): self.domain = domain self.url = None self.username = None self.legacyPassword = None self.notes = None self.iterations = 4096 self.salt = Crypter.createSalt() self.creationDate = datetime.now() self.modificationDate = self.creationDate self.extraCharacters = DEFAULT_CHARACTER_SET_EXTRA self.template = 'x' * 10 self.calculate_template(True, True, True, True) self.synced = False
def test_get_export_data(self): settings = { 'settings': { 'unit.test': { 'domain': 'unit.test', 'length': 11, 'iterations': 5000, 'notes': 'Nice note!', 'salt': 'cGVwcGVy', 'usedCharacters': 'abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRTUVWXYZ0123456789' + '#!"§$%&/()[]{}=-_+*<>;:.', 'cDate': '2011-02-12T11:07:31', 'mDate': '2011-02-12T11:07:32' }, 'some.domain': { 'domain': 'some.domain', 'length': 4, 'iterations': 4096, 'salt': 'cGVwcGVy', 'usedCharacters': '6478593021', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:12' } }, 'synced': [] } salt = os.urandom(32) crypter = Crypter(salt, 'xyz') f = open(os.path.expanduser('~/.ctSESAM_test.pws'), 'bw') f.write(salt + struct.pack('!I', 0) + crypter.encrypt( Packer.compress(json.dumps(settings).encode('utf-8')))) f.close() self.manager.load_settings_from_file('xyz') data = b64decode(self.manager.get_export_data('xyz')) salt = data[1:33] crypter = Crypter(salt, 'xyz') self.assertEqual( settings['settings'], json.loads(str(Packer.decompress(crypter.decrypt(data[33:])), encoding='utf-8')))
def test_update_from_export_data(self): remote_data = { 'unit.test': { 'domain': 'unit.test', 'length': 12, 'iterations': 5001, 'notes': 'another note!', 'salt': 'cGVwcGVy', 'usedCharacters': 'abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRTUVWXYZ0123456789', 'cDate': '2011-02-12T11:07:31', 'mDate': '2013-07-12T14:46:11' }, 'some.domain': { 'domain': 'some.domain', 'length': 4, 'iterations': 4097, 'salt': 'cGVwcGVy', 'usedCharacters': '6478593021', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:11' }, 'third.domain': { 'domain': 'third.domain', 'length': 10, 'iterations': 4098, 'salt': 'cGVwcGVy', 'usedCharacters': 'aeiou', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:11' } } settings = { 'settings': { 'unit.test': { 'domain': 'unit.test', 'length': 11, 'iterations': 5000, 'notes': 'Nice note!', 'salt': 'cGVwcGVy', 'usedCharacters': 'abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRTUVWXYZ0123456789' + '#!"§$%&/()[]{}=-_+*<>;:.', 'cDate': '2011-02-12T11:07:31', 'mDate': '2011-02-12T11:07:32' }, 'some.domain': { 'domain': 'some.domain', 'length': 4, 'iterations': 4096, 'salt': 'cGVwcGVy', 'usedCharacters': '6478593021', 'cDate': '2013-06-17T04:03:41', 'mDate': '2014-08-02T10:37:12' } }, 'synced': [] } crypter = Crypter('xyz') f = open(os.path.expanduser('~/.ctSESAM_test.pws'), 'bw') f.write( crypter.encrypt( Packer.compress(json.dumps(settings).encode('utf-8')))) f.close() self.manager.load_settings_from_file('xyz') self.assertTrue( self.manager.update_from_export_data( 'xyz', b64encode(b'\x00' + crypter.encrypt( Packer.compress(json.dumps(remote_data).encode('utf-8'))))) ) self.assertEqual(['unit.test', 'some.domain', 'third.domain'], self.manager.get_domain_list()) self.assertEqual( 5001, self.manager.get_setting('unit.test').get_iterations()) self.assertEqual( 4096, self.manager.get_setting('some.domain').get_iterations()) self.assertEqual( 4098, self.manager.get_setting('third.domain').get_iterations())
''' app = wx.App() error_dialog = wx.MessageDialog(None, str(message), "Error", wx.OK | wx.ICON_ERROR) error_dialog.ShowModal() app.MainLoop() # GO if __name__ == "__main__": ## START try: mutex = Mutex() go = Crypter() # Could not acquire mutex except MutexAlreadyAcquired as maa: showErrorDialog("The file is corrupt and cannot be opened") sys.exit() # Exception except Exception as ex: if "--debug" in sys.argv: exc_type, exc_obj, exc_tb = sys.exc_info() msg = "Exception encountered!\n\n" msg += "Exception: %s\n" % ex msg += "Type: %s\n" % exc_type.__name__ msg += "Traceback: %s" % "".join(traceback.format_tb(exc_tb)) showErrorDialog(msg) sys.exit()
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.")