This document contains instructions and C++ code snippets for common tasks in Tink.
Tink can be built using Bazel or CMake. Using any other build system is currently not supported. This implies that you need to build your binary from scratch.
Using Tink in projects built with Bazel is straightforward and is the recommended approach. For reference, see the C++ examples.
Using Tink with CMake is supported, see CMAKE-HOWTO for a detailed description.
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 be:
#include "tink/config/tink_config.h" // ... auto status = TinkConfig::Register(); if (!status.ok()) /* ... handle failure */; // ...
To use standard implementations of only one primitive, say AEAD:
#include "tink/aead/aead_config.h" // ... auto status = AeadConfig::Register(); if (!status.ok()) /* ... handle failure */; // ...
The registration of custom key managers can proceed directly via Registry
class:
#include "tink/registry.h" #include "custom_project/custom_aead_key_manager.h" // ... auto status = Registry::RegisterKeyManager(absl::make_unique<CustomAeadKeyManager>()); if (!status.ok()) /* ... handle failure */;
Each KeyManager
implementation provides a NewKey(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 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 C++ code, you can use KeysetHandle
:
auto new_keyset_handle_result = KeysetHandle::GenerateNew(key_template); if (!new_keyset_handle_result.ok()) return new_keyset_handle_result.status(); auto keyset_handle = std::move(new_keyset_handle_result.ValueOrDie()); // use the keyset...
Recommended key templates can be obtained from util classes corresponding to Tink primitives, e.g. MacKeyTemplates, AeadKeyTemplates, and HybridKeyTemplates.
To load encrypted keysets, use KeysetHandle
and an appropriate KeysetReader
depending on the wire format of the stored keyset, for example a BinaryKeysetReader
or a JsonKeysetReader
:
#include "tink/aead.h" #include "tink/json_keyset_reader.h" #include "tink/cleartext_keyset_handle.h" #include "tink/integration/aws_kms_client.h" // ... std::string json_encrypted_keyset = ...; auto reader_result = JsonKeysetReader::New(json_encrypted_keyset); if (!reader_result.ok()) return reader_result.status(); auto reader = std::move(reader_result.ValueOrDie()); std::string master_key_uri = "aws-kms://arn:aws:kms:us-east-1:007084425826:key/84a65985-f868-4bfc-83c2-366618acf147"; auto aead = std::move(AwsKmsClient::NewAead(master_key_uri).ValueOrDie()); auto handle_result = KeysetHandle::Read(std::move(reader), *aead); if (!handle_result.ok()) return handle_result.status(); auto keyset_handle = std::move(handle_result.ValueOrDie());
To load cleartext keysets, use CleartextKeysetHandle
and an appropriate KeysetReader
,
#include "tink/binary_keyset_reader.h" #include "tink/cleartext_keyset_handle.h" // ... std::string binary_keyset = ...; auto reader_result = BinaryKeysetReader::New(binary_keyset); if (!reader_result.ok()) return reader_result.status(); auto reader = std::move(reader_result.ValueOrDie()); auto handle_result = CleartextKeysetHandle::Read(std::move(reader)); if (!handle_result.ok()) return handle_result.status(); auto keyset_handle = std::move(handle_result.ValueOrDie());
Primitives represent cryptographic operations offered by Tink, hence they form the core of the Tink API. A primitive is 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 a corresponding type (see this document for further details).
A list of primitives and the implementations currently supported by Tink in C++ can be found here.
You obtain a primitive by calling the method GetPrimitive<>
of a KeysetHandle
.
You can use an AEAD (Authenticated Encryption with Associated Data) primitive to encrypt or decrypt data:
#include "tink/aead.h" #include "tink/keyset_handle.h" // 1. Get a handle to the key material. KeysetHandle keyset_handle = ...; // 2. Get the primitive. auto aead_result= keyset_handle.GetPrimitive<Aead>(); if (!aead_result.ok()) return aead_result.status(); auto aead = std::move(aead_result.ValueOrDie()); // 3. Use the primitive. auto ciphertext_result = aead.Encrypt(plaintext, aad); if (!ciphertext_result.ok()) return ciphertext_result.status(); auto ciphertext = std::move(ciphertext_result.ValueOrDie());
You can encrypt and decrypt using a combination of public key encryption and symmetric key encryption:
#include "tink/hybrid_decrypt.h" #include "tink/keyset_handle.h" // 1. Get a handle to the key material. KeysetHandle keyset_handle = ...; // 2. Get the primitive. auto hybrid_decrypt_result = keyset_handle.GetPrimitive<HybridDecrypt>(); if (!hybrid_decrypt_result.ok()) return hybrid_decrypt_result.status(); auto hybrid_decrypt = std::move(hybrid_decrypt_result.ValueOrDie()); // 3. Use the primitive. auto plaintext_result = hybrid_decrypt.Decrypt(ciphertext, context_info); if (!plaintext_result.ok()) return plaintext_result.status(); auto plaintext = std::move(plaintext_result.ValueOrDie());
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:
#include "tink/aead.h" #include "tink/aead_key_templates.h" #include "tink/keyset_handle.h" #include "tink/integration/gcpkms/gcp_kms_client.h" using crypto::tink::Aead; using crypto::tink::integration::gcpkms::GcpKmsClient; std::string kek_uri = "gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar"; std::string credentials = "credentials.json"; const KeyTemplate& dek_template = AeadKeyTemplates::Aes128Gcm(); // Register GcpKmsClient. auto client_result = GcpKmsClient::RegisterNewClient(kek_uri, credentials); if (!client_result.ok()) { std::clog << "GCP KMS client registration failed: " << client_result.status().error_message() << "\n"; exit(1); } // 1. Get a handle to the key material. const KeyTemplate& envelope_kt = AeadKeyTemplates::KmsEnvelopeAead(kek_uri, dek_template); auto new_keyset_handle_result = KeysetHandle::GenerateNew(envelope_kt); if (!new_keyset_handle_result.ok()) return new_keyset_handle_result.status(); // The nice thing about envelope encryption is that you don't have to store // this keyset handle because it only contains a reference to the remote KEK. auto keyset_handle = std::move(new_keyset_handle_result.ValueOrDie()); // 2. Get the primitive. auto aead_result= keyset_handle.GetPrimitive<Aead>(); if (!aead_result.ok()) return aead_result.status(); auto aead = std::move(aead_result.ValueOrDie()); // 3. Use the primitive. auto ciphertext_result = aead->Encrypt(plaintext, aad); if (!ciphertext_result.ok()) return ciphertext_result.status(); auto ciphertext = std::move(ciphertext_result.ValueOrDie());