def decrypt(self, passwd): orig_content = self.content try: cryer = Crypto(passwd) self.content = cryer.decrypt(self.content) self.__is_encrypted = False except Exception as e: self.__is_encrypted = True self.content = orig_content raise RuntimeError(F"Failed to decrypt: <{e}>")
def test_encrypt_decrypt(self): crypto = Crypto("7h3-7ru7h") msg = "Hello, World!" secret = crypto.encrypt(msg) self.assertIsNotNone(secret) self.assertNotEqual(msg, secret) clear_text = crypto.decrypt(secret) self.assertIsNotNone(clear_text) self.assertEqual(msg, clear_text)
def __init__(self, id=None, **kwargs): self.name = None self.system = None self.sub_system = None self.type = None self.__data = None self.note = None self.__encrypted = False # TODO: Find a better way to manage encryption key??? if not self.ENCRYPTION_KEY: raise Exception("Secret - Encryption Key not set.") self.__cryer = Crypto(self.ENCRYPTION_KEY) super().__init__(id=id, **kwargs)
def setUp(self): self.encryption_key = '0000000' Secret.ENCRYPTION_KEY = self.encryption_key self.crypto = Crypto(self.encryption_key)
class SecretTest(unittest.TestCase): def setUp(self): self.encryption_key = '0000000' Secret.ENCRYPTION_KEY = self.encryption_key self.crypto = Crypto(self.encryption_key) def test_missing_encryption_key(self): Secret.ENCRYPTION_KEY = None with self.assertRaisesRegex(Exception, "Secret - Encryption Key not set"): secret = Secret(name="bad wolf") def test_construction(self): # Set secret 'data' directly secret = Secret( name="LEGO", system="www.lego.com", sub_system="UI", type=Secret.TYPE_USER_PASS, data={'username': '******', 'password': '******'} ) self.assertDictEqual(secret.data, { 'username': '******', 'password': '******' }) # "Forge" secret 'data' from kwargs secret = Secret( name="LEGO", system="api.lego.com", sub_system="REST API", type=Secret.TYPE_KEY_SECRET, key="4c1300c900d1af3de0e67560f542090b", secret="bWluaS1maWcK" ) self.assertDictEqual(secret.data, { 'key': "4c1300c900d1af3de0e67560f542090b", 'secret': "bWluaS1maWcK" }) def test_forge(self): # user-pass secret = Secret.forge( Secret.TYPE_USER_PASS, username="******", password="******" ) self.assertDictEqual(secret, { 'username': "******", 'password': "******" }) # token secret = Secret.forge( Secret.TYPE_TOKEN, token="0xDEADBEEF" ) self.assertDictEqual(secret, {'token': "0xDEADBEEF"}) # key-secret secret = Secret.forge( Secret.TYPE_KEY_SECRET, key="abc12377x", secret="frog blast the vent core" ) self.assertDictEqual(secret, { 'key': "abc12377x", 'secret': "frog blast the vent core" }) def test_forge_invalid_type(self): with self.assertRaisesRegex(TypeError, "Invalid Secret Type: 'base64-encoded-string'"): Secret.forge('base64-encoded-string', string="deciduous manifestations") def test_forge_missing_args(self): with self.assertRaisesRegex(ValueError, "Missing Required Value: 'username'"): Secret.forge( Secret.TYPE_USER_PASS, password="******" ) def test_encryption(self): Secret.purge() stype = Secret.TYPE_USER_PASS username = '******' password = '******' secret = Secret( name="My Email Account", system="email.com", sub_system="UI", type=stype, data={'username': username, 'password': password}, note="Personal Email **ONLY**" ) self.assertEqual(secret.data['username'], username) self.assertEqual(secret.data['password'], password) secret.save() with open('tests/tmp/Secrets-test.json') as file: raw_data = json.load(file).get('_default', {}).get('1', {}).get('data') self.assertNotEqual(raw_data['username'], secret.data['username']) self.assertNotEqual(raw_data['password'], secret.data['password']) self.assertEqual(self.crypto.decrypt(raw_data['username']), secret.data['username']) self.assertEqual(self.crypto.decrypt(raw_data['password']), secret.data['password']) self.assertEqual(secret.data['username'], username) self.assertEqual(secret.data['password'], password) # Load and check stuff secret2 = Secret(id=1) secret2.load() self.assertEqual(secret2.type, stype) self.assertEqual(secret2.data['username'], username) self.assertEqual(secret2.data['password'], password) def test_serialize(self): # set data directly secret = Secret( name="My Email Account", system="email.com", sub_system="UI", type=Secret.TYPE_USER_PASS, data={'username': '******', 'password': '******'}, note="Personal Email **ONLY**" ) data = secret.serialize() self.assertEqual(secret.name, "My Email Account") self.assertEqual(secret.system, "email.com") self.assertEqual(secret.sub_system, "UI") self.assertEqual(secret.type, Secret.TYPE_USER_PASS) self.assertEqual(secret.data['username'], "rufus42") self.assertEqual(secret.data['password'], "y5kqyRrPXUUjS4DM") self.assertEqual(secret.note, "Personal Email **ONLY**") # forge data directly secret = Secret( name="My Email Account", system="email.com", sub_system="UI", type=Secret.TYPE_USER_PASS, username='******', password='******', note="Personal Email **ONLY**" ) data = secret.serialize() self.assertEqual(secret.name, "My Email Account") self.assertEqual(secret.system, "email.com") self.assertEqual(secret.sub_system, "UI") self.assertEqual(secret.type, Secret.TYPE_USER_PASS) self.assertEqual(secret.data['username'], "rufus007") self.assertEqual(secret.data['password'], "y5kqyRrXPXUUjS4DM") self.assertEqual(secret.note, "Personal Email **ONLY**") def test_tagging(self): # Basic instance secret = Secret( name="LEGO", system="www.lego.com", sub_system="UI", type=Secret.TYPE_USER_PASS, data={'username': '******', 'password': '******'}, ) self.assertIsNotNone(secret.tags) self.assertIsInstance(secret.tags, set) self.assertEqual(len(secret.tags), 0) # Create with Tags secret.tag("lego") secret.tag("brick-by-brick") self.assertEqual(len(secret.tags), 2) self.assertIsInstance(list(secret.tags)[0], Tag) secret.save() # Retrieve has Tags secret2 = Secret(id=secret.id) secret2.load() self.assertIsNotNone(secret2.tags) self.assertIsInstance(secret2.tags, set) self.assertEqual(len(secret2.tags), 2) self.assertIsInstance(list(secret2.tags)[0], Tag) self.assertTrue(Tag(name="lego") in secret2.tags) # Update tags secret2.tag("studs-r-us") self.assertEqual(len(secret2.tags), 3) self.assertIsInstance(list(secret2.tags)[2], Tag) secret2.save() secret3 = Secret(id=secret2.id) secret3.load() self.assertIsNotNone(secret3.tags) self.assertIsInstance(secret3.tags, set) self.assertEqual(len(secret3.tags), 3) self.assertTrue(Tag(name="lego") in secret3.tags) self.assertTrue(Tag(name="brick-by-brick") in secret3.tags) self.assertTrue(Tag(name="studs-r-us") in secret3.tags) def test_blot(self): license = 'this is the way the world ends. not with a bang...' secret = Secret( name="Hydra License", system="Hydra", sub_system="license", type=Secret.TYPE_BLOT, data={'content': license} ) self.assertEqual(secret.type, Secret.TYPE_BLOT) self.assertDictEqual(secret.data, { 'content': license })
def test_construct(self): with self.assertRaisesRegex(ValueError, "'password' cannot be empty or None"): Crypto(None)
class Secret(Taggable, Base): TYPE_USER_PASS = '******' TYPE_TOKEN = 'token' TYPE_KEY_SECRET = 'key-secret' TYPE_BLOT = 'blot' ENCRYPTION_KEY = None TEMPLATES = { TYPE_USER_PASS: { 'username': None, 'password': None }, TYPE_TOKEN: { 'token': None, }, TYPE_KEY_SECRET: { 'key': None, 'secret': None }, TYPE_BLOT: { 'content': None } } def __init__(self, id=None, **kwargs): self.name = None self.system = None self.sub_system = None self.type = None self.__data = None self.note = None self.__encrypted = False # TODO: Find a better way to manage encryption key??? if not self.ENCRYPTION_KEY: raise Exception("Secret - Encryption Key not set.") self.__cryer = Crypto(self.ENCRYPTION_KEY) super().__init__(id=id, **kwargs) @property def data(self): if self.__encrypted: self.__decrypt_data() return self.__data @data.setter def data(self, new_data): # Assume new_data is in clear text self.__data = new_data self.__encrypt_data() @classmethod def forge(cls, stype, **kwargs): template = cls.TEMPLATES.get(stype, None) if not template: raise TypeError(F"Invalid Secret Type: '{stype}'") secret = template.copy() for attr in secret.keys(): if attr not in kwargs: raise ValueError( F"Secret.forge - Missing Required Value: '{attr}'") secret[attr] = kwargs.get(attr) return secret def __encrypt_data(self): if self.__data: enc_data = self.__data.copy() for attr in enc_data.keys(): enc_data[attr] = self.__cryer.encrypt(enc_data[attr]) self.__data = enc_data self.__encrypted = True def __decrypt_data(self): if self.__data: clear_data = self.__data.copy() for attr in clear_data.keys(): clear_data[attr] = self.__cryer.decrypt(clear_data[attr]) self.__data = clear_data self.__encrypted = False def _serialize(self): if not self.__encrypted: self.__encrypt_data() data = { 'name': self.name, 'system': self.system, 'sub_system': self.sub_system, 'type': self.type, # MUST access via __data else it will be decrypted 'data': self.__data, 'note': self.note, '__encrypted': self.__encrypted } # Tags data.update(super()._serialize()) return data def rekey(self, new_key): self.__cryer = Crypto(new_key) self.save() def update(self, data): self.name = data.get('name', self.name) self.system = data.get('system', self.system) self.sub_system = data.get('sub_system', self.sub_system) self.type = data.get('type', self.type) # Access via __data so as to not change the underlying data self.__data = data.get('data', self.__data) self.note = data.get('note', self.note) self.__encrypted = data.get('__encrypted', self.__encrypted) self.tags = data.get('tags', self.tags) if self.type and self.data is None: self.data = Secret.forge(self.type, **data)
def rekey(self, new_key): self.__cryer = Crypto(new_key) self.save()