1# Tink for Python HOW-TO 2 3This document presents instructions and Python code snippets for common tasks in 4[Tink](https://github.com/google/tink). 5 6## Setup instructions 7 8Tink requires Python 3.7 or above. 9 10The simplest way is to install a binary release from 11[PyPi](https://pypi.org/project/tink/): 12 13```shell 14pip3 install tink 15``` 16 17Currently, the following set of binary wheels are published: 18 19* Linux 20 * Python 3.7 21 * Python 3.8 22 * Python 3.9 23* macOS 24 * Python 3.7 25 * Python 3.8 26 * Python 3.9 27 28In addition to the binary wheels, a source distribution is also published. If 29`pip` does not find a suitable binary wheel for your environment, it will fall 30back to building the project using the source distribution. This process has the 31same requirements as building from source instructions below. 32 33### Common setup issues 34 35* If you get an error regarding the root certs when using the GCP KMS 36 integration, you will have to install the 37 [root certs](https://github.com/googleapis/google-cloud-cpp/blob/master/google/cloud/bigtable/examples/README.md#configure-grpc-root-certificates). 38 39## Building from source 40 41Tink currently supports two build systems for use with Python: 42 43* [Bazel](https://bazel.build/) 44* Setuptools to create a Python package 45 46Note that in both cases [Bazel](https://bazel.build/) is required, as it is 47needed to compile the wrapper around the C++ implementation which uses 48[pybind11](https://github.com/pybind/pybind11). 49 50### Build with Bazel 51 52To build the Python implementation: 53 54```shell 55cd python 56bazel build ... 57``` 58 59### Build a Python package using pip 60 61A setup script is provided which allows building a Python package using pip. 62 63The setup script requires: 64 65* Bazel 66* [protobuf compiler](https://github.com/protocolbuffers/protobuf#protocol-compiler-installation). 67 68To build and install the Python package: 69 70```shell 71cd python 72pip3 install . 73``` 74 75### Running tests 76 77To run all tests, you can: 78 79```shell 80cd python 81bazel test ... 82``` 83 84## Initializing Tink 85 86Tink provides customizable initialization, which allows for choosing specific 87implementations (identified by _key types_) of desired primitives. This 88initialization happens via _registration_ of the implementations. 89 90For example, if you want to use all standard implementations of all primitives 91in the current release of Tink, the initialization would look as follows: 92 93```python 94import tink 95from tink import tink_config 96tink_config.register() 97``` 98 99To use standard implementations of only one primitive, say AEAD: 100 101```python 102import tink 103from tink import aead 104aead.register() 105``` 106 107The registration of custom key managers can proceed directly via 108the `core.Registry` class: 109 110```python 111import tink 112from tink import core 113core.Registry.register_key_manager(CustomAeadKeyManager()) 114``` 115 116## Generating new keys and keysets 117 118Each `KeyManager` implementation provides a `new_key_data(key_template)` method 119that generates new keys of the corresponding key type. However, to avoid 120accidental leakage of sensitive key material you should avoid mixing key(set) 121generation with key(set) usage in code. 122 123To support the separation between these activities Tink package provides a 124command-line tool called [Tinkey](TINKEY.md), which can be used for common key 125management tasks. 126 127Still, if there is a need to generate a KeysetHandle with fresh key material 128directly in Python code, you can use `tink.new_keyset_handle`: 129 130```python 131import tink 132from tink import aead 133 134key_template = aead.aead_key_templates.AES128_EAX 135keyset_handle = tink.new_keyset_handle(key_template) 136# use the keyset... 137``` 138 139where `key_template` can be obtained from util classes corresponding to Tink 140primitives, e.g. 141[mac_key_templates](https://github.com/google/tink/blob/master/python/tink/mac/_mac_key_templates.py), 142[aead_key_templates](https://github.com/google/tink/blob/master/python/tink/aead/_aead_key_templates.py), 143or 144[hybrid_key_templates](https://github.com/google/tink/blob/master/python/tink/hybrid/_hybrid_key_templates.py). 145 146## Loading existing keysets 147 148To load encrypted keysets, use `tink.read_keyset_handle` 149and an appropriate [`KeysetReader`](https://github.com/google/tink/blob/master/python/tink/_keyset_reader.py), 150depending on the wire format of the stored keyset, for example a 151`tink.BinaryKeysetReader` or a `tink.JsonKeysetReader`. 152 153```python 154import tink 155 156json_encrypted_keyset = ... 157reader = tink.JsonKeysetReader(json_encrypted_keyset) 158keyset_handle = tink.read_keyset_handle(reader, master_key_aead) 159``` 160 161To load cleartext keysets, use [`cleartext_keyset_handle`](https://github.com/google/tink/blob/master/python/tink/cleartext_keyset_handle.py) 162and an appropriate `KeysetReader`. 163 164```python 165import tink 166from tink import cleartext_keyset_handle 167 168json_keyset = ... 169reader = tink.JsonKeysetReader(json_keyset) 170keyset_handle = cleartext_keyset_handle.read(reader) 171``` 172## Obtaining and using primitives 173 174[_Primitives_](PRIMITIVES.md) represent cryptographic operations offered by 175Tink, hence they form the core of the Tink API. A primitive is just an interface 176that specifies what operations are offered by the primitive. A primitive can 177have multiple implementations, and you choose a desired implementation by using 178a key of corresponding type (see [this 179section](KEY-MANAGEMENT.md#key-keyset-and-keysethandle) for further details). 180 181Tink for Python supports the same primitives as Tink for C++. A list of 182primitives and their implementations currently supported by Tink in C++ can be 183found [here](PRIMITIVES.md#c). 184 185You obtain a primitive by calling the method `primitive` of the `KeysetHandle`. 186 187### Symmetric key encryption 188 189You can obtain and use an [AEAD (Authenticated Encryption with Associated 190Data)](PRIMITIVES.md#authenticated-encryption-with-associated-data) primitive to 191encrypt or decrypt data: 192 193```python 194import tink 195from tink import aead 196 197plaintext = b'your data...' 198associated_data = b'context' 199 200# Register all AEAD primitives 201aead.register() 202 203# 1. Get a handle to the key material. 204keyset_handle = tink.new_keyset_handle(aead.aead_key_templates.AES256_GCM) 205 206# 2. Get the primitive. 207aead_primitive = keyset_handle.primitive(aead.Aead) 208 209# 3. Use the primitive. 210ciphertext = aead_primitive.encrypt(plaintext, associated_data) 211``` 212 213### Deterministic symmetric key encryption 214 215You can obtain and use a 216[DeterministicAEAD (Deterministic Authenticated Encryption with Associated Data](PRIMITIVES.md#deterministic-authenticated-encryption-with-associated-data) 217primitive to encrypt or decrypt data: 218 219```python 220import tink 221from tink import daead 222 223plaintext = b'your data...' 224associated_data = b'context' 225 226# Register all deterministic AEAD primitives 227daead.register() 228 229# 1. Get a handle to the key material. 230keyset_handle = tink.new_keyset_handle(daead.deterministic_aead_key_templates.AES256_SIV) 231 232# 2. Get the primitive. 233daead_primitive = keyset_handle.primitive(daead.DeterministicAead) 234 235# 3. Use the primitive. 236ciphertext = daead_primitive.encrypt_deterministically(plaintext, associated_data) 237``` 238 239### Symmetric key encryption of streaming data 240 241You can obtain and use a 242[Streaming AEAD (Streaming Authenticated Encryption with Associated Data)](PRIMITIVES.md#streaming-authenticated-encryption-with-associated-data) 243primitive to encrypt or decrypt data streams: 244 245```python 246import tink 247from tink import streaming_aead 248 249long_plaintext = b'your data...' 250associated_data = b'context' 251 252# Register all streaming AEAD primitives 253streaming_aead.register() 254 255# 1. Get a handle to the key material 256keyset_handle = tink.new_keyset_handle(streaming_aead.streaming_aead_key_templates.AES256_CTR_HMAC_SHA256_4KB) 257 258# 2. Get the primitive. 259streaming_aead_primitive = keyset_handle.primitive(streaming_aead.StreamingAead) 260 261# 3. Use the primitive. 262output_file = open("ciphertext.out", 'wb') 263with streaming_aead_primitive.new_encrypting_stream(output_file, associated_data) as enc_stream: 264 bytes_written = enc_stream.write(long_plaintext) 265``` 266 267### Message Authentication Code 268 269You can compute or verify a 270[MAC (Message Authentication Code)](PRIMITIVES.md#message-authentication-code): 271 272```python 273import tink 274from tink import mac 275 276data = b'your data...' 277 278# Register all MAC primitives 279mac.register() 280 281# 1. Get a handle to the key material. 282keyset_handle = tink.new_keyset_handle(mac.mac_key_templates.HMAC_SHA256_128BITTAG) 283 284# 2. Get the primitive. 285mac_primitive = keyset_handle.primitive(mac.Mac) 286 287# 3. Use the primitive to compute a tag, 288tag = mac_primitive.compute_mac(data) 289 290# ... or to verify a tag. 291mac_primitive.verify_mac(tag, data) 292``` 293 294### Hybrid Encryption 295 296To encrypt or decrypt using 297[a combination of public key encryption and symmetric key encryption](PRIMITIVES.md#hybrid-encryption) 298one can use the following: 299 300```python 301import tink 302from tink import hybrid 303 304plaintext = b'your data...' 305context = b'context' 306 307# Register all Hybrid primitives 308hybrid.register() 309 310# 1. Generate the private key material. 311private_keyset_handle = tink.new_keyset_handle(hybrid.hybrid_key_templates.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM) 312 313# Obtain the public key material. 314public_keyset_handle = private_keyset_handle.public_keyset_handle() 315 316# Encryption 317 318# 2. Get the primitive. 319hybrid_encrypt = public_keyset_handle.primitive(hybrid.HybridEncrypt) 320 321# 3. Use the primitive. 322ciphertext = hybrid_encrypt.encrypt(plaintext, context) 323 324# Decryption 325 326# 2. Get the primitive. 327hybrid_decrypt = private_keyset_handle.primitive(hybrid.HybridDecrypt) 328 329# 3. Use the primitive. 330plaintext = hybrid_decrypt.decrypt(ciphertext, context) 331``` 332 333### Digital Signatures 334 335You can sign or verify using a 336[digital signature](PRIMITIVES.md#digital-signatures): 337 338```python 339import tink 340from tink import signature 341 342# Register key manager for signatures 343signature.register() 344 345# Signing 346# 1. Generate the private key material. 347keyset_handle = tink.new_keyset_handle(signature.signature_key_templates.ED25519) 348 349# 2. Get the primitive. 350signer = keyset_handle.primitive(signature.PublicKeySign) 351 352# 3. Use the primitive to sign. 353signature_data = signer.sign(b'your data') 354 355# Verifying 356# 1. Obtain the public key material. 357public_keyset_handle = keyset_handle.public_keyset_handle() 358 359# 2. Get the primitive. 360verifier = public_keyset_handle.primitive(signature.PublicKeyVerify) 361 362# 3. Use the primitive to verify. 363verifier.verify(signature_data, b'your data') 364``` 365 366### Envelope encryption 367 368Via the AEAD interface, Tink supports 369[envelope encryption](KEY-MANAGEMENT.md#envelope-encryption). 370 371For example, you can perform envelope encryption with a Google Cloud KMS key at 372`gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar` 373using the credentials in `credentials.json` as follows: 374 375```python 376import tink 377from tink import aead 378from tink.integration import gcpkms 379 380key_uri = 'gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar' 381gcp_credentials = 'credentials.json' 382 383plaintext = b'your data...' 384associated_data = b'context' 385 386# Read the GCP credentials and setup client 387try: 388 gcp_client = gcpkms.GcpKmsClient(key_uri, gcp_credentials) 389 gcp_aead = gcp_client.get_aead(key_uri) 390except tink.TinkError as e: 391 logging.error('Error initializing GCP client: %s', e) 392 return 1 393 394# Create envelope AEAD primitive using AES256 GCM for encrypting the data 395try: 396 key_template = aead.aead_key_templates.AES256_GCM 397 env_aead = aead.KmsEnvelopeAead(key_template, gcp_aead) 398except tink.TinkError as e: 399 logging.error('Error creating primitive: %s', e) 400 return 1 401 402# Use env_aead to encrypt data 403ciphertext = env_aead.encrypt(plaintext, associated_data) 404``` 405