tink-crypto/tink-py

KmsEnvelopeAead raises "TinkError: 400 {kek_URI} is not a valid resource type for this request."

Opened this issue · 2 comments

Describe the bug:

Following the Client-side encryption with Tink and Cloud KMS example for Python. I run into a "TinkError: 400 {kek_URI} is not a valid resource type for this request." on line 91: env_aead.decrypt(ciphertext, associated_data).

I have defined the KEK_URI correctly, I have tested env_aead.encrypt several times and everything works perfectly with this KEK_URI, which follows the template: gcp-kms://projects/<PROJECT>/locations/<LOCATION>/keyRings/ <KEY RING>/cryptoKeys/<KEY NAME>/cryptoKeyVersions/<VERSION>

How can we reproduce the bug?
Since this worked some months ago, I think this could be related with a change either Google KMS or in my GCP account, I've tried both with Tink 1.8.0 and 1.9.0, the same happens. I have also tried with two different key rings and versions, and the error still appears.

KEK_URI = "gcp-kms://projects/ait-mlops/locations/us-central1/keyRings/test-keys/cryptoKeys/aitor-test/cryptoKeyVersions/1"
CYPHER_FILE_PATH = "path_to_encrypted_file_with_same_kek_uri"
ASSOCIATED_DATA = b''

import tink
from tink import aead
from tink.integration import gcpkms

# Initialise Tink
aead.register()

try:
    # Read the GCP credentials and setup client
    client = gcpkms.GcpKmsClient(KEK_URI, {})
except tink.TinkError as e:
    logging.exception('Error creating GCP KMS client: %s', e)

# Create envelope AEAD primitive using AES256 GCM for encrypting the data
try:
    remote_aead = client.get_aead(KEK_URI)
    env_aead = aead.KmsEnvelopeAead(
        aead.aead_key_templates.AES256_GCM, remote_aead
    )
except tink.TinkError as e:
    logging.exception('Error creating primitive: %s', e)

with open(CYPHER_FILE_PATH, 'rb') as f:
    ciphertext = f.read()
    env_aead.decrypt(ciphertext, ASSOCIATED_DATA)

Do you have any debugging information?

TinkError                                 Traceback (most recent call last)
Cell In[24], line 3
      1 with open(CYPHER_FILE_PATH, 'rb') as f:
      2     ciphertext = f.read()
----> 3     env_aead.decrypt(ciphertext, ASSOCIATED_DATA)

