xref: /aosp_15_r20/external/tink/docs/PYTHON-HOWTO.md (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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