def decrypt_value_with_meta(self, value, key, required, decoder): """ Base64-decode and decrypt a value if crypto meta can be extracted from the value itself, otherwise return the value unmodified. A value should either be a string that does not contain the ';' character or should be of the form: <base64-encoded ciphertext>;swift_meta=<crypto meta> :param value: value to decrypt :param key: crypto key to use :param required: if True then the value is required to be decrypted and an EncryptionException will be raised if the header cannot be decrypted due to missing crypto meta. :param decoder: function to turn the decrypted bytes into useful data :returns: decrypted value if crypto meta is found, otherwise the unmodified value :raises EncryptionException: if an error occurs while parsing crypto meta or if the header value was required to be decrypted but crypto meta was not found. """ extracted_value, crypto_meta = extract_crypto_meta(value) if crypto_meta: self.crypto.check_crypto_meta(crypto_meta) value = self.decrypt_value( extracted_value, key, crypto_meta, decoder) elif required: raise EncryptionException( "Missing crypto meta in value %s" % value) return value
def decrypt_obj_dict(self, req, obj_dict): if 'hash' in obj_dict: # each object's etag may have been encrypted with a different key # so fetch keys based on its crypto meta ciphertext, crypto_meta = extract_crypto_meta(obj_dict['hash']) bad_keys = set() if crypto_meta: try: self.crypto.check_crypto_meta(crypto_meta) keys = self.get_decryption_keys(req, crypto_meta) # Note that symlinks (for example) may put swift paths in # the listing ETag, so we can't just use ASCII. obj_dict['hash'] = self.decrypt_value( ciphertext, keys['container'], crypto_meta, decoder=lambda x: x.decode('utf-8')) except EncryptionException as err: if not isinstance(err, UnknownSecretIdError) or \ err.args[0] not in bad_keys: # Only warn about an unknown key once per listing self.logger.error( "Error decrypting container listing: %s", err) if isinstance(err, UnknownSecretIdError): bad_keys.add(err.args[0]) obj_dict['hash'] = '<unknown>' return obj_dict
def test_extract_crypto_meta(self): val, meta = crypto_utils.extract_crypto_meta( 'abc; swift_meta=%s' % self.serialized_meta) self.assertEqual('abc', val) self.assertDictEqual(self.meta, meta) val, meta = crypto_utils.extract_crypto_meta( 'abc; swift_meta=%s' % self.serialized_meta_with_key) self.assertEqual('abc', val) self.assertDictEqual(self.meta_with_key, meta) val, meta = crypto_utils.extract_crypto_meta('abc') self.assertEqual('abc', val) self.assertIsNone(meta) # other param names will be ignored val, meta = crypto_utils.extract_crypto_meta('abc; foo=bar') self.assertEqual('abc', val) self.assertIsNone(meta)
def test_extract_crypto_meta(self): val, meta = crypto_utils.extract_crypto_meta('abc; swift_meta=%s' % self.serialized_meta) self.assertEqual('abc', val) self.assertDictEqual(self.meta, meta) val, meta = crypto_utils.extract_crypto_meta( 'abc; swift_meta=%s' % self.serialized_meta_with_key) self.assertEqual('abc', val) self.assertDictEqual(self.meta_with_key, meta) val, meta = crypto_utils.extract_crypto_meta('abc') self.assertEqual('abc', val) self.assertIsNone(meta) # other param names will be ignored val, meta = crypto_utils.extract_crypto_meta('abc; foo=bar') self.assertEqual('abc', val) self.assertIsNone(meta) val, meta = crypto_utils.extract_crypto_meta( 'abc; swift_meta=%s; foo=bar' % self.serialized_meta_with_key) self.assertEqual('abc', val) self.assertDictEqual(self.meta_with_key, meta)
def decrypt_obj_dict(self, req, obj_dict): if 'hash' in obj_dict: # each object's etag may have been encrypted with a different key # so fetch keys based on its crypto meta ciphertext, crypto_meta = extract_crypto_meta(obj_dict['hash']) bad_keys = set() if crypto_meta: try: self.crypto.check_crypto_meta(crypto_meta) keys = self.get_decryption_keys(req, crypto_meta) obj_dict['hash'] = self.decrypt_value( ciphertext, keys['container'], crypto_meta) except EncryptionException as err: if not isinstance(err, UnknownSecretIdError) or \ err.args[0] not in bad_keys: # Only warn about an unknown key once per listing self.logger.error( "Error decrypting container listing: %s", err) if isinstance(err, UnknownSecretIdError): bad_keys.add(err.args[0]) obj_dict['hash'] = '<unknown>' return obj_dict
def test_append_then_extract_crypto_meta(self): val = 'abc' actual = crypto_utils.extract_crypto_meta( crypto_utils.append_crypto_meta(val, self.meta)) self.assertEqual((val, self.meta), actual)