Tink for Python HOW-TO

This document presents instructions and Python code snippets for common tasks in Tink.

Setup instructions

The Tink Python implementation is a wrapper around the C++ implementation using pybind11. It is therefore necessary to compile the project before the Python implementation is ready to use.

Build with Bazel

Bazel is used to build and test Tink.

To build the Python implementation:

cd python
bazel build ...

Build a Python package using pip

A setup script is provided which allows building a Python package using pip.

The setup script requires:

To build and install the Python package:

cd python
pip3 install .

Running tests

To run all tests, you can:

cd python
bazel test ...

Initializing Tink

Tink provides customizable initialization, which allows for choosing specific implementations (identified by key types) of desired primitives. This initialization happens via registration of the implementations.

For example, if you want to use all standard implementations of all primitives in the current release of Tink, the initialization would look as follows:

import tink
from tink import tink_config

To use standard implementations of only one primitive, say AEAD:

import tink
from tink import aead

The registration of custom key managers can proceed directly via the core.Registry class:

import tink
from tink import core

Generating new keys and keysets

Each KeyManager implementation provides a new_key_data(key_template) method that generates new keys of the corresponding key type. However, to avoid accidental leakage of sensitive key material you should avoid mixing key(set) generation with key(set) usage in code.

To support the separation between these activities Tink package provides a command-line tool called Tinkey, which can be used for common key management tasks.

Still, if there is a need to generate a KeysetHandle with fresh key material directly in Python code, you can use tink.new_keyset_handle:

import tink
from tink import aead

key_template = aead.aead_key_templates.AES128_EAX
keyset_handle = tink.new_keyset_handle(key_template)
# use the keyset...

where key_template can be obtained from util classes corresponding to Tink primitives, e.g. mac_key_templates, aead_key_templates, or hybrid_key_templates.

Loading existing keysets

To load encrypted keysets, use tink.read_keyset_handle and an appropriate KeysetReader, depending on the wire format of the stored keyset, for example a tink.BinaryKeysetReader or a tink.JsonKeysetReader.

import tink

json_encrypted_keyset = ...
reader = tink.JsonKeysetReader(json_encrypted_keyset)
keyset_handle = tink.read_keyset_handle(reader, master_key_aead)

To load cleartext keysets, use cleartext_keyset_handle and an appropriate KeysetReader.

import tink
from tink import cleartext_keyset_handle

json_keyset = ...
reader = tink.JsonKeysetReader(json_keyset)
keyset_handle = cleartext_keyset_handle.read(reader)

Obtaining and using primitives

Primitives represent cryptographic operations offered by Tink, hence they form the core of the Tink API. A primitive is just an interface that specifies what operations are offered by the primitive. A primitive can have multiple implementations, and you choose a desired implementation by using a key of corresponding type (see this section for further details).

Tink for Python supports the same primitives as Tink for C++. A list of primitives and their implementations currently supported by Tink in C++ can be found here.

You obtain a primitive by calling the method primitive of the KeysetHandle.

Symmetric key encryption

You can obtain and use an AEAD (Authenticated Encryption with Associated Data) primitive to encrypt or decrypt data:

  import tink
  from tink import aead

  # Register all AEAD primitives

  # 1. Get a handle to the key material.
  keyset_handle = tink.new_keyset_handle(aead.aead_key_templates.AES256_GCM)

  # 2. Get the primitive.
  aead_primitive = keyset_handle.primitive(aead.Aead)

  # 3. Use the primitive.
  ciphertext = aead_primitive.encrypt(plaintext, associated data)

Deterministic symmetric key encryption

You can obtain and use a DeterministicAEAD (Deterministic Authenticated Encryption with Associated Data primitive to encrypt or decrypt data:

  import tink
  from tink import daead

  # Register all deterministic AEAD primitives

  # 1. Get a handle to the key material.
  keyset_handle = tink.new_keyset_handle(daead.deterministic_aead_key_templates.AES256_SIV)

  # 2. Get the primitive.
  daead_primitive = keyset_handle.primitive(daead.DeterministicAead)

  # 3. Use the primitive.
  ciphertext = daead_primitive.encrypt_deterministically(plaintext, associated data)

Message Authentication Code

You can compute or verify a MAC (Message Authentication Code):

  import tink
  from tink import mac

  # Register all MAC primitives

  # 1. Get a handle to the key material.
  keyset_handle = tink.new_keyset_handle(mac.mac_key_templates.HMAC_SHA256_128BITTAG)

  # 2. Get the primitive.
  mac = keyset_handle.primitive(mac.Mac)

  # 3. Use the primitive to compute a tag,
  tag = mac.compute_mac(data)

  # ... or to verify a tag.
  mac.verify_mac(tag, data)

Hybrid Encryption

To encrypt or decrypt using a combination of public key encryption and symmetric key encryption one can use the following:

  import tink
  from tink import hybrid

  # Register all Hybrid primitives

  # 1. Generate the private key material.
  private_keyset_handle = tink.new_keyset_handle(hybrid.hybrid_key_templates.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM)

  # Obtain the public key material.
  public_keyset_handle = private_keyset_handle.public_keyset_handle()

  # Encryption

  # 2. Get the primitive.
  hybrid_encrypt = public_keyset_handle.primitive(hybrid.HybridEncrypt)

  # 3. Use the primitive.
  ciphertext = hybrid_encrypt.encrypt(plaintext, context)

  # Decryption

  # 2. Get the primitive.
  hybrid_decrypt = private_keyset_handle.primitive(hybrid.HybridDecrypt)

  # 3. Use the primitive.
  plaintext = hybrid_decrypt.decrypt(ciphertext, context)

Digital Signatures

You can sign or verify using a digital signature:

   import tink
   from tink import signature

   # Register key manager for signatures

   # Signing
   # 1. Generate the private key material.
   keyset_handle = tink.new_keyset_handle(signature.signature_key_templates.ED25519)

   # 2. Get the primitive.
   signer = keyset_handle.primitive(signature.PublicKeySign)

   # 3. Use the primitive to sign.
   signature = signer.sign(b'your data')

   # Verifying
   # 1. Obtain the public key material.
   public_keyset_handle = keyset_handle.public_keyset_handle()

   # 2. Get the primitive.
   verifier = public_keyset_handle.primitive(signature.PublicKeyVerify)

   # 3. Use the primitive to verify.
   verifier.verify(signature, b'your data')

Envelope encryption

Via the AEAD interface, Tink supports envelope encryption.

For example, you can perform envelope encryption with a Google Cloud KMS key at gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar using the credentials in credentials.json as follows:

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

  key_uri = 'gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar'
  gcp_credentials = 'credentials.json'

  # Read the GCP credentials and setup client
    gcp_client = gcpkms.GcpKmsClient(key_uri, gcp_credentials)
    gcp_aead = gcp_client.get_aead(key_uri)
  except tink.TinkError as e:
    logging.error('Error initializing GCP client: %s', e)
    return 1

  # Create envelope AEAD primitive using AES256 GCM for encrypting the data
    key_template = aead.aead_key_templates.AES256_GCM
    env_aead = aead.KmsEnvelopeAead(key_template, gcp_aead)
  except tink.TinkError as e:
    logging.error('Error creating primitive: %s', e)
    return 1
  # Use env_aead to encrypt data
  ciphertext = env_aead.encrypt(plaintext, associated data)