| # Tink for C++ HOW-TO |
| |
| The following subsections present instructions and/or C++ snippets for some |
| common tasks in [Tink](https://github.com/google/tink). |
| |
| |
| ## Installing Tink |
| |
| Tink is built with [Bazel](https://www.bazel.build), 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](https://github.com/thaidn/tink-examples) 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. |
| |
| #### Supported Platforms |
| |
| * Linux x86_64 |
| * macOS x86_64, 10.12.6 (Sierra) or newer |
| |
| #### Caveats |
| |
| Tink depends on [Abseil](https://github.com/abseil/abseil-cpp), [Protocol |
| Buffers](https://developers.google.com/protocol-buffers/), and |
| [BoringSSL](https://opensource.google.com/projects/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](https://github.com/google/tink/blob/master/WORKSPACE) file), or not |
| depend directly on these libraries at all (i.e. have only the indirect |
| dependence via Tink). |
| |
| ### Installing pre-built binaries |
| |
| 1. Download and extract the Tink library into `/usr/local/lib` by invoking the |
| following shell commands: |
| |
| ```sh |
| OS="linux" # Change to "darwin" for macOS |
| TARGET_DIR="/usr/local" |
| curl -L \ |
| "https://storage.googleapis.com/tink/releases/libtink-${OS}-x86_64-1.2.0-rc2.tar.gz" | |
| sudo tar -xz -C $TARGET_DIR |
| ``` |
| |
| The tar command extracts the Tink library into the `lib` subdirectory of |
| `TARGET_DIR`. For example, specifying `/usr/local` as `TARGET_DIR` causes tar to |
| extract the Tink library into `/usr/local/lib`. |
| |
| If you'd prefer to extract the library into a different directory, adjust |
| `TARGET_DIR` accordingly. |
| |
| 1. On Linux, if in Step 2 you specified a system directory (for example, |
| `/usr/local`) as the `TARGET_DIR`, then run ldconfig to configure the |
| linker. For example: |
| |
| ```sh |
| 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: |
| |
| ```sh |
| export LIBRARY_PATH=$LIBRARY_PATH:$TARGET_DIR/lib |
| export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TARGET_DIR/lib |
| ``` |
| |
| ### Installing from the source |
| |
| #### Prerequisites |
| |
| To install Tink from the source code, the following prerequisites must be installed: |
| |
| * [git](https://git-scm.com/) - to download the source of Tink |
| * [Bazel](https://www.bazel.build) - to build the Tink library |
| |
| #### Step-by-step instructions to build and use `libtink.so` |
| |
| 1. clone Tink from GitHub: |
| |
| ```sh |
| git clone https://github.com/google/tink/ |
| ``` |
| |
| 2. build the library and header-file bundles, and install them in appropriate |
| directories of the target project (`TARGET_DIR`): |
| |
| ```sh |
| cd tink |
| TARGET_DIR="/usr/local" |
| mkdir -p $TARGET_DIR/lib $TARGET_DIR/include |
| bazel build -c opt cc:libtink.so |
| sudo cp bazel-bin/cc/libtink.so $TARGET_DIR/lib/ |
| bazel build cc:tink_headers cc:tink_deps_headers |
| 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/ |
| ``` |
| |
| 3. On Linux, if in Step 2 you specified a system directory (for example, |
| `/usr/local`) as the `TARGET_DIR`, then run ldconfig to configure the |
| linker. For example: |
| |
| ```sh |
| 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: |
| |
| ```sh |
| export LIBRARY_PATH=$LIBRARY_PATH:$TARGET_DIR/lib |
| export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TARGET_DIR/lib |
| ``` |
| |
| ### Validate your installation |
| |
| To validate the installation compile and run [`hello_world.cc`](https://github.com/google/tink/tree/master/examples/helloworld/cc/hello_world.cc). |
| |
| 1. Copy the source code and a test cryptographic key, create some plaintext to encrypt. |
| |
| ```sh |
| 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 |
| ``` |
| |
| 2. Compile the source code. |
| |
| ```sh |
| g++ -std=c++11 -I$TARGET_DIR/include/ -L$TARGET_DIR/lib/ hello_world.cc -ltink -o hello_world |
| ``` |
| |
| 3. Run `hello_world` application to encrypt and decrypt some data. |
| |
| ```sh |
| ./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 |
| ``` |
| |
| ## 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: |
| |
| ```cpp |
| #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, proceed as follows: |
| |
| ```cpp |
| #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: |
| |
| ```cpp |
| #include "tink/registry.h" |
| #include "custom_project/custom_aead_key_manager.h" |
| |
| // ... |
| auto status = Registry::RegisterKeyManager(new CustomAeadKeyManager()); |
| if (!status.ok()) /* ... handle failure */; |
| ``` |
| |
| A more complex custom initialization (especially when registering a mix of |
| standard and custom key managers) can take advantage of |
| [`Config`](https://github.com/google/tink/blob/master/cc/config.h)-class, which |
| enables use of human-readable configuration files for Tink initialization. |
| Please note however that to use such configurations one must first add to the |
| Registry so-called _catalogues_, which provide a bridge between text |
| descriptions of key managers and their implementations (see |
| e.g. [`AeadConfig::Register()`](https://github.com/google/tink/blob/master/cc/aead/aead_config.cc)-method). |
| |
| |
| ## Generating New Key(set)s |
| |
| 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](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 C++ code, one can use |
| [`KeysetHandle`](https://github.com/google/tink/blob/master/cc/keyset_handle.h): |
| |
| ```cpp |
| 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](https://github.com/google/tink/tree/master/examples/keytemplates)-folder. |
| |
| |
| ## Loading Existing Keysets |
| |
| To load cleartext keysets, use |
| [`CleartextKeysetHandle`](https://github.com/google/tink/blob/master/cc/cleartext_keyset_handle.h) and an appropriate [`KeysetReader`](https://github.com/google/tink/blob/master/cc/keyset_reader.h), depending on the wire format of the stored keyset, for example a [`BinaryKeysetReader`](https://github.com/google/tink/blob/master/cc/binary_keyset_reader.h) or a [`JsonKeysetReader`](https://github.com/google/tink/blob/master/cc/json_keyset_reader.h). |
| |
| ```cpp |
| #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`](https://github.com/google/tink/blob/master/cc/keyset_handle.h) and an appropriate [`KeysetReader`](https://github.com/google/tink/blob/master/cc/keyset_reader.h): |
| |
| ```cpp |
| #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()); |
| ``` |
| |
| ## Obtaining and Using Primitives |
| |
| [_Primitives_](PRIMITIVES.md) 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](KEY-MANAGEMENT.md#key-keyset-and-keysethandle) 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, KMS Envelope, XCHACHA20-POLY1305 | |
| | Deterministic AEAD | AES-SIV | |
| | Streaming AEAD | AES-GCM-HKDF-STREAMING | |
| | MAC | HMAC-SHA2 | |
| | Digital Signatures | ECDSA over NIST curves, Ed25519, RSA-SSA-PKCS1, RSA-SSA-PSS | |
| | Hybrid Encryption | ECIES with AEAD and HKDF | |
| |
| The user obtains a primitive by calling the function `getPrimitive<>` of the |
| `KeysetHandle`. |
| |
| ### Symmetric Key Encryption |
| |
| Here is how 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: |
| |
| ```cpp |
| #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()); |
| ``` |
| |
| ### Hybrid Encryption |
| |
| To decrypt using [a combination of public key encryption and |
| symmetric key encryption](PRIMITIVES.md#hybrid-encryption): |
| |
| ```cpp |
| #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()); |
| ``` |