File [~/micromamba/envs/p38/lib/python3.8/site-packages/tink/aead/_kms_envelope_aead.py:107](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/tink/aead/_kms_envelope_aead.py#line=106), in KmsEnvelopeAead.decrypt(self, ciphertext, associated_data)
    104 # Decrypt DEK with remote AEAD
    105 encrypted_dek_bytes = ciphertext[self.DEK_LEN_BYTES:self.DEK_LEN_BYTES +
    106                                  dek_len]
--> 107 dek_bytes = self.remote_aead.decrypt(encrypted_dek_bytes, b'')
    109 # Get AEAD primitive based on DEK
    110 dek = tink_pb2.KeyData(
    111     type_url=self.key_template.type_url,
    112     value=dek_bytes,
    113     key_material_type=tink_pb2.KeyData.SYMMETRIC,
    114 )

File [~/micromamba/envs/p38/lib/python3.8/site-packages/tink/integration/gcpkms/_gcp_kms_client.py:63](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/tink/integration/gcpkms/_gcp_kms_client.py#line=62), in _GcpKmsAead.decrypt(self, ciphertext, associated_data)
     61   return response.plaintext
     62 except core_exceptions.GoogleAPIError as e:
---> 63   raise tink.TinkError(e)

TinkError: 400 projects[/{PROJECT}/locations/us-central1/keyRings/{KEY RING}/cryptoKeys/{KEY}/cryptoKeyVersions/1](http://localhost:33425/{PROJECT}/locations/us-central1/keyRings/{KEY RING}/cryptoKeys/{KEY}/cryptoKeyVersions/1) is not a valid resource type for this request.

What version of Tink are you using?

This happens both for Tink 1.8.0 and Tink 1.9.0.

Can you tell us more about your development environment?

  • Python 3.8.11

Extended traceback

InvalidArgument                           Traceback (most recent call last)
File [~/micromamba/envs/p38/lib/python3.8/site-packages/tink/integration/gcpkms/_gcp_kms_client.py:54](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/tink/integration/gcpkms/_gcp_kms_client.py#line=53), in _GcpKmsAead.decrypt(self, ciphertext, associated_data)
     53 try:
---> 54   response = self.client.decrypt(
     55      request=kms_v1.types.service.DecryptRequest(
     56          name=self.name,
     57          ciphertext=ciphertext,
     58          additional_authenticated_data=associated_data
     59      )
     60   )
     61   return response.plaintext

File [~/micromamba/envs/p38/lib/python3.8/site-packages/google/cloud/kms_v1/services/key_management_service/client.py:3490](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/google/cloud/kms_v1/services/key_management_service/client.py#line=3489), in KeyManagementServiceClient.decrypt(self, request, name, ciphertext, retry, timeout, metadata)
   3489 # Send the request.
-> 3490 response = rpc(
   3491     request,
   3492     retry=retry,
   3493     timeout=timeout,
   3494     metadata=metadata,
   3495 )
   3497 # Done; return the response.

File [~/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/gapic_v1/method.py:131](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/gapic_v1/method.py#line=130), in _GapicCallable.__call__(self, timeout, retry, compression, *args, **kwargs)
    129     kwargs["compression"] = compression
--> 131 return wrapped_func(*args, **kwargs)

File [~/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/retry/retry_unary.py:293](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/retry/retry_unary.py#line=292), in Retry.__call__.<locals>.retry_wrapped_func(*args, **kwargs)
    290 sleep_generator = exponential_sleep_generator(
    291     self._initial, self._maximum, multiplier=self._multiplier
    292 )
--> 293 return retry_target(
    294     target,
    295     self._predicate,
    296     sleep_generator,
    297     timeout=self._timeout,
    298     on_error=on_error,
    299 )

File [~/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/retry/retry_unary.py:153](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/retry/retry_unary.py#line=152), in retry_target(target, predicate, sleep_generator, timeout, on_error, exception_factory, **kwargs)
    151 except Exception as exc:
    152     # defer to shared logic for handling errors
--> 153     _retry_error_helper(
    154         exc,
    155         deadline,
    156         sleep,
    157         error_list,
    158         predicate,
    159         on_error,
    160         exception_factory,
    161         timeout,
    162     )
    163     # if exception not raised, sleep before next attempt

File [~/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/retry/retry_base.py:212](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/retry/retry_base.py#line=211), in _retry_error_helper(exc, deadline, next_sleep, error_list, predicate_fn, on_error_fn, exc_factory_fn, original_timeout)
    207     final_exc, source_exc = exc_factory_fn(
    208         error_list,
    209         RetryFailureReason.NON_RETRYABLE_ERROR,
    210         original_timeout,
    211     )
--> 212     raise final_exc from source_exc
    213 if on_error_fn is not None:

File [~/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/retry/retry_unary.py:144](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/retry/retry_unary.py#line=143), in retry_target(target, predicate, sleep_generator, timeout, on_error, exception_factory, **kwargs)
    143 try:
--> 144     result = target()
    145     if inspect.isawaitable(result):

File [~/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/timeout.py:120](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/timeout.py#line=119), in TimeToDeadlineTimeout.__call__.<locals>.func_with_timeout(*args, **kwargs)
    118     kwargs["timeout"] = max(0, self._timeout - time_since_first_attempt)
--> 120 return func(*args, **kwargs)

File [~/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/grpc_helpers.py:78](http://localhost:33425/lab/tree/algotive/aiengine/aitrials_test/data_anonymization/micromamba/envs/p38/lib/python3.8/site-packages/google/api_core/grpc_helpers.py#line=77), in _wrap_unary_errors.<locals>.error_remapped_callable(*args, **kwargs)
     77 except grpc.RpcError as exc:
---> 78     raise exceptions.from_grpc_error(exc) from exc

InvalidArgument: 400 projects[/{PROJECT}/locations/us-central1/keyRings/{KEY RING}/cryptoKeys/{KEY}/cryptoKeyVersions/1](http://localhost:33425/{PROJECT}/locations/us-central1/keyRings/{KEY RING}/cryptoKeys/{KEY}/cryptoKeyVersions/1)  is not a valid resource type for this request.

This is related to having the /cryptoKeyVersions/1 at the end of KEK_URI. Normally you wouldn't need it and something like:
KEK_URI = "gcp-kms://projects/ait-mlops/locations/us-central1/keyRings/test-keys/cryptoKeys/aitor-test"
is enough. However, you may need it for decryption if using a non-primary key.

The issue has been addressed and will be available in the next release.

@mdetemad do you know when the next release will be available?
I have a use case where I want to choose the key version for encryption. That is important for me because setting the primary key is only eventually consistent on Cloud KMS, but I need to make sure that I'm using the new key version right after it is created.