diff options
author | cinlin <cinlin@google.com> | 2023-07-11 14:52:51 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-07-11 14:56:04 -0700 |
commit | 10c008a4d077fadcd78ba02665b1088da98b3351 (patch) | |
tree | 45d62d596cce8323c096b701c7d4d28961781702 | |
parent | a14cdf3e36d2207b4849e28fff402f40cb69549a (diff) | |
download | tink-10c008a4d077fadcd78ba02665b1088da98b3351.tar.gz |
Add deterministic AEAD to C++ examples directory.
Keyset generated via
```tinkey create-keyset --key-template AES256_SIV --out keyset.json```
PiperOrigin-RevId: 547304870
-rw-r--r-- | cc/examples/daead/BUILD.bazel | 40 | ||||
-rw-r--r-- | cc/examples/daead/CMakeLists.txt | 16 | ||||
-rw-r--r-- | cc/examples/daead/deterministic_aead_cli.cc | 134 | ||||
-rwxr-xr-x | cc/examples/daead/deterministic_aead_cli_test.sh | 215 | ||||
-rw-r--r-- | cc/examples/daead/deterministic_aead_test_keyset.json | 15 |
5 files changed, 420 insertions, 0 deletions
diff --git a/cc/examples/daead/BUILD.bazel b/cc/examples/daead/BUILD.bazel new file mode 100644 index 000000000..2e2637bf3 --- /dev/null +++ b/cc/examples/daead/BUILD.bazel @@ -0,0 +1,40 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +filegroup( + name = "deterministic_aead_test_keyset", + srcs = ["deterministic_aead_test_keyset.json"], +) + +cc_binary( + name = "deterministic_aead_cli", + srcs = ["deterministic_aead_cli.cc"], + deps = [ + "//util", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/strings", + "@tink_cc//:deterministic_aead", + "@tink_cc//:keyset_handle", + "@tink_cc//:keyset_reader", + "@tink_cc//config:tink_config", + "@tink_cc//daead:deterministic_aead_config", + "@tink_cc//util:status", + ], +) + +sh_test( + name = "deterministic_aead_cli_test", + size = "small", + srcs = ["deterministic_aead_cli_test.sh"], + args = [ + "$(rootpath :deterministic_aead_cli)", + "$(rootpath :deterministic_aead_test_keyset)", + ], + data = [ + ":deterministic_aead_cli", + ":deterministic_aead_test_keyset", + ], +) diff --git a/cc/examples/daead/CMakeLists.txt b/cc/examples/daead/CMakeLists.txt new file mode 100644 index 000000000..bd4b9e1e9 --- /dev/null +++ b/cc/examples/daead/CMakeLists.txt @@ -0,0 +1,16 @@ +add_executable(deterministic_aead_cli deterministic_aead_cli.cc) +target_include_directories(deterministic_aead_cli PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(deterministic_aead_cli + tink::static + absl::check + absl::flags_parse + util) + +add_test( + NAME deterministic_aead_cli_test + COMMAND "${BASH_PROGRAM}" + "${CMAKE_CURRENT_SOURCE_DIR}/deterministic_aead_cli_test.sh" + "${CMAKE_CURRENT_BINARY_DIR}/deterministic_aead_cli" + "${CMAKE_CURRENT_SOURCE_DIR}/deterministic_aead_test_keyset.json") diff --git a/cc/examples/daead/deterministic_aead_cli.cc b/cc/examples/daead/deterministic_aead_cli.cc new file mode 100644 index 000000000..fc2d29854 --- /dev/null +++ b/cc/examples/daead/deterministic_aead_cli.cc @@ -0,0 +1,134 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////////////// +// [START daead-example] +// A command-line utility for testing Tink Deterministic AEAD. +#include <iostream> +#include <memory> +#include <ostream> +#include <string> + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/log/check.h" +#include "absl/strings/string_view.h" +#include "tink/daead/deterministic_aead_config.h" +#include "tink/deterministic_aead.h" +#include "util/util.h" +#include "tink/keyset_handle.h" +#include "tink/util/status.h" + +ABSL_FLAG(std::string, keyset_filename, "", "Keyset file in JSON format"); +ABSL_FLAG(std::string, mode, "", "Mode of operation {encrypt|decrypt}"); +ABSL_FLAG(std::string, input_filename, "", "Filename to operate on"); +ABSL_FLAG(std::string, output_filename, "", "Output file name"); +ABSL_FLAG(std::string, associated_data, "", + "Associated data for Deterministic AEAD (default: empty"); + +namespace { + +using ::crypto::tink::DeterministicAead; +using ::crypto::tink::DeterministicAeadConfig; +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; + +constexpr absl::string_view kEncrypt = "encrypt"; +constexpr absl::string_view kDecrypt = "decrypt"; + +void ValidateParams() { + // [START_EXCLUDE] + CHECK(absl::GetFlag(FLAGS_mode) == kEncrypt || + absl::GetFlag(FLAGS_mode) == kDecrypt) + << "Invalid mode; must be `encrypt` or `decrypt`"; + CHECK(!absl::GetFlag(FLAGS_keyset_filename).empty()) + << "Keyset file must be specified"; + CHECK(!absl::GetFlag(FLAGS_input_filename).empty()) + << "Input file must be specified"; + CHECK(!absl::GetFlag(FLAGS_output_filename).empty()) + << "Output file must be specified"; + // [END_EXCLUDE] +} + +} // namespace + +namespace tink_cc_examples { + +// Deterministic AEAD example CLI implementation. +Status DeterministicAeadCli(absl::string_view mode, + const std::string& keyset_filename, + const std::string& input_filename, + const std::string& output_filename, + absl::string_view associated_data) { + Status result = DeterministicAeadConfig::Register(); + if (!result.ok()) return result; + + // Read keyset from file. + StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = + ReadJsonCleartextKeyset(keyset_filename); + if (!keyset_handle.ok()) return keyset_handle.status(); + + // Get the primitive. + StatusOr<std::unique_ptr<DeterministicAead>> daead = + (*keyset_handle)->GetPrimitive<DeterministicAead>(); + if (!daead.ok()) return daead.status(); + + // Read the input. + StatusOr<std::string> input_file_content = ReadFile(input_filename); + if (!input_file_content.ok()) return input_file_content.status(); + + // Compute the output. + std::string output; + if (mode == kEncrypt) { + StatusOr<std::string> result = (*daead)->EncryptDeterministically( + *input_file_content, associated_data); + if (!result.ok()) return result.status(); + output = *result; + } else if (mode == kDecrypt) { + StatusOr<std::string> result = (*daead)->DecryptDeterministically( + *input_file_content, associated_data); + if (!result.ok()) return result.status(); + output = *result; + } + + // Write output to file. + return WriteToFile(output, output_filename); +} + +} // namespace tink_cc_examples + +int main(int argc, char** argv) { + absl::ParseCommandLine(argc, argv); + + ValidateParams(); + + std::string mode = absl::GetFlag(FLAGS_mode); + std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename); + std::string input_filename = absl::GetFlag(FLAGS_input_filename); + std::string output_filename = absl::GetFlag(FLAGS_output_filename); + std::string associated_data = absl::GetFlag(FLAGS_associated_data); + + std::clog << "Using keyset from file " << keyset_filename + << " to Deterministic AEAD-" << mode << " file " << input_filename + << " with associated data '" << associated_data << "'." + << std::endl; + std::clog << "The resulting output will be written to " << output_filename + << "." << std::endl; + + CHECK_OK(tink_cc_examples::DeterministicAeadCli( + mode, keyset_filename, input_filename, output_filename, associated_data)); + return 0; +} +// [END daead-example] diff --git a/cc/examples/daead/deterministic_aead_cli_test.sh b/cc/examples/daead/deterministic_aead_cli_test.sh new file mode 100755 index 000000000..553e7a188 --- /dev/null +++ b/cc/examples/daead/deterministic_aead_cli_test.sh @@ -0,0 +1,215 @@ +#!/bin/bash +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +set -euo pipefail + +############################################################################# +# Tests for Tink CC Deterministic AEAD. +############################################################################# + +: "${TEST_TMPDIR:=$(mktemp -d)}" + +readonly CLI="$1" +readonly KEYSET_FILE="$2" +readonly DATA_FILE="${TEST_TMPDIR}/example_data.txt" +readonly TEST_NAME="TinkExamplesCcDeterministicAeadTest" + +echo "This is some plaintext to be encrypted." > "${DATA_FILE}" + +####################################### +# A helper function for getting the return code of a command that may fail. +# Temporarily disables error safety and stores return value in TEST_STATUS. +# +# Globals: +# TEST_STATUS +# Arguments: +# Command to execute. +####################################### +test_command() { + set +e + "$@" + TEST_STATUS=$? + set -e +} + +####################################### +# Asserts that the outcome of the latest test command is 0. +# +# If not, it terminates the test execution. +# +# Globals: +# TEST_STATUS +# TEST_NAME +# TEST_CASE +####################################### +assert_command_succeeded() { + if (( TEST_STATUS != 0 )); then + echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" + exit 1 + fi +} + +####################################### +# Asserts that the outcome of the latest test command is not 0. +# +# If not, it terminates the test execution. +# +# Globals: +# TEST_STATUS +# TEST_NAME +# TEST_CASE +####################################### +assert_command_failed() { + if (( TEST_STATUS == 0 )); then + echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" + exit 1 + fi +} + +####################################### +# Starts a new test case; records the test case name to TEST_CASE. +# +# Globals: +# TEST_NAME +# TEST_CASE +# Arguments: +# test_case: The name of the test case. +####################################### +start_test_case() { + TEST_CASE="$1" + echo "[ RUN ] ${TEST_NAME}.${TEST_CASE}" +} + +####################################### +# Ends a test case printing a success message. +# +# Globals: +# TEST_NAME +# TEST_CASE +####################################### +end_test_case() { + echo "[ OK ] ${TEST_NAME}.${TEST_CASE}" +} + +############################################################################# + +start_test_case "encrypt" + +# Run encryption. +test_command "${CLI}" \ + --mode encrypt \ + --keyset_filename "${KEYSET_FILE}" \ + --input_filename "${DATA_FILE}" \ + --output_filename "${DATA_FILE}.encrypted" +assert_command_succeeded + +end_test_case + +############################################################################# + +start_test_case "decrypt" + +# Run decryption. +test_command "${CLI}" \ + --mode decrypt \ + --keyset_filename "${KEYSET_FILE}" \ + --input_filename "${DATA_FILE}.encrypted" \ + --output_filename "${DATA_FILE}.decrypted" +assert_command_succeeded + +test_command cmp -s "${DATA_FILE}" "${DATA_FILE}.decrypted" +assert_command_succeeded + +end_test_case + +############################################################################# + +start_test_case "encrypt_decrypt_fails_with_modified_ciphertext" + +# Run encryption +test_command "${CLI}" \ + --mode encrypt \ + --keyset_filename "${KEYSET_FILE}" \ + --input_filename "${DATA_FILE}" \ + --output_filename "${DATA_FILE}.encrypted" +assert_command_succeeded + +# Modify ciphertext. +echo "modified" >> "${DATA_FILE}.encrypted" + +# Run decryption. +test_command "${CLI}" \ + --mode decrypt \ + --keyset_filename "${KEYSET_FILE}" \ + --input_filename "${DATA_FILE}.encrypted" \ + --output_filename "${DATA_FILE}.decrypted" +assert_command_failed + +end_test_case + +############################################################################# + +start_test_case "encrypt_decrypt_succeeds_with_associated_data" + +# Run encryption. +ASSOCIATED_DATA="header information" +test_command "${CLI}" \ + --mode encrypt \ + --keyset_filename "${KEYSET_FILE}" \ + --input_filename "${DATA_FILE}" \ + --output_filename "${DATA_FILE}.encrypted" \ + --associated_data "${ASSOCIATED_DATA}" +assert_command_succeeded + +# Run decryption. +test_command "${CLI}" \ + --mode decrypt \ + --keyset_filename "${KEYSET_FILE}" \ + --input_filename "${DATA_FILE}.encrypted" \ + --output_filename "${DATA_FILE}.decrypted" \ + --associated_data "${ASSOCIATED_DATA}" +assert_command_succeeded + +cmp --silent "${DATA_FILE}" "${DATA_FILE}.decrypted" +assert_command_succeeded + +end_test_case + +############################################################################# + +start_test_case "encrypt_decrypt_fails_with_modified_associated_data" + +# Run encryption. +ASSOCIATED_DATA="header information" +test_command "${CLI}" \ + --mode encrypt \ + --keyset_filename "${KEYSET_FILE}" \ + --input_filename "${DATA_FILE}" \ + --output_filename "${DATA_FILE}.encrypted" \ + --associated_data "${ASSOCIATED_DATA}" +assert_command_succeeded + +# Run decryption. +MODIFIED_ASSOCIATED_DATA="modified header information" +test_command "${CLI}" \ + --mode decrypt \ + --keyset_filename "${KEYSET_FILE}" \ + --input_filename "${DATA_FILE}.encrypted" \ + --output_filename "${DATA_FILE}.decrypted" \ + --associated_data "${MODIFIED_ASSOCIATED_DATA}" +assert_command_failed + +end_test_case diff --git a/cc/examples/daead/deterministic_aead_test_keyset.json b/cc/examples/daead/deterministic_aead_test_keyset.json new file mode 100644 index 000000000..74cd75f56 --- /dev/null +++ b/cc/examples/daead/deterministic_aead_test_keyset.json @@ -0,0 +1,15 @@ +{ + "primaryKeyId": 1184417862, + "key": [ + { + "keyData": { + "typeUrl": "type.googleapis.com/google.crypto.tink.AesSivKey", + "value": "EkAbqs8wuMAXvuqU9FVOW9VvG9kE9P3aI5qjnkGvNTeRh/Cxoh06kosU5R9jRCHCkdMgnOSHMtfIKkQj5exuhesH", + "keyMaterialType": "SYMMETRIC" + }, + "status": "ENABLED", + "keyId": 1184417862, + "outputPrefixType": "TINK" + } + ] +} |