def setUpClass(cls): super(ActionExecutionControllerTestCaseAuthEnabled, cls).setUpClass() cls.action = copy.deepcopy(ACTION_1) headers = { 'content-type': 'application/json', 'X-Auth-Token': str(SYS_TOKEN.token) } post_resp = cls.app.post_json('/v1/actions', cls.action, headers=headers) cls.action['id'] = post_resp.json['id'] cls.action_encrypt = copy.deepcopy(ACTION_DEFAULT_ENCRYPT) post_resp = cls.app.post_json('/v1/actions', cls.action_encrypt, headers=headers) cls.action_encrypt['id'] = post_resp.json['id'] FixturesLoader().save_fixtures_to_db(fixtures_pack=FIXTURES_PACK, fixtures_dict=FIXTURES) # register datastore values which are used in this tests KeyValuePairAPI._setup_crypto() register_items = [ { 'name': 'secret', 'secret': True, 'value': crypto_utils.symmetric_encrypt(KeyValuePairAPI.crypto_key, 'foo') }, { 'name': 'user1:secret', 'secret': True, 'scope': FULL_USER_SCOPE, 'value': crypto_utils.symmetric_encrypt(KeyValuePairAPI.crypto_key, 'bar') }, ] cls.kvps = [ KeyValuePair.add_or_update(KeyValuePairDB(**x)) for x in register_items ]
def setUpClass(cls): super(ActionExecutionControllerTestCaseAuthEnabled, cls).setUpClass() cls.action = copy.deepcopy(ACTION_1) headers = { "content-type": "application/json", "X-Auth-Token": str(SYS_TOKEN.token), } post_resp = cls.app.post_json("/v1/actions", cls.action, headers=headers) cls.action["id"] = post_resp.json["id"] cls.action_encrypt = copy.deepcopy(ACTION_DEFAULT_ENCRYPT) post_resp = cls.app.post_json("/v1/actions", cls.action_encrypt, headers=headers) cls.action_encrypt["id"] = post_resp.json["id"] FixturesLoader().save_fixtures_to_db(fixtures_pack=FIXTURES_PACK, fixtures_dict=FIXTURES) # register datastore values which are used in this tests KeyValuePairAPI._setup_crypto() register_items = [ { "name": "secret", "secret": True, "value": crypto_utils.symmetric_encrypt(KeyValuePairAPI.crypto_key, "foo"), }, { "name": "user1:secret", "secret": True, "scope": FULL_USER_SCOPE, "value": crypto_utils.symmetric_encrypt(KeyValuePairAPI.crypto_key, "bar"), }, ] cls.kvps = [ KeyValuePair.add_or_update(KeyValuePairDB(**x)) for x in register_items ]
def setUpClass(cls): super(SystemScopeDatastoreFunctionTest, cls).setUpClass() user = auth_db.UserDB(name="stanley") user.save() scope = kvp_const.FULL_SYSTEM_SCOPE cls.kvps = {} # Plain key keys = {"foo": "bar", "foo_empty": "", "foo_null": None} for k, v in six.iteritems(keys): instance = kvp_db.KeyValuePairDB(name=k, value=v, scope=scope) cls.kvps[k] = kvp_db_access.KeyValuePair.add_or_update(instance) # Secret key keys = {"fu": "bar", "fu_empty": ""} for k, v in six.iteritems(keys): value = crypto.symmetric_encrypt( kvp_api.KeyValuePairAPI.crypto_key, v) instance = kvp_db.KeyValuePairDB(name=k, value=value, scope=scope, secret=True) cls.kvps[k] = kvp_db_access.KeyValuePair.add_or_update(instance)
def setUpClass(cls): super(UserScopeDatastoreFunctionTest, cls).setUpClass() user = auth_db.UserDB(name='stanley') user.save() scope = kvp_const.FULL_USER_SCOPE cls.kvps = {} # Plain keys keys = { 'stanley:foo': 'bar', 'stanley:foo_empty': '', 'stanley:foo_null': None } for k, v in six.iteritems(keys): instance = kvp_db.KeyValuePairDB(name=k, value=v, scope=scope) cls.kvps[k] = kvp_db_access.KeyValuePair.add_or_update(instance) # Secret key keys = {'stanley:fu': 'bar', 'stanley:fu_empty': ''} for k, v in six.iteritems(keys): value = crypto.symmetric_encrypt( kvp_api.KeyValuePairAPI.crypto_key, v) instance = kvp_db.KeyValuePairDB(name=k, value=value, scope=scope, secret=True) cls.kvps[k] = kvp_db_access.KeyValuePair.add_or_update(instance)
def test_symmetric_encrypt_decrypt(self): original = 'secret' crypto = crypto_utils.symmetric_encrypt( CryptoUtilsTestCase.test_crypto_key, original) plain = crypto_utils.symmetric_decrypt( CryptoUtilsTestCase.test_crypto_key, crypto) self.assertEqual(plain, original)
def setUpClass(cls): super(UserScopeDatastoreFunctionTest, cls).setUpClass() user = auth_db.UserDB(name='stanley') user.save() scope = kvp_const.FULL_USER_SCOPE cls.kvps = {} # Plain keys keys = { 'stanley:foo': 'bar', 'stanley:foo_empty': '', 'stanley:foo_null': None } for k, v in six.iteritems(keys): instance = kvp_db.KeyValuePairDB(name=k, value=v, scope=scope) cls.kvps[k] = kvp_db_access.KeyValuePair.add_or_update(instance) # Secret key keys = { 'stanley:fu': 'bar', 'stanley:fu_empty': '' } for k, v in six.iteritems(keys): value = crypto.symmetric_encrypt(kvp_api.KeyValuePairAPI.crypto_key, v) instance = kvp_db.KeyValuePairDB(name=k, value=value, scope=scope, secret=True) cls.kvps[k] = kvp_db_access.KeyValuePair.add_or_update(instance)
def to_model(cls, kvp): if not KeyValuePairAPI.crypto_setup: KeyValuePairAPI._setup_crypto() name = getattr(kvp, 'name', None) description = getattr(kvp, 'description', None) value = kvp.value secret = False if getattr(kvp, 'ttl', None): expire_timestamp = (date_utils.get_datetime_utc_now() + datetime.timedelta(seconds=kvp.ttl)) else: expire_timestamp = None if getattr(kvp, 'secret', False): if not KeyValuePairAPI.crypto_key: msg = ('Crypto key not found in %s. Unable to encrypt value for key %s.' % (KeyValuePairAPI.crypto_key_path, name)) raise CryptoKeyNotSetupException(msg) value = symmetric_encrypt(KeyValuePairAPI.crypto_key, value) secret = True model = cls.model(name=name, description=description, value=value, secret=secret, expire_timestamp=expire_timestamp) return model
def test_encrypt_output_is_diff_due_to_diff_IV(self): original = 'Kami is a little boy.' cryptos = set() for _ in range(0, 10000): crypto = symmetric_encrypt(CryptoUtilsTestCase.test_crypto_key, original) self.assertTrue(crypto not in cryptos) cryptos.add(crypto)
def test_encrypt_output_is_diff_due_to_diff_IV(self): original = 'Kami is a little boy.' cryptos = set() for _ in range(0, 10000): crypto = symmetric_encrypt(CryptoUtilsTestCase.test_crypto_key, original) self.assertNotIn(crypto, cryptos) cryptos.add(crypto)
def setUp(self): super(JinjaUtilsDecryptTestCase, self).setUp() crypto_key_path = cfg.CONF.keyvalue.encryption_key_path crypto_key = read_crypto_key(key_path=crypto_key_path) self.secret = 'Build a wall' self.secret_value = symmetric_encrypt(encrypt_key=crypto_key, plaintext=self.secret) self.env = jinja_utils.get_jinja_environment()
def test_get_config_dynamic_config_item_under_additional_properties(self): pack_name = "dummy_pack_schema_with_additional_properties_1" loader = ContentPackConfigLoader(pack_name=pack_name) encrypted_value = crypto.symmetric_encrypt(KeyValuePairAPI.crypto_key, "v1_encrypted") KeyValuePair.add_or_update( KeyValuePairDB(name="k1_encrypted", value=encrypted_value, secret=True)) #################### # values in objects under an object with additionalProperties values = { "profiles": { "dev": { # no host or port to test default value "token": "hard-coded-secret", }, "prod": { "host": "127.1.2.7", "port": 8282, # encrypted in datastore "token": "{{st2kv.system.k1_encrypted}}", # schema declares `secret: true` which triggers auto-decryption. # If this were not encrypted, it would try to decrypt it and fail. }, } } config_db = ConfigDB(pack=pack_name, values=values) config_db = Config.add_or_update(config_db) config_rendered = loader.get_config() self.assertEqual( config_rendered, { "region": "us-east-1", "profiles": { "dev": { "host": "127.0.0.3", "port": 8080, "token": "hard-coded-secret", }, "prod": { "host": "127.1.2.7", "port": 8282, "token": "v1_encrypted", }, }, }, ) config_db.delete()
def setUp(self): super(UserScopeDatastoreFunctionTest, self).setUp() scope = kvp_const.FULL_USER_SCOPE # Plain key key_id = 'stanley:foo' instance = kvp_db.KeyValuePairDB(name=key_id, value='bar', scope=scope) self.kvp = kvp_db_access.KeyValuePair.add_or_update(instance) # Secret key key_id = 'stanley:fu' value = crypto.symmetric_encrypt(kvp_api.KeyValuePairAPI.crypto_key, 'bar') instance = kvp_db.KeyValuePairDB(name=key_id, value=value, scope=scope, secret=True) self.secret_kvp = kvp_db_access.KeyValuePair.add_or_update(instance)
def setUpClass(cls): super(ActionExecutionControllerTestCaseAuthEnabled, cls).setUpClass() cls.action = copy.deepcopy(ACTION_1) headers = {'content-type': 'application/json', 'X-Auth-Token': str(SYS_TOKEN.token)} post_resp = cls.app.post_json('/v1/actions', cls.action, headers=headers) cls.action['id'] = post_resp.json['id'] cls.action_encrypt = copy.deepcopy(ACTION_DEFAULT_ENCRYPT) post_resp = cls.app.post_json('/v1/actions', cls.action_encrypt, headers=headers) cls.action_encrypt['id'] = post_resp.json['id'] FixturesLoader().save_fixtures_to_db(fixtures_pack=FIXTURES_PACK, fixtures_dict=FIXTURES) # register datastore values which are used in this tests KeyValuePairAPI._setup_crypto() register_items = [ {'name': 'secret', 'secret': True, 'value': crypto_utils.symmetric_encrypt(KeyValuePairAPI.crypto_key, 'foo')}, {'name': 'user1:secret', 'secret': True, 'scope': FULL_USER_SCOPE, 'value': crypto_utils.symmetric_encrypt(KeyValuePairAPI.crypto_key, 'bar')}, ] cls.kvps = [KeyValuePair.add_or_update(KeyValuePairDB(**x)) for x in register_items]
def test_symmetric_encrypt_decrypt_utf8_character(self): values = [ u'£', u'£££', u'££££££', u'č š hello đ č p ž Ž', u'hello 💩', u'💩💩💩💩💩' u'💩💩💩', u'💩😁' ] for index, original in enumerate(values): crypto = symmetric_encrypt(CryptoUtilsTestCase.test_crypto_key, original) plain = symmetric_decrypt(CryptoUtilsTestCase.test_crypto_key, crypto) self.assertEqual(plain, original) self.assertEqual(index, (len(values) - 1))
def to_model(cls, kvp): if not KeyValuePairAPI.crypto_setup: KeyValuePairAPI._setup_crypto() kvp_id = getattr(kvp, 'id', None) name = getattr(kvp, 'name', None) description = getattr(kvp, 'description', None) value = kvp.value secret = False if getattr(kvp, 'ttl', None): expire_timestamp = (date_utils.get_datetime_utc_now() + datetime.timedelta(seconds=kvp.ttl)) else: expire_timestamp = None secret = getattr(kvp, 'secret', False) if secret: if not KeyValuePairAPI.crypto_key: msg = ( 'Crypto key not found in %s. Unable to encrypt value for key %s.' % (KeyValuePairAPI.crypto_key_path, name)) raise CryptoKeyNotSetupException(msg) value = symmetric_encrypt(KeyValuePairAPI.crypto_key, value) scope = getattr(kvp, 'scope', FULL_SYSTEM_SCOPE) if scope not in ALLOWED_SCOPES: raise InvalidScopeException( 'Invalid scope "%s"! Allowed scopes are %s.' % (scope, ALLOWED_SCOPES)) model = cls.model(id=kvp_id, name=name, description=description, value=value, secret=secret, scope=scope, expire_timestamp=expire_timestamp) return model
def test_filter_decrypt_kv(self): secret = 'Build a wall' crypto_key_path = cfg.CONF.keyvalue.encryption_key_path crypto_key = read_crypto_key(key_path=crypto_key_path) secret_value = symmetric_encrypt(encrypt_key=crypto_key, plaintext=secret) KeyValuePair.add_or_update(KeyValuePairDB(name='k8', value=secret_value, scope=FULL_SYSTEM_SCOPE, secret=True)) env = jinja_utils.get_jinja_environment() context = {} context.update({SYSTEM_SCOPE: KeyValueLookup(scope=SYSTEM_SCOPE)}) context.update({ DATASTORE_PARENT_SCOPE: { SYSTEM_SCOPE: KeyValueLookup(scope=FULL_SYSTEM_SCOPE) } }) template = '{{st2kv.system.k8 | decrypt_kv}}' actual = env.from_string(template).render(context) self.assertEqual(actual, secret)
def to_model(cls, kvp): if not KeyValuePairAPI.crypto_setup: KeyValuePairAPI._setup_crypto() kvp_id = getattr(kvp, 'id', None) name = getattr(kvp, 'name', None) description = getattr(kvp, 'description', None) value = kvp.value secret = False if getattr(kvp, 'ttl', None): expire_timestamp = (date_utils.get_datetime_utc_now() + datetime.timedelta(seconds=kvp.ttl)) else: expire_timestamp = None secret = getattr(kvp, 'secret', False) if secret: if not KeyValuePairAPI.crypto_key: msg = ('Crypto key not found in %s. Unable to encrypt value for key %s.' % (KeyValuePairAPI.crypto_key_path, name)) raise CryptoKeyNotSetupException(msg) value = symmetric_encrypt(KeyValuePairAPI.crypto_key, value) scope = getattr(kvp, 'scope', FULL_SYSTEM_SCOPE) if scope not in ALLOWED_SCOPES: raise InvalidScopeException('Invalid scope "%s"! Allowed scopes are %s.' % ( scope, ALLOWED_SCOPES) ) model = cls.model(id=kvp_id, name=name, description=description, value=value, secret=secret, scope=scope, expire_timestamp=expire_timestamp) return model
def test_filter_decrypt_kv(self): secret = 'Build a wall' crypto_key_path = cfg.CONF.keyvalue.encryption_key_path crypto_key = read_crypto_key(key_path=crypto_key_path) secret_value = symmetric_encrypt(encrypt_key=crypto_key, plaintext=secret) KeyValuePair.add_or_update( KeyValuePairDB(name='k8', value=secret_value, scope=FULL_SYSTEM_SCOPE, secret=True)) env = jinja_utils.get_jinja_environment() context = {} context.update({SYSTEM_SCOPE: KeyValueLookup(scope=SYSTEM_SCOPE)}) context.update({ DATASTORE_PARENT_SCOPE: { SYSTEM_SCOPE: KeyValueLookup(scope=FULL_SYSTEM_SCOPE) } }) template = '{{st2kv.system.k8 | decrypt_kv}}' actual = env.from_string(template).render(context) self.assertEqual(actual, secret)
def to_model(cls, kvp): if not KeyValuePairAPI.crypto_setup: KeyValuePairAPI._setup_crypto() kvp_id = getattr(kvp, "id", None) name = getattr(kvp, "name", None) description = getattr(kvp, "description", None) value = kvp.value original_value = value secret = False if getattr(kvp, "ttl", None): expire_timestamp = date_utils.get_datetime_utc_now( ) + datetime.timedelta(seconds=kvp.ttl) else: expire_timestamp = None encrypted = getattr(kvp, "encrypted", False) secret = getattr(kvp, "secret", False) # If user transmitted the value in an pre-encrypted format, we perform the decryption here # to ensure data integrity. Besides that, we store data as-is. # Keep in mind that encrypted=True also always implies secret=True. If we didn't do # that and supported encrypted=True, secret=False, this would allow users to decrypt # any encrypted value. if encrypted: secret = True cls._verif_key_is_set_up(name=name) try: symmetric_decrypt(KeyValuePairAPI.crypto_key, value) except Exception: msg = ( 'Failed to verify the integrity of the provided value for key "%s". Ensure ' "that the value is encrypted with the correct key and not corrupted." % (name)) raise ValueError(msg) # Additional safety check to ensure that the value hasn't been decrypted if value != original_value: raise ValueError( f"The encrypted value {value} is not the" f" same original encrypted value {original_value}.") elif secret: cls._verif_key_is_set_up(name=name) value = symmetric_encrypt(KeyValuePairAPI.crypto_key, value) scope = getattr(kvp, "scope", FULL_SYSTEM_SCOPE) if scope not in ALLOWED_SCOPES: raise InvalidScopeException( 'Invalid scope "%s"! Allowed scopes are %s.' % (scope, ALLOWED_SCOPES)) # NOTE: For security reasons, encrypted always implies secret=True. See comment # above for explanation. if encrypted and not secret: raise ValueError( "encrypted option can only be used in combination with secret " "option") model = cls.model( id=kvp_id, name=name, description=description, value=value, secret=secret, scope=scope, expire_timestamp=expire_timestamp, ) return model
def test_symmetric_encrypt_decrypt_short_string_needs_to_be_padded(self): original = u'a' crypto = symmetric_encrypt(CryptoUtilsTestCase.test_crypto_key, original) plain = symmetric_decrypt(CryptoUtilsTestCase.test_crypto_key, crypto) self.assertEqual(plain, original)
def test_symmetric_encrypt_decrypt(self): original = 'secret' crypto = crypto_utils.symmetric_encrypt(CryptoUtilsTestCase.test_crypto_key, original) plain = crypto_utils.symmetric_decrypt(CryptoUtilsTestCase.test_crypto_key, crypto) self.assertEqual(plain, original)
def to_model(cls, kvp): if not KeyValuePairAPI.crypto_setup: KeyValuePairAPI._setup_crypto() kvp_id = getattr(kvp, 'id', None) name = getattr(kvp, 'name', None) description = getattr(kvp, 'description', None) value = kvp.value original_value = value secret = False if getattr(kvp, 'ttl', None): expire_timestamp = (date_utils.get_datetime_utc_now() + datetime.timedelta(seconds=kvp.ttl)) else: expire_timestamp = None encrypted = getattr(kvp, 'encrypted', False) secret = getattr(kvp, 'secret', False) # If user transmitted the value in an pre-encrypted format, we perform the decryption here # to ensure data integrity. Besides that, we store data as-is. # Keep in mind that encrypted=True also always implies secret=True. If we didn't do # that and supported encrypted=True, secret=False, this would allow users to decrypt # any encrypted value. if encrypted: secret = True cls._verif_key_is_set_up(name=name) try: symmetric_decrypt(KeyValuePairAPI.crypto_key, value) except Exception: msg = ('Failed to verify the integrity of the provided value for key "%s". Ensure ' 'that the value is encrypted with the correct key and not corrupted.' % (name)) raise ValueError(msg) # Additional safety check to ensure that the value hasn't been decrypted assert value == original_value elif secret: cls._verif_key_is_set_up(name=name) value = symmetric_encrypt(KeyValuePairAPI.crypto_key, value) scope = getattr(kvp, 'scope', FULL_SYSTEM_SCOPE) if scope not in ALLOWED_SCOPES: raise InvalidScopeException('Invalid scope "%s"! Allowed scopes are %s.' % ( scope, ALLOWED_SCOPES) ) # NOTE: For security reasons, encrypted always implies secret=True. See comment # above for explanation. if encrypted and not secret: raise ValueError('encrypted option can only be used in combination with secret ' 'option') model = cls.model(id=kvp_id, name=name, description=description, value=value, secret=secret, scope=scope, expire_timestamp=expire_timestamp) return model