# Tink for Python HOW-TO This document presents instructions and Python code snippets for common tasks in [Tink](https://github.com/google/tink). ## Setup instructions Tink requires Python 3.7 or above. The simplest way is to install a binary release from [PyPi](https://pypi.org/project/tink/): ```shell pip3 install tink ``` Currently, the following set of binary wheels are published: * Linux * Python 3.7 * Python 3.8 * Python 3.9 * macOS * Python 3.7 * Python 3.8 * Python 3.9 In addition to the binary wheels, a source distribution is also published. If `pip` does not find a suitable binary wheel for your environment, it will fall back to building the project using the source distribution. This process has the same requirements as building from source instructions below. ### Common setup issues * If you get an error regarding the root certs when using the GCP KMS integration, you will have to install the [root certs](https://github.com/googleapis/google-cloud-cpp/blob/master/google/cloud/bigtable/examples/README.md#configure-grpc-root-certificates). ## Building from source Tink currently supports two build systems for use with Python: * [Bazel](https://bazel.build/) * Setuptools to create a Python package Note that in both cases [Bazel](https://bazel.build/) is required, as it is needed to compile the wrapper around the C++ implementation which uses [pybind11](https://github.com/pybind/pybind11). ### Build with Bazel To build the Python implementation: ```shell 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: * Bazel * [protobuf compiler](https://github.com/protocolbuffers/protobuf#protocol-compiler-installation). To build and install the Python package: ```shell cd python pip3 install . ``` ### Running tests To run all tests, you can: ```shell 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: ```python import tink from tink import tink_config tink_config.register() ``` To use standard implementations of only one primitive, say AEAD: ```python import tink from tink import aead aead.register() ``` The registration of custom key managers can proceed directly via the `core.Registry` class: ```python import tink from tink import core core.Registry.register_key_manager(CustomAeadKeyManager()) ``` ## 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](TINKEY.md), 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`: ```python 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](https://github.com/google/tink/blob/master/python/tink/mac/_mac_key_templates.py), [aead_key_templates](https://github.com/google/tink/blob/master/python/tink/aead/_aead_key_templates.py), or [hybrid_key_templates](https://github.com/google/tink/blob/master/python/tink/hybrid/_hybrid_key_templates.py). ## Loading existing keysets To load encrypted keysets, use `tink.read_keyset_handle` and an appropriate [`KeysetReader`](https://github.com/google/tink/blob/master/python/tink/_keyset_reader.py), depending on the wire format of the stored keyset, for example a `tink.BinaryKeysetReader` or a `tink.JsonKeysetReader`. ```python 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`](https://github.com/google/tink/blob/master/python/tink/cleartext_keyset_handle.py) and an appropriate `KeysetReader`. ```python 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_](PRIMITIVES.md) 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](KEY-MANAGEMENT.md#key-keyset-and-keysethandle) 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](PRIMITIVES.md#c). 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)](PRIMITIVES.md#authenticated-encryption-with-associated-data) primitive to encrypt or decrypt data: ```python import tink from tink import aead plaintext = b'your data...' associated_data = b'context' # Register all AEAD primitives aead.register() # 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](PRIMITIVES.md#deterministic-authenticated-encryption-with-associated-data) primitive to encrypt or decrypt data: ```python import tink from tink import daead plaintext = b'your data...' associated_data = b'context' # Register all deterministic AEAD primitives daead.register() # 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) ``` ### Symmetric key encryption of streaming data You can obtain and use a [Streaming AEAD (Streaming Authenticated Encryption with Associated Data)](PRIMITIVES.md#streaming-authenticated-encryption-with-associated-data) primitive to encrypt or decrypt data streams: ```python import tink from tink import streaming_aead long_plaintext = b'your data...' associated_data = b'context' # Register all streaming AEAD primitives streaming_aead.register() # 1. Get a handle to the key material keyset_handle = tink.new_keyset_handle(streaming_aead.streaming_aead_key_templates.AES256_CTR_HMAC_SHA256_4KB) # 2. Get the primitive. streaming_aead_primitive = keyset_handle.primitive(streaming_aead.StreamingAead) # 3. Use the primitive. output_file = open("ciphertext.out", 'wb') with streaming_aead_primitive.new_encrypting_stream(output_file, associated_data) as enc_stream: bytes_written = enc_stream.write(long_plaintext) ``` ### Message Authentication Code You can compute or verify a [MAC (Message Authentication Code)](PRIMITIVES.md#message-authentication-code): ```python import tink from tink import mac data = b'your data...' # Register all MAC primitives mac.register() # 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_primitive = keyset_handle.primitive(mac.Mac) # 3. Use the primitive to compute a tag, tag = mac_primitive.compute_mac(data) # ... or to verify a tag. mac_primitive.verify_mac(tag, data) ``` ### Hybrid Encryption To encrypt or decrypt using [a combination of public key encryption and symmetric key encryption](PRIMITIVES.md#hybrid-encryption) one can use the following: ```python import tink from tink import hybrid plaintext = b'your data...' context = b'context' # Register all Hybrid primitives hybrid.register() # 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](PRIMITIVES.md#digital-signatures): ```python import tink from tink import signature # Register key manager for signatures signature.register() # 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_data = 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_data, b'your data') ``` ### Envelope encryption Via the AEAD interface, Tink supports [envelope encryption](KEY-MANAGEMENT.md#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: ```python 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' plaintext = b'your data...' associated_data = b'context' # Read the GCP credentials and setup client try: 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 try: 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) ```