Esempio n. 1
0
    def test_append_crypto_meta(self):
        actual = crypto_utils.append_crypto_meta('abc', self.meta)
        expected = 'abc; swift_meta=%s' % self.serialized_meta
        self.assertEqual(actual, expected)

        actual = crypto_utils.append_crypto_meta('abc', self.meta_with_key)
        expected = 'abc; swift_meta=%s' % self.serialized_meta_with_key
        self.assertEqual(actual, expected)
Esempio n. 2
0
    def test_append_crypto_meta(self):
        actual = crypto_utils.append_crypto_meta('abc', self.meta)
        expected = 'abc; swift_meta=%s' % self.serialized_meta
        self.assertEqual(actual, expected)

        actual = crypto_utils.append_crypto_meta('abc', self.meta_with_key)
        expected = 'abc; swift_meta=%s' % self.serialized_meta_with_key
        self.assertEqual(actual, expected)
Esempio n. 3
0
    def encrypt_user_metadata(self, req, keys):
        """
        Encrypt user-metadata header values. Replace each x-object-meta-<key>
        user metadata header with a corresponding
        x-object-transient-sysmeta-crypto-meta-<key> header which has the
        crypto metadata required to decrypt appended to the encrypted value.

        :param req: a swob Request
        :param keys: a dict of encryption keys
        """
        prefix = get_object_transient_sysmeta('crypto-meta-')
        user_meta_headers = [h for h in req.headers.items() if
                             is_user_meta(self.server_type, h[0]) and h[1]]
        crypto_meta = None
        for name, val in user_meta_headers:
            short_name = strip_user_meta_prefix(self.server_type, name)
            new_name = prefix + short_name
            enc_val, crypto_meta = encrypt_header_val(
                self.crypto, val, keys[self.server_type])
            req.headers[new_name] = append_crypto_meta(enc_val, crypto_meta)
            req.headers.pop(name)
        # store a single copy of the crypto meta items that are common to all
        # encrypted user metadata independently of any such meta that is stored
        # with the object body because it might change on a POST. This is done
        # for future-proofing - the meta stored here is not currently used
        # during decryption.
        if crypto_meta:
            meta = dump_crypto_meta({'cipher': crypto_meta['cipher'],
                                     'key_id': keys['id']})
            req.headers[get_object_transient_sysmeta('crypto-meta')] = meta
Esempio n. 4
0
    def encrypt_user_metadata(self, req, keys):
        """
        Encrypt user-metadata header values. Replace each x-object-meta-<key>
        user metadata header with a corresponding
        x-object-transient-sysmeta-crypto-meta-<key> header which has the
        crypto metadata required to decrypt appended to the encrypted value.

        :param req: a swob Request
        :param keys: a dict of encryption keys
        """
        prefix = get_object_transient_sysmeta('crypto-meta-')
        user_meta_headers = [
            h for h in req.headers.items()
            if is_user_meta(self.server_type, h[0]) and h[1]
        ]
        crypto_meta = None
        for name, val in user_meta_headers:
            short_name = strip_user_meta_prefix(self.server_type, name)
            new_name = prefix + short_name
            enc_val, crypto_meta = encrypt_header_val(self.crypto, val,
                                                      keys[self.server_type])
            req.headers[new_name] = append_crypto_meta(enc_val, crypto_meta)
            req.headers.pop(name)
        # store a single copy of the crypto meta items that are common to all
        # encrypted user metadata independently of any such meta that is stored
        # with the object body because it might change on a POST. This is done
        # for future-proofing - the meta stored here is not currently used
        # during decryption.
        if crypto_meta:
            meta = dump_crypto_meta({
                'cipher': crypto_meta['cipher'],
                'key_id': keys['id']
            })
            req.headers[get_object_transient_sysmeta('crypto-meta')] = meta
Esempio n. 5
0
    def test_append_crypto_meta(self):
        actual = crypto_utils.append_crypto_meta('abc', self.meta)
        expected = 'abc; swift_meta=%s' % self.serialized_meta
        self.assertEqual(actual, expected)

        actual = crypto_utils.append_crypto_meta('abc', self.meta_with_key)
        expected = 'abc; swift_meta=%s' % self.serialized_meta_with_key
        self.assertEqual(actual, expected)

        def check_bad_value(value):
            with self.assertRaises(ValueError):
                crypto_utils.append_crypto_meta(value, self.meta)

        check_bad_value(None)
        check_bad_value({})
        check_bad_value(1)
Esempio n. 6
0
        def footers_callback(footers):
            if inner_callback:
                # pass on footers dict to any other callback that was
                # registered before this one. It may override any footers that
                # were set.
                inner_callback(footers)

            plaintext_etag = None
            if self.body_crypto_ctxt:
                plaintext_etag = self.plaintext_md5.hexdigest()
                # If client (or other middleware) supplied etag, then validate
                # against plaintext etag
                etag_to_check = footers.get('Etag') or client_etag
                if (etag_to_check is not None and
                        plaintext_etag != etag_to_check):
                    raise HTTPUnprocessableEntity(request=Request(self.env))

                # override any previous notion of etag with the ciphertext etag
                footers['Etag'] = self.ciphertext_md5.hexdigest()

                # Encrypt the plaintext etag using the object key and persist
                # as sysmeta along with the crypto parameters that were used.
                encrypted_etag, etag_crypto_meta = encrypt_header_val(
                    self.crypto, plaintext_etag, self.keys['object'])
                footers['X-Object-Sysmeta-Crypto-Etag'] = \
                    append_crypto_meta(encrypted_etag, etag_crypto_meta)
                footers['X-Object-Sysmeta-Crypto-Body-Meta'] = \
                    dump_crypto_meta(self.body_crypto_meta)

                # Also add an HMAC of the etag for use when evaluating
                # conditional requests
                footers['X-Object-Sysmeta-Crypto-Etag-Mac'] = _hmac_etag(
                    self.keys['object'], plaintext_etag)
            else:
                # No data was read from body, nothing was encrypted, so don't
                # set any crypto sysmeta for the body, but do re-instate any
                # etag provided in inbound request if other middleware has not
                # already set a value.
                if client_etag is not None:
                    footers.setdefault('Etag', client_etag)

            # When deciding on the etag that should appear in container
            # listings, look for:
            #   * override in the footer, otherwise
            #   * override in the header, and finally
            #   * MD5 of the plaintext received
            # This may be None if no override was set and no data was read. An
            # override value of '' will be passed on.
            container_listing_etag = footers.get(
                'X-Object-Sysmeta-Container-Update-Override-Etag',
                container_listing_etag_header)

            if container_listing_etag is None:
                container_listing_etag = plaintext_etag

            if (container_listing_etag and
                    (container_listing_etag != MD5_OF_EMPTY_STRING or
                     plaintext_etag)):
                # Encrypt the container-listing etag using the container key
                # and a random IV, and use it to override the container update
                # value, with the crypto parameters appended. We use the
                # container key here so that only that key is required to
                # decrypt all etag values in a container listing when handling
                # a container GET request. Don't encrypt an EMPTY_ETAG
                # unless there actually was some body content, in which case
                # the container-listing etag is possibly conveying some
                # non-obvious information.
                val, crypto_meta = encrypt_header_val(
                    self.crypto, container_listing_etag,
                    self.keys['container'])
                crypto_meta['key_id'] = self.keys['id']
                footers['X-Object-Sysmeta-Container-Update-Override-Etag'] = \
                    append_crypto_meta(val, crypto_meta)
Esempio n. 7
0
        def footers_callback(footers):
            if inner_callback:
                # pass on footers dict to any other callback that was
                # registered before this one. It may override any footers that
                # were set.
                inner_callback(footers)

            plaintext_etag = None
            if self.body_crypto_ctxt:
                plaintext_etag = self.plaintext_md5.hexdigest()
                # If client (or other middleware) supplied etag, then validate
                # against plaintext etag
                etag_to_check = footers.get('Etag') or client_etag
                if (etag_to_check is not None
                        and plaintext_etag != etag_to_check):
                    raise HTTPUnprocessableEntity(request=Request(self.env))

                # override any previous notion of etag with the ciphertext etag
                footers['Etag'] = self.ciphertext_md5.hexdigest()

                # Encrypt the plaintext etag using the object key and persist
                # as sysmeta along with the crypto parameters that were used.
                encrypted_etag, etag_crypto_meta = encrypt_header_val(
                    self.crypto, plaintext_etag, self.keys['object'])
                footers['X-Object-Sysmeta-Crypto-Etag'] = \
                    append_crypto_meta(encrypted_etag, etag_crypto_meta)
                footers['X-Object-Sysmeta-Crypto-Body-Meta'] = \
                    dump_crypto_meta(self.body_crypto_meta)

                # Also add an HMAC of the etag for use when evaluating
                # conditional requests
                footers['X-Object-Sysmeta-Crypto-Etag-Mac'] = _hmac_etag(
                    self.keys['object'], plaintext_etag)
            else:
                # No data was read from body, nothing was encrypted, so don't
                # set any crypto sysmeta for the body, but do re-instate any
                # etag provided in inbound request if other middleware has not
                # already set a value.
                if client_etag is not None:
                    footers.setdefault('Etag', client_etag)

            # When deciding on the etag that should appear in container
            # listings, look for:
            #   * override in the footer, otherwise
            #   * override in the header, and finally
            #   * MD5 of the plaintext received
            # This may be None if no override was set and no data was read
            container_listing_etag = footers.get(
                'X-Object-Sysmeta-Container-Update-Override-Etag',
                container_listing_etag_header) or plaintext_etag

            if (container_listing_etag is not None
                    and (container_listing_etag != MD5_OF_EMPTY_STRING
                         or plaintext_etag)):
                # Encrypt the container-listing etag using the container key
                # and a random IV, and use it to override the container update
                # value, with the crypto parameters appended. We use the
                # container key here so that only that key is required to
                # decrypt all etag values in a container listing when handling
                # a container GET request. Don't encrypt an EMPTY_ETAG
                # unless there actually was some body content, in which case
                # the container-listing etag is possibly conveying some
                # non-obvious information.
                val, crypto_meta = encrypt_header_val(self.crypto,
                                                      container_listing_etag,
                                                      self.keys['container'])
                crypto_meta['key_id'] = self.keys['id']
                footers['X-Object-Sysmeta-Container-Update-Override-Etag'] = \
                    append_crypto_meta(val, crypto_meta)
Esempio n. 8
0
 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)
Esempio n. 9
0
 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)
Esempio n. 10
0
 def check_bad_value(value):
     with self.assertRaises(ValueError):
         crypto_utils.append_crypto_meta(value, self.meta)