The following subsections present instructions and/or C++ snippets for some common tasks in Tink.
Tink is built with Bazel, so it is quite easy to use Tink in a project built with Bazel, and this is the recommended way. See for example tink-examples repo on how to import Tink using Bazel's WORKSPACE file.
Still, there are definitely projects for which using Bazel is not an option, and for such situations we offer a library that can be used with other build tools. Currently only we offer support for Linux machines, but are working on supporting other operating systems as well.
Warning: In any case, the use of Tink without Bazel is at experimental stage, so the instructions given below might not work in some environments.
- Linux x86-64
- MacOS x86-64, 10.12.6 (Sierra) or newer
Tink depends on Abseil, Protocol Buffers, and BoringSSL, so any project that wants to use Tink should either depend on the same versions of these libraries (cf. versions in the corresponding entries in WORKSPACE file), or not depend directly on these libraries at all (i.e. have only the indirect dependence via Tink).
-
Download and unpack a tar-archive with pre-built binary and relevant headers.
cd /tmp curl https://some.repository/tar_bin_snapshot.tar.gz -O (TODO) tar xfvz tar_bin_snapshot.tar.gz
-
Install the binary and the headers in appropriate directories of the target project (
TARGET_DIR
):TARGET_DIR="/usr/local" mkdir -p $TARGET_DIR/lib $TARGET_DIR/include sudo cp tink_bin_snapshot/libtink.so $TARGET_DIR/lib/ sudo tar xfv tink_bin_snapshot/tink_headers.tar -C $TARGET_DIR/include/ sudo tar xfv tink_bin_snapshot/tink_deps_headers.tar -C $TARGET_DIR/include/
-
If in Step 2 you specified a system directory (for example,
/usr/local
) as theTARGET_DIR
, then run ldconfig to configure the linker. For example:sudo ldconfig
If you assigned a
TARGET_DIR
other than a system directory (for example,~/mydir
), then you must append the extraction directory (for example,~/mydir/lib
) to two environment variables:export LIBRARY_PATH=$LIBRARY_PATH:$TARGET_DIR/lib export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TARGET_DIR/lib
To install Tink from the source code, the following prerequisites must be installed:
-
clone Tink from GitHub:
git clone https://github.com/google/tink/
-
build the library and header-file bundles, and install them in appropriate directories of the target project (
TARGET_DIR
):cd tink TARGET_DIR="/usr/local" bazel build cc:libtink.so bazel build cc:tink_headers cc:tink_deps_headers mkdir -p $TARGET_DIR/lib $TARGET_DIR/include sudo cp bazel-bin/cc/libtink.so $TARGET_DIR/lib/ sudo tar xfv bazel-genfiles/cc/tink_headers.tar -C $TARGET_DIR/include/ sudo tar xfv bazel-genfiles/cc/tink_deps_headers.tar -C $TARGET_DIR/include/
-
If in Step 2 you specified a system directory (for example,
/usr/local
) as theTARGET_DIR
, then run ldconfig to configure the linker. For example:sudo ldconfig
If you assigned a
TARGET_DIR
other than a system directory (for example,~/mydir
), then you must append the extraction directory (for example,~/mydir/lib
) to two environment variables:export LIBRARY_PATH=$LIBRARY_PATH:$TARGET_DIR/lib export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TARGET_DIR/lib
To validate the installation compile and run hello_world.cc
.
-
Copy the source code and a test cryptographic key, create some plaintext to encrypt.
cd /tmp GITHUB_DIR=https://raw.githubusercontent.com/google/tink/master/examples/helloworld/cc/ curl $GITHUB_DIR/hello_world.cc -O $GITHUB_DIR/aes128_gcm_test_keyset_json.txt -O echo "some message to be encrypted" > plaintext.txt
-
Compile the source code.
g++ -std=c++11 -I$TARGET_DIR/include/ -L$TARGET_DIR/lib/ hello_world.cc -ltink -o hello_world
-
Run
hello_world
application to encrypt and decrypt some data../hello_world aes128_gcm_test_keyset_json.txt encrypt plaintext.txt "associated data" ciphertext.bin ./hello_world aes128_gcm_test_keyset_json.txt decrypt ciphertext.bin "associated data" decrypted.txt cat decrypted.txt
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. Registration
For example, if you want to use all implementations of all primitives in Tink 1.1.0, the initialization would look as follows:
#include "tink/config/tink_config.h"
// ...
auto status = TinkConfig::Init();
if (!status.ok()) /* ... handle failure */;
status = Config::Register(TinkConfig::Tink_1_1_0());
// ...
To use only implementations of the AEAD primitive:
#include "tink/aead/aead_config.h"
// ...
auto status = AeadConfig::Init();
if (!status.ok()) /* ... handle failure */;
status = Config::Register(AeadConfig::Tink_1_1_0());
// ...
For custom initialization the registration proceeds directly via
Registry
-class:
#include "tink/registry.h"
#include "custom_project/custom_aead_key_manager.h"
// ...
auto status = Registry::RegisterKeyManager(
CustomAeadKeyManager.kKeyType, new CustomAeadKeyManager());
if (!status.ok()) /* ... handle failure */;
Each KeyManager
-implementation provides NewKey(template)
-method that generates new
keys of the corresponding key type. However to avoid accidental leakage of
sensitive key material one should be careful with 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 C++ code, one 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...
where key_template
can be initialized with one of pre-generated templates from
examples/keytemplates-folder.
To load cleartext keysets, use
CleartextKeysetHandle
and an appropriate KeysetReader
, depending on the wire format of the stored keyset, for example a BinaryKeysetReader
or a JsonKeysetReader
.
#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());
To load encrypted keysets, one can use
KeysetHandle
and an appropriate KeysetReader
:
#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());
Primitives represent cryptographic operations offered by Tink, hence they form the core of Tink API. A primitive is just an interface that specifies what operations are offered by the primitive. A primitive can have multiple implementations, and user chooses a desired implementation by using a key of corresponding type (see the this section for details).
The following table summarizes C++ implementations of primitives that are currently available or planned (the latter are listed in brackets).
Primitive | Implementations |
---|---|
AEAD | AES-GCM, AES-CTR-HMAC, AES-EAX |
MAC | HMAC-SHA2 |
Digital Signatures | ECDSA over NIST curves, (Ed25519) |
Hybrid Encryption | ECIES with AEAD and HKDF |
Tink user accesses implementations of a primitive via a factory that corresponds
to the primitive: AEAD via AeadFactory
, MAC via MacFactory
, etc. where each
factory offers corresponding getPrimitive(...)
methods.
Here is how you can obtain and use an AEAD (Authenticated Encryption with Associated Data) primitive to encrypt or decrypt data:
#include "tink/aead.h"
#include "tink/keyset_handle.h"
#include "tink/aead/aead_factory.h"
// 1. Get a handle to the key material.
KeysetHandle keyset_handle = ...;
// 2. Get the primitive.
auto aead_result= AeadFactory.GetPrimitive(keyset_handle);
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.status();
auto ciphertext = std::move(ciphertext_result.ValueOrDie());
To decrypt using a combination of public key encryption and symmetric key encryption:
#include "tink/hybrid_decrypt.h"
#include "tink/keyset_handle.h"
#include "tink/hybrid/hybrid_decrypt_factory.h"
// 1. Get a handle to the key material.
KeysetHandle keyset_handle = ...;
// 2. Get the primitive.
auto hybrid_decrypt_result= HybridDecryptFactory.GetPrimitive(keyset_handle);
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.status();
auto plaintext = std::move(plaintext_result.ValueOrDie());