Merge commit '053b5b8c4214ec379daac2cc2bf208c51cc1cd66' into main Current Tink mirror in Fuchsia has missing include issue with the latest version of Clang. The missing include is #include <cstdlib> (https://github.com/tink-crypto/tink/blob/master/cc/util/secret_data_internal.h#L21) Tink introduced it in https://github.com/tink-crypto/tink/commit/053b5b8c4214ec379daac2cc2bf208c51cc1cd66 We should at least uprev Tink to this commit to unblock the issue. Command to uprev Tink: ``` git checkout origin/main -b ${USER}-merge git merge 053b5b8c4214ec379daac2cc2bf208c51cc1cd66 python3 ./tools/convert_for_cobalt fx format-code --files=${gn build files that convert_for_cobalt wrote} git commit --no-verify ``` Locally patch to include missing absl/strings/str_cat.h header: cc/mac/hmac_parameters.cc cc/mac/aes_cmac_parameters.cc Bug: 376297654 Change-Id: I7cc47556e5ddec6be01bd7a523f527abea47e20a Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/tink/+/1153894 Commit-Queue: Anivia Li <aniviali@google.com> Reviewed-by: Alex Pankhurst <pankhurst@google.com>
diff --git a/.bazelversion b/.bazelversion index ac14c3d..09b254e 100644 --- a/.bazelversion +++ b/.bazelversion
@@ -1 +1 @@ -5.1.1 +6.0.0
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 66f2ffc..2f37316 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,7 +1,6 @@ --- name: Bug report about: Create a report to help us improve Tink -assignees: 'thaidn, chuckx' ---
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 486da6f..100d12c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,7 +1,6 @@ --- name: Feature request about: Suggest an idea for Tink -assignees: 'thaidn, chuckx' ---
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 49711e3..49269d9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml
@@ -32,7 +32,7 @@ steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. @@ -45,7 +45,7 @@ # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # âšī¸ Command-line programs to run using the OS shell. # đ https://git.io/JvXDl @@ -70,4 +70,4 @@ # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2
diff --git a/CMakeLists.txt b/CMakeLists.txt index dff0033..2e4f78f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.5) -project(Tink VERSION 1.7.0 LANGUAGES CXX) +cmake_minimum_required(VERSION 3.13) +project(Tink VERSION 2.0.0 LANGUAGES CXX) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -7,8 +7,6 @@ option(TINK_USE_SYSTEM_OPENSSL "Build Tink linking to OpenSSL installed in the system" OFF) option(TINK_USE_INSTALLED_ABSEIL "Build Tink linking to Abseil installed in the system" OFF) option(TINK_USE_INSTALLED_GOOGLETEST "Build Tink linking to GTest installed in the system" OFF) -option(TINK_USE_ABSL_STATUS "Compile Tink with absl::Status" OFF) -option(TINK_USE_ABSL_STATUSOR "Compile Tink with absl::StatusOr" OFF) option(USE_ONLY_FIPS "Enables the FIPS only mode in Tink" OFF) set(CPACK_GENERATOR TGZ)
diff --git a/README.md b/README.md index 74035b9..3ff7337 100644 --- a/README.md +++ b/README.md
@@ -1,15 +1,74 @@ # Tink -*A multi-language, cross-platform library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.* +*A multi-language, cross-platform library that provides cryptographic APIs that +are secure, easy to use correctly, and hard(er) to misuse. See also: +https://developers.google.com/tink*. -https://developers.google.com/tink +> **NOTE**: **Tink is moving!** +> +> As part of our roadmap we are splitting Tink into +> [multiple GitHub repositories][split_repo_roadmap_url] that will be hosted at +> [github.com/tink-crypto](https://github.com/tink-crypto) and will be +> independently versioned. +> +> Roughly, we are going to create one repository per language, library extension +> such as KMS (except Tink Python), and tools. +> +> A few important highlights: +> +> - The migration will be done gradually over the course of 2023 with a new +> release from each of the new repositories. Releases will be announced in +> our [mailing list][tink_mailing_list_url]. +> - We will keep updating each implementation/tool in +> [github.com/google/tink](https://github.com/google/tink) for a specified +> amount of time; migrated implementations/tools will eventually stop being +> updated on [github.com/google/tink](https://github.com/google/tink). The +> support window depends on the specific implementation, as shown in the +> table below. +> - New issues and pull requests should be created in the new repos. +> +> Below is the list of resulting repositories, migration timeline and expected +> end of support. +> +> Tink implementation/extension | New repository | Migration status | End of support in google/tink +> ------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------ | ----------------------------- +> Tink Java | [tink-crypto/tink-java](https://github.com/tink-crypto/tink-java) | Complete (Q1 2023) | Q3 2023 +> Tink Java AWS KMS extension | [tink-crypto/tink-java-awskms](https://github.com/tink-crypto/tink-java-awskms) | Complete (Q1 2023) | Q3 2023 +> Tink Java Google Cloud KMS extension | [tink-crypto/tink-java-gcpkms](https://github.com/tink-crypto/tink-java-gcpkms) | Complete (Q1 2023) | Q3 2023 +> Tink Java apps extension | [tink-crypto/tink-java-apps](https://github.com/tink-crypto/tink-java-apps) | Complete (Q1 2023) | Q3 2023 +> Tink C++ | [tink-crypto/tink-cc](https://github.com/tink-crypto/tink-cc) | Complete (Q2 2023) | Q4 2023 +> Tink C++ AWS KMS extension | [tink-crypto/tink-cc-awskms](https://github.com/tink-crypto/tink-cc-awskms) | Complete (Q2 2023) | Q4 2023 +> Tink C++ Google Cloud KMS extension | [tink-crypto/tink-cc-gcpkms](https://github.com/tink-crypto/tink-cc-gcpkms) | Complete (Q2 2023) | Q4 2023 +> Tink Python | [tink-crypto/tink-py](https://github.com/tink-crypto/tink-py) | Not started (expected Q3 2023) | TBA +> Tink Go | [tink-crypto/tink-go](https://github.com/tink-crypto/tink-go) | In progress (expected Q2 2023) | TBA +> Tink Go AWS KMS extension | [tink-crypto/tink-go-awskms](https://github.com/tink-crypto/tink-go-awskms) | In progress (expected Q2 2023) | TBA +> Tink Go Google Cloud KMS extension | [tink-crypto/tink-go-gcpkms](https://github.com/tink-crypto/tink-go-gcpkms) | In progress (expected Q2 2023) | TBA +> Tink Go HashiCorp Vault KMS extension | [tink-crypto/tink-go-hcvault](https://github.com/tink-crypto/tink-go-hcvault) | In progress (expected Q2 2023) | TBA +> Tink Obj-C | [tink-crypto/tink-objc](https://github.com/tink-crypto/tink-objc) | Not started (expected Q4 2023) | TBA +> Tink Tinkey | [tink-crypto/tink-tinkey](https://github.com/tink-crypto/tink-tinkey) | Complete (Q2 2023) | Q4 2023 +> Tink cross language tests | [tink-crypto/tink-cross-lang-tests](https://github.com/tink-crypto/tink-cross-lang-tests) | Not started (expected Q4 2023) | TBA -**`Ubuntu`** | **`macOS`** ------------------------------------ | --------------------------------- -[![Kokoro Ubuntu][ubuntu_badge]](#) | [![Kokoro macOS][macos_badge]](#) +> **NOTE**: **We are removing Tink for JavaScript/TypeScript** +> +> We are removing the Tink JavaScript/TypeScript library from our current Github +> repository (master branch). As part of our effort to migrate Tink to +> https://github.com/tink-crypto, we will not release an individual +> JavaScript/Typescript repository. Furthermore, the JavaScript/TypeScript +> [directory](https://github.com/google/tink/tree/master/javascript) in the +> current release branch (v1.7.0) will no longer be actively supported. +> +> _We aim to remove the JS/TS directory from the current Tink Github repository +> (master branch) on **June 22, 2023**. We will also deprecate the Tink npm +> package on this date._ +> +> See [this](https://github.com/google/tink/issues/689) tracking issue for more +> details. +> +> Feel free to use our [mailing list][tink_mailing_list_url] to raise any +> questions, issues or concerns. -[ubuntu_badge]: https://storage.googleapis.com/tink-kokoro-build-badges/tink-ubuntu.png -[macos_badge]: https://storage.googleapis.com/tink-kokoro-build-badges/tink-macos.png +[split_repo_roadmap_url]: https://developers.google.com/tink/roadmap#splitting_tink_into_multiple_github_repositories +[tink_mailing_list_url]: https://groups.google.com/forum/#!forum/tink-users ## Index @@ -25,9 +84,10 @@ Using crypto in your application [shouldn't have to][devs_are_users_too_slides] feel like juggling chainsaws in the dark. Tink is a crypto library written by a group of cryptographers and security engineers at Google. It was born out of our -extensive experience working with Google's product teams, [fixing weaknesses in -implementations](https://github.com/google/wycheproof), and providing simple -APIs that can be used safely without needing a crypto background. +extensive experience working with Google's product teams, +[fixing weaknesses in implementations](https://github.com/google/wycheproof), +and providing simple APIs that can be used safely without needing a crypto +background. Tink provides secure APIs that are easy to use correctly and hard(er) to misuse. It reduces common crypto pitfalls with user-centered design, careful @@ -52,6 +112,15 @@ released on 2022-08-09. Javascript/Typescript is in an alpha state and should only be used for testing. +Please see the intent to remove statement +[here](https://github.com/google/tink/issues/689). + +**`Ubuntu`** | **`macOS`** +----------------------------------- | --------------------------------- +[![Kokoro Ubuntu][ubuntu_badge]](#) | [![Kokoro macOS][macos_badge]](#) + +[ubuntu_badge]: https://storage.googleapis.com/tink-kokoro-build-badges/tink-ubuntu.png +[macos_badge]: https://storage.googleapis.com/tink-kokoro-build-badges/tink-macos.png ## Getting started @@ -130,12 +199,11 @@ ## Contact and mailing list -If you want to contribute, please read [CONTRIBUTING](docs/CONTRIBUTING.md) -and send us pull requests. You can also report bugs or file feature requests. +If you want to contribute, please read [CONTRIBUTING](docs/CONTRIBUTING.md) and +send us pull requests. You can also report bugs or file feature requests. If you'd like to talk to the developers or get notified about major product -updates, you may want to subscribe to our -[mailing list](https://groups.google.com/forum/#!forum/tink-users). +updates, you may want to subscribe to our [mailing list][tink_mailing_list_url]. ## Maintainers @@ -167,3 +235,5 @@ - Enzo Puig - Veronika Slívová - Paula Vidas +- Cathie Yun +- Federico Zalcberg
diff --git a/WORKSPACE b/WORKSPACE deleted file mode 100644 index 5a9185b..0000000 --- a/WORKSPACE +++ /dev/null
@@ -1,9 +0,0 @@ -workspace(name = "tink_base") - -load("@tink_base//:tink_base_deps.bzl", "tink_base_deps") - -tink_base_deps() - -load("@tink_base//:tink_base_deps_init.bzl", "tink_base_deps_init") - -tink_base_deps_init()
diff --git a/apps/.bazelversion b/apps/.bazelversion deleted file mode 100644 index ac14c3d..0000000 --- a/apps/.bazelversion +++ /dev/null
@@ -1 +0,0 @@ -5.1.1
diff --git a/apps/BUILD.bazel b/apps/BUILD.bazel deleted file mode 100644 index 1a1b3a3..0000000 --- a/apps/BUILD.bazel +++ /dev/null
@@ -1,3 +0,0 @@ -package(default_visibility = ["//:__subpackages__"]) - -licenses(["notice"])
diff --git a/apps/README.md b/apps/README.md deleted file mode 100644 index 70b329f..0000000 --- a/apps/README.md +++ /dev/null
@@ -1,3 +0,0 @@ -# Tink Java Apps - -It contains extensions and applications of Tink Java.
diff --git a/apps/WORKSPACE b/apps/WORKSPACE deleted file mode 100644 index 27aaa48..0000000 --- a/apps/WORKSPACE +++ /dev/null
@@ -1,22 +0,0 @@ -workspace(name = "tink_apps") - -local_repository( - name = "tink_java", - path = "../java_src", -) - -load("@tink_java//:tink_java_deps.bzl", "tink_java_deps", "TINK_MAVEN_ARTIFACTS") -tink_java_deps() - -load("@tink_java//:tink_java_deps_init.bzl", "tink_java_deps_init") -tink_java_deps_init() - -load("@rules_jvm_external//:defs.bzl", "maven_install") - -maven_install( - artifacts = TINK_MAVEN_ARTIFACTS, - repositories = [ - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -)
diff --git a/apps/paymentmethodtoken/BUILD.bazel b/apps/paymentmethodtoken/BUILD.bazel deleted file mode 100644 index f601945..0000000 --- a/apps/paymentmethodtoken/BUILD.bazel +++ /dev/null
@@ -1,26 +0,0 @@ -load("@tink_java//tools:gen_maven_jar_rules.bzl", "gen_maven_jar_rules") - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -gen_maven_jar_rules( - name = "maven", - doctitle = "Tink Cryptography API for Google Payment Method Token", - manifest_lines = [ - "Automatic-Module-Name: com.google.crypto.tink.apps.paymentmethodtoken", - ], - root_packages = ["com.google.crypto.tink.apps.paymentmethodtoken"], - deps = [ - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:google_payments_public_keys_manager", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_constants", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_hybrid_decrypt", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_hybrid_encrypt", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_recipient", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_recipient_kem", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_recipient_key_gen", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_sender", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_util", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:sender_intermediate_cert_factory", - ], -)
diff --git a/apps/paymentmethodtoken/README.md b/apps/paymentmethodtoken/README.md deleted file mode 100644 index 936026c..0000000 --- a/apps/paymentmethodtoken/README.md +++ /dev/null
@@ -1,52 +0,0 @@ -# An implementation of [Google Payment Method Token](https://developers.google.com/pay/api/payment-data-cryptography). - -## Latest release - -The most recent release is -[1.7.0](https://github.com/google/tink/releases/tag/v1.7.0), released -2022-08-09. API docs can be found -[here](https://google.github.io/tink/javadoc/apps-paymentmethodtoken/1.7.0). - -The Maven group ID is `com.google.crypto.tink`, and the artifact ID is -`apps-paymentmethodtoken`. - -To add a dependency using Maven: - -```xml -<dependency> - <groupId>com.google.crypto.tink</groupId> - <artifactId>apps-paymentmethodtoken</artifactId> - <version>1.7.0</version> -</dependency> -``` - -## Snapshots - -Snapshots of this app built from the master branch are available through Maven -using version `HEAD-SNAPSHOT`. API docs can be found -[here](https://google.github.io/tink/javadoc/apps-paymentmethodtoken/HEAD-SNAPSHOT). - -To add a dependency using Maven: - -```xml -<repositories> -<repository> - <id>sonatype-snapshots</id> - <name>sonatype-snapshots</name> - <url>https://oss.sonatype.org/content/repositories/snapshots/</url> - <snapshots> - <enabled>true</enabled> - <updatePolicy>always</updatePolicy> - </snapshots> - <releases> - <updatePolicy>always</updatePolicy> - </releases> -</repository> -</repositories> - -<dependency> - <groupId>com.google.crypto.tink</groupId> - <artifactId>apps-paymentmethodtoken</artifactId> - <version>HEAD-SNAPSHOT</version> -</dependency> -```
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/BUILD.bazel b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/BUILD.bazel deleted file mode 100644 index 434db56..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/BUILD.bazel +++ /dev/null
@@ -1,125 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -java_library( - name = "google_payments_public_keys_manager", - srcs = ["GooglePaymentsPublicKeysManager.java"], - deps = [ - "@maven//:com_google_http_client_google_http_client", - "@tink_java//src/main/java/com/google/crypto/tink/util:keys_downloader", - ], -) - -java_library( - name = "payment_method_token_hybrid_decrypt", - srcs = ["PaymentMethodTokenHybridDecrypt.java"], - deps = [ - ":payment_method_token_constants", - ":payment_method_token_recipient_kem", - ":payment_method_token_util", - "@maven//:com_google_code_gson_gson", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_decrypt", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:bytes", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:hkdf", - ], -) - -java_library( - name = "payment_method_token_sender", - srcs = ["PaymentMethodTokenSender.java"], - deps = [ - ":payment_method_token_constants", - ":payment_method_token_hybrid_encrypt", - ":payment_method_token_util", - "@maven//:com_google_code_gson_gson", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_encrypt", - "@tink_java//src/main/java/com/google/crypto/tink:public_key_sign", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:ecdsa_sign_jce", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - ], -) - -java_library( - name = "payment_method_token_recipient_key_gen", - srcs = ["PaymentMethodTokenRecipientKeyGen.java"], - deps = [ - ":payment_method_token_constants", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - ], -) - -java_library( - name = "payment_method_token_constants", - srcs = ["PaymentMethodTokenConstants.java"], - deps = [ - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:enums", - ], -) - -java_library( - name = "payment_method_token_recipient_kem", - srcs = ["PaymentMethodTokenRecipientKem.java"], -) - -java_library( - name = "payment_method_token_hybrid_encrypt", - srcs = ["PaymentMethodTokenHybridEncrypt.java"], - deps = [ - ":payment_method_token_constants", - ":payment_method_token_util", - "@maven//:com_google_code_gson_gson", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_encrypt", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:ecies_hkdf_sender_kem", - ], -) - -java_library( - name = "payment_method_token_recipient", - srcs = ["PaymentMethodTokenRecipient.java"], - deps = [ - ":google_payments_public_keys_manager", - ":payment_method_token_constants", - ":payment_method_token_hybrid_decrypt", - ":payment_method_token_recipient_kem", - ":payment_method_token_util", - "@maven//:com_google_code_gson_gson", - "@maven//:joda_time_joda_time", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_decrypt", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:ecdsa_verify_jce", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - ], -) - -java_library( - name = "sender_intermediate_cert_factory", - srcs = ["SenderIntermediateCertFactory.java"], - deps = [ - ":payment_method_token_constants", - ":payment_method_token_util", - "@maven//:com_google_code_gson_gson", - "@tink_java//src/main/java/com/google/crypto/tink:public_key_sign", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:ecdsa_sign_jce", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - ], -) - -java_library( - name = "payment_method_token_util", - srcs = ["PaymentMethodTokenUtil.java"], - deps = [ - ":payment_method_token_constants", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:bytes", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:subtle_util_cluster", - ], -)
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/GooglePaymentsPublicKeysManager.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/GooglePaymentsPublicKeysManager.java deleted file mode 100644 index 0da08d2..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/GooglePaymentsPublicKeysManager.java +++ /dev/null
@@ -1,144 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import com.google.api.client.http.HttpTransport; -import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.crypto.tink.util.KeysDownloader; -import java.io.IOException; -import java.util.Arrays; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -/** - * Thread-safe Google Payments public key manager. - * - * <p>For best performance, use the {@link GooglePaymentsPublicKeysManager#INSTANCE_PRODUCTION} for - * production environment or {@link GooglePaymentsPublicKeysManager#INSTANCE_TEST} for test - * environment. - * - * <p>If you need extra customizations for your use, we recommend you to use {@link - * GooglePaymentsPublicKeysManager.Builder} to construct an instance and keep it as a singleton in a - * static final variable across requests. - * - * <p>When initializing your server, we also recommend that you call {@link #refreshInBackground()} - * to proactively fetch the keys. - * - * @since 1.0.0 - */ -public class GooglePaymentsPublicKeysManager { - /** Default HTTP transport used by this class. */ - public static final NetHttpTransport DEFAULT_HTTP_TRANSPORT = - new NetHttpTransport.Builder().build(); - /** URL to fetch keys for environment production. */ - public static final String KEYS_URL_PRODUCTION = - "https://payments.developers.google.com/paymentmethodtoken/keys.json"; - /** URL to fetch keys for environment test. */ - public static final String KEYS_URL_TEST = - "https://payments.developers.google.com/paymentmethodtoken/test/keys.json"; - - private static final Executor DEFAULT_BACKGROUND_EXECUTOR = Executors.newCachedThreadPool(); - - private final KeysDownloader downloader; - - /** - * Instance configured to talk to fetch keys from production environment (from {@link - * GooglePaymentsPublicKeysManager#KEYS_URL_PRODUCTION}). - */ - public static final GooglePaymentsPublicKeysManager INSTANCE_PRODUCTION = - new GooglePaymentsPublicKeysManager( - DEFAULT_BACKGROUND_EXECUTOR, DEFAULT_HTTP_TRANSPORT, KEYS_URL_PRODUCTION); - /** - * Instance configured to talk to fetch keys from test environment (from {@link - * GooglePaymentsPublicKeysManager#KEYS_URL_TEST}). - */ - public static final GooglePaymentsPublicKeysManager INSTANCE_TEST = - new GooglePaymentsPublicKeysManager( - DEFAULT_BACKGROUND_EXECUTOR, DEFAULT_HTTP_TRANSPORT, KEYS_URL_TEST); - - GooglePaymentsPublicKeysManager( - Executor backgroundExecutor, HttpTransport httpTransport, String keysUrl) { - this.downloader = - new KeysDownloader.Builder() - .setUrl(keysUrl) - .setExecutor(backgroundExecutor) - .setHttpTransport(httpTransport) - .build(); - } - - HttpTransport getHttpTransport() { - return downloader.getHttpTransport(); - } - - String getUrl() { - return downloader.getUrl(); - } - - /** - * Returns a string containing a JSON with the Google public signing keys. - * - * <p>Meant to be called by {@link PaymentMethodTokenRecipient}. - */ - String getTrustedSigningKeysJson() throws IOException { - return this.downloader.download(); - } - - /** Fetches keys in the background. */ - public void refreshInBackground() { - downloader.refreshInBackground(); - } - - /** - * Builder for {@link GooglePaymentsPublicKeysManager}. - * - * @since 1.0.0 - */ - public static class Builder { - private HttpTransport httpTransport = DEFAULT_HTTP_TRANSPORT; - private String keysUrl = KEYS_URL_PRODUCTION; - - public Builder setKeysUrl(String keysUrl) { - this.keysUrl = keysUrl; - return this; - } - - /** - * Sets the HTTP transport. - * - * <p>You generally should not need to set a custom transport as the default transport {@link - * GooglePaymentsPublicKeysManager#DEFAULT_HTTP_TRANSPORT} should be suited for most use cases. - */ - public Builder setHttpTransport(HttpTransport httpTransport) { - this.httpTransport = httpTransport; - return this; - } - - public GooglePaymentsPublicKeysManager build() { - // If all parameters are equal to the existing singleton instances, returning them instead. - // This is more a safe guard if users of this class construct a new class and forget to - // save in a singleton. - for (GooglePaymentsPublicKeysManager instance : - Arrays.asList(INSTANCE_PRODUCTION, INSTANCE_TEST)) { - if (instance.getHttpTransport() == httpTransport && instance.getUrl().equals(keysUrl)) { - return instance; - } - } - return new GooglePaymentsPublicKeysManager( - DEFAULT_BACKGROUND_EXECUTOR, httpTransport, keysUrl); - } - } -}
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenConstants.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenConstants.java deleted file mode 100644 index 416a426..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenConstants.java +++ /dev/null
@@ -1,104 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.Enums.HashType; -import java.nio.charset.StandardCharsets; - -/** Various constants. */ -final class PaymentMethodTokenConstants { - public static final String GOOGLE_SENDER_ID = "Google"; - public static final String HMAC_SHA256_ALGO = "HmacSha256"; - public static final byte[] HKDF_EMPTY_SALT = new byte[0]; - public static final byte[] GOOGLE_CONTEXT_INFO_ECV1 = "Google".getBytes(StandardCharsets.UTF_8); - public static final String AES_CTR_ALGO = "AES/CTR/NoPadding"; - // Zero IV is fine here because each encryption uses a unique key. - public static final byte[] AES_CTR_ZERO_IV = new byte[16]; - public static final EllipticCurves.CurveType P256_CURVE_TYPE = EllipticCurves.CurveType.NIST_P256; - public static final EllipticCurves.PointFormatType UNCOMPRESSED_POINT_FORMAT = - EllipticCurves.PointFormatType.UNCOMPRESSED; - public static final String PROTOCOL_VERSION_EC_V1 = "ECv1"; - public static final String PROTOCOL_VERSION_EC_V2 = "ECv2"; - public static final String PROTOCOL_VERSION_EC_V2_SIGNING_ONLY = "ECv2SigningOnly"; - public static final HashType ECDSA_HASH_SHA256 = HashType.SHA256; - - public static final String JSON_ENCRYPTED_MESSAGE_KEY = "encryptedMessage"; - public static final String JSON_EPHEMERAL_PUBLIC_KEY = "ephemeralPublicKey"; - public static final String JSON_INTERMEDIATE_SIGNING_KEY = "intermediateSigningKey"; - public static final String JSON_KEY_EXPIRATION_KEY = "keyExpiration"; - public static final String JSON_KEY_VALUE_KEY = "keyValue"; - public static final String JSON_MESSAGE_EXPIRATION_KEY = "messageExpiration"; - public static final String JSON_PROTOCOL_VERSION_KEY = "protocolVersion"; - public static final String JSON_SIGNATURES_KEY = "signatures"; - public static final String JSON_SIGNATURE_KEY = "signature"; - public static final String JSON_SIGNED_KEY_KEY = "signedKey"; - public static final String JSON_SIGNED_MESSAGE_KEY = "signedMessage"; - public static final String JSON_TAG_KEY = "tag"; - - /** Represents configuration regarding each protocol version. */ - enum ProtocolVersionConfig { - EC_V1( - /* protocolVersion= */ PROTOCOL_VERSION_EC_V1, - /* aesCtrKeySize= */ 128 / 8, - /* hmacSha256KeySize= */ 128 / 8, - /* isEncryptionRequired= */ true, - /* supportsIntermediateSigningKeys= */ false), - EC_V2( - /* protocolVersion= */ PROTOCOL_VERSION_EC_V2, - /* aesCtrKeySize= */ 256 / 8, - /* hmacSha256KeySize= */ 256 / 8, - /* isEncryptionRequired= */ true, - /* supportsIntermediateSigningKeys= */ true), - EC_V2_SIGNING_ONLY( - /* protocolVersion= */ PROTOCOL_VERSION_EC_V2_SIGNING_ONLY, - /* aesCtrKeySize= */ 256 / 8, - /* hmacSha256KeySize= */ 256 / 8, - /* isEncryptionRequired= */ false, - /* supportsIntermediateSigningKeys= */ true); - - public final String protocolVersion; - public final int aesCtrKeySize; - public final int hmacSha256KeySize; - public final boolean isEncryptionRequired; - public final boolean supportsIntermediateSigningKeys; - - ProtocolVersionConfig( - String protocolVersion, - int aesCtrKeySize, - int hmacSha256KeySize, - boolean isEncryptionRequired, - boolean supportsIntermediateSigningKeys) { - this.protocolVersion = protocolVersion; - this.aesCtrKeySize = aesCtrKeySize; - this.hmacSha256KeySize = hmacSha256KeySize; - this.isEncryptionRequired = isEncryptionRequired; - this.supportsIntermediateSigningKeys = supportsIntermediateSigningKeys; - } - - public static ProtocolVersionConfig forProtocolVersion(String protocolVersion) { - for (ProtocolVersionConfig protocolVersionConfig : ProtocolVersionConfig.values()) { - if (protocolVersionConfig.protocolVersion.equals(protocolVersion)) { - return protocolVersionConfig; - } - } - throw new IllegalArgumentException("Unknown protocol version: " + protocolVersion); - } - } - - private PaymentMethodTokenConstants() {} -}
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridDecrypt.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridDecrypt.java deleted file mode 100644 index 6d2637a..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridDecrypt.java +++ /dev/null
@@ -1,123 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.crypto.tink.HybridDecrypt; -import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.Bytes; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.Hkdf; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import java.security.GeneralSecurityException; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.util.Arrays; - -/** - * A {@link HybridDecrypt} implementation for the hybrid encryption used in <a - * href="https://developers.google.com/pay/api/payment-data-cryptography">Google Payment Method - * Token</a>. - */ -class PaymentMethodTokenHybridDecrypt implements HybridDecrypt { - private final PaymentMethodTokenRecipientKem recipientKem; - private final ProtocolVersionConfig protocolVersionConfig; - - PaymentMethodTokenHybridDecrypt( - final ECPrivateKey recipientPrivateKey, ProtocolVersionConfig protocolVersionConfig) - throws GeneralSecurityException { - this( - new PaymentMethodTokenRecipientKem() { - @Override - public byte[] computeSharedSecret(final byte[] ephemeralPublicKey) - throws GeneralSecurityException { - ECPublicKey publicKey = - EllipticCurves.getEcPublicKey( - recipientPrivateKey.getParams(), - PaymentMethodTokenConstants.UNCOMPRESSED_POINT_FORMAT, - ephemeralPublicKey); - return EllipticCurves.computeSharedSecret(recipientPrivateKey, publicKey); - } - }, - protocolVersionConfig); - } - - PaymentMethodTokenHybridDecrypt( - final PaymentMethodTokenRecipientKem recipientKem, - ProtocolVersionConfig protocolVersionConfig) { - this.recipientKem = recipientKem; - this.protocolVersionConfig = protocolVersionConfig; - } - - @Override - public byte[] decrypt(final byte[] ciphertext, final byte[] contextInfo) - throws GeneralSecurityException { - try { - JsonObject json = JsonParser.parseString(new String(ciphertext, UTF_8)).getAsJsonObject(); - validate(json); - byte[] demKey = kem(json, contextInfo); - return dem(json, demKey); - } catch (JsonParseException | IllegalStateException e) { - throw new GeneralSecurityException("cannot decrypt; failed to parse JSON", e); - } - } - - private byte[] kem(JsonObject json, final byte[] contextInfo) throws GeneralSecurityException { - int demKeySize = protocolVersionConfig.aesCtrKeySize + protocolVersionConfig.hmacSha256KeySize; - byte[] ephemeralPublicKey = - Base64.decode( - json.get(PaymentMethodTokenConstants.JSON_EPHEMERAL_PUBLIC_KEY).getAsString()); - byte[] sharedSecret = recipientKem.computeSharedSecret(ephemeralPublicKey); - return Hkdf.computeEciesHkdfSymmetricKey( - ephemeralPublicKey, - sharedSecret, - PaymentMethodTokenConstants.HMAC_SHA256_ALGO, - PaymentMethodTokenConstants.HKDF_EMPTY_SALT, - contextInfo, - demKeySize); - } - - private byte[] dem(JsonObject json, final byte[] demKey) throws GeneralSecurityException { - byte[] hmacSha256Key = - Arrays.copyOfRange(demKey, protocolVersionConfig.aesCtrKeySize, demKey.length); - byte[] encryptedMessage = - Base64.decode( - json.get(PaymentMethodTokenConstants.JSON_ENCRYPTED_MESSAGE_KEY).getAsString()); - byte[] computedTag = PaymentMethodTokenUtil.hmacSha256(hmacSha256Key, encryptedMessage); - byte[] expectedTag = - Base64.decode(json.get(PaymentMethodTokenConstants.JSON_TAG_KEY).getAsString()); - if (!Bytes.equal(expectedTag, computedTag)) { - throw new GeneralSecurityException("cannot decrypt; invalid MAC"); - } - byte[] aesCtrKey = Arrays.copyOf(demKey, protocolVersionConfig.aesCtrKeySize); - return PaymentMethodTokenUtil.aesCtr(aesCtrKey, encryptedMessage); - } - - private void validate(JsonObject payload) throws GeneralSecurityException { - if (!payload.has(PaymentMethodTokenConstants.JSON_ENCRYPTED_MESSAGE_KEY) - || !payload.has(PaymentMethodTokenConstants.JSON_TAG_KEY) - || !payload.has(PaymentMethodTokenConstants.JSON_EPHEMERAL_PUBLIC_KEY) - || payload.size() != 3) { - throw new GeneralSecurityException( - "The payload must contain exactly encryptedMessage, tag and ephemeralPublicKey"); - } - } -}
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridEncrypt.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridEncrypt.java deleted file mode 100644 index ff3a418..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridEncrypt.java +++ /dev/null
@@ -1,91 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.crypto.tink.HybridEncrypt; -import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EciesHkdfSenderKem; -import com.google.gson.JsonObject; -import com.google.gson.internal.Streams; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.security.GeneralSecurityException; -import java.security.interfaces.ECPublicKey; -import java.util.Arrays; - -/** - * A {@link HybridEncrypt} implementation for the hybrid encryption used in <a - * href="https://developers.google.com/pay/api/payment-data-cryptography">Google Payment Method - * Token</a>. - */ -class PaymentMethodTokenHybridEncrypt implements HybridEncrypt { - private final EciesHkdfSenderKem senderKem; - private final ProtocolVersionConfig protocolVersionConfig; - - public PaymentMethodTokenHybridEncrypt( - final ECPublicKey recipientPublicKey, final ProtocolVersionConfig protocolVersionConfig) { - this.senderKem = new EciesHkdfSenderKem(recipientPublicKey); - this.protocolVersionConfig = protocolVersionConfig; - } - - static String jsonEncodeCiphertext(byte[] ciphertext, byte[] tag, byte[] ephemeralPublicKey) - throws GeneralSecurityException { - JsonObject result = new JsonObject(); - result.addProperty( - PaymentMethodTokenConstants.JSON_ENCRYPTED_MESSAGE_KEY, Base64.encode(ciphertext)); - result.addProperty( - PaymentMethodTokenConstants.JSON_EPHEMERAL_PUBLIC_KEY, Base64.encode(ephemeralPublicKey)); - result.addProperty(PaymentMethodTokenConstants.JSON_TAG_KEY, Base64.encode(tag)); - StringWriter stringWriter = new StringWriter(); - JsonWriter jsonWriter = new JsonWriter(stringWriter); - jsonWriter.setHtmlSafe(true); - try { - Streams.write(result, jsonWriter); - return stringWriter.toString(); - } catch (IOException e) { - throw new GeneralSecurityException("cannot encrypt; JSON error", e); - } - } - - @Override - public byte[] encrypt(final byte[] plaintext, final byte[] contextInfo) - throws GeneralSecurityException { - int symmetricKeySize = - protocolVersionConfig.aesCtrKeySize + protocolVersionConfig.hmacSha256KeySize; - EciesHkdfSenderKem.KemKey kemKey = - senderKem.generateKey( - PaymentMethodTokenConstants.HMAC_SHA256_ALGO, - PaymentMethodTokenConstants.HKDF_EMPTY_SALT, - contextInfo, - symmetricKeySize, - PaymentMethodTokenConstants.UNCOMPRESSED_POINT_FORMAT); - byte[] aesCtrKey = Arrays.copyOf(kemKey.getSymmetricKey(), protocolVersionConfig.aesCtrKeySize); - byte[] ciphertext = PaymentMethodTokenUtil.aesCtr(aesCtrKey, plaintext); - byte[] hmacSha256Key = - Arrays.copyOfRange( - kemKey.getSymmetricKey(), protocolVersionConfig.aesCtrKeySize, symmetricKeySize); - byte[] tag = PaymentMethodTokenUtil.hmacSha256(hmacSha256Key, ciphertext); - byte[] ephemeralPublicKey = kemKey.getKemBytes(); - - String jsonEncodedCiphertext = jsonEncodeCiphertext(ciphertext, tag, ephemeralPublicKey); - return jsonEncodedCiphertext.getBytes(UTF_8); - } -}
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipient.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipient.java deleted file mode 100644 index 5f6cfe7..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipient.java +++ /dev/null
@@ -1,629 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.crypto.tink.HybridDecrypt; -import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EcdsaVerifyJce; -import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import org.joda.time.Instant; - -/** - * An implementation of the recipient side of <a - * href="https://developers.google.com/pay/api/payment-data-cryptography">Google Payment Method - * Token</a>. - * - * <h3>Warning</h3> - * - * <p>This implementation only supports versions {@code ECv1}, {@code ECv2} and {@code - * ECv2SigningOnly}. - * - * <h3>Typical usage</h3> - * - * <pre>{@code - * PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() - * .fetchSenderVerifyingKeysWith( - * GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION) - * .recipientId(recipientId) - * // Multiple recipient private keys can be added to support graceful key rotations - * .addRecipientPrivateKey(recipientPrivateKey1) - * .addRecipientPrivateKey(recipientPrivateKey2) - * .build(); - * String ciphertext = ...; - * String plaintext = recipient.unseal(ciphertext); - * }</pre> - * - * <h3>Custom decryption</h3> - * - * <p>Recipients that store private keys in HSM can provide implementations of {@link - * PaymentMethodTokenRecipientKem} and configure Tink to use their custom code with {@link - * PaymentMethodTokenRecipient.Builder#addRecipientKem}. - * - * <pre>{@code - * PaymentMethodTokenRecipient recipient = new PaymentMethodTokenRecipient.Builder() - * .fetchSenderVerifyingKeysWith( - * GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION) - * .recipientId(recipientId) - * .addRecipientKem(new MyPaymentMethodTokenRecipientKem()) - * .build(); - * String ciphertext = ...; - * String plaintext = recipient.unseal(ciphertext); - * }</pre> - * - * @see <a href="https://developers.google.com/pay/api/payment-data-cryptography">Google Payment - * Method Token standard</a> - * @since 1.0.0 - */ -public final class PaymentMethodTokenRecipient { - private final String protocolVersion; - private final List<SenderVerifyingKeysProvider> senderVerifyingKeysProviders; - private final List<HybridDecrypt> hybridDecrypters = new ArrayList<>(); - private final String senderId; - private final String recipientId; - - PaymentMethodTokenRecipient( - String protocolVersion, - List<SenderVerifyingKeysProvider> senderVerifyingKeysProviders, - String senderId, - List<ECPrivateKey> recipientPrivateKeys, - List<PaymentMethodTokenRecipientKem> recipientKems, - String recipientId) - throws GeneralSecurityException { - if (!protocolVersion.equals(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1) - && !protocolVersion.equals(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - && !protocolVersion.equals( - PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY)) { - throw new IllegalArgumentException("invalid version: " + protocolVersion); - } - this.protocolVersion = protocolVersion; - if (senderVerifyingKeysProviders == null || senderVerifyingKeysProviders.isEmpty()) { - throw new IllegalArgumentException( - "must set at least one way to get sender's verifying key using" - + " Builder.fetchSenderVerifyingKeysWith or Builder.senderVerifyingKeys"); - } - this.senderVerifyingKeysProviders = senderVerifyingKeysProviders; - this.senderId = senderId; - - ProtocolVersionConfig protocolVersionConfig = - ProtocolVersionConfig.forProtocolVersion(protocolVersion); - if (protocolVersionConfig.isEncryptionRequired) { - if (recipientPrivateKeys.isEmpty() && recipientKems.isEmpty()) { - throw new IllegalArgumentException( - "must add at least one recipient's decrypting key using Builder.addRecipientPrivateKey " - + "or Builder.addRecipientKem"); - } - for (ECPrivateKey privateKey : recipientPrivateKeys) { - hybridDecrypters.add( - new PaymentMethodTokenHybridDecrypt(privateKey, protocolVersionConfig)); - } - for (PaymentMethodTokenRecipientKem kem : recipientKems) { - hybridDecrypters.add(new PaymentMethodTokenHybridDecrypt(kem, protocolVersionConfig)); - } - } else { - if (!recipientPrivateKeys.isEmpty() || !recipientKems.isEmpty()) { - throw new IllegalArgumentException( - "must not set private decrypting key using Builder.addRecipientPrivateKey " - + "or Builder.addRecipientDecrypter"); - } - } - - if (recipientId == null) { - throw new IllegalArgumentException("must set recipient Id using Builder.recipientId"); - } - this.recipientId = recipientId; - } - - private PaymentMethodTokenRecipient(Builder builder) throws GeneralSecurityException { - this( - builder.protocolVersion, - builder.senderVerifyingKeysProviders, - builder.senderId, - builder.recipientPrivateKeys, - builder.recipientKems, - builder.recipientId); - } - - /** - * Builder for {@link PaymentMethodTokenRecipient}. - * - * @since 1.0.0 - */ - public static class Builder { - private String protocolVersion = PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1; - private String senderId = PaymentMethodTokenConstants.GOOGLE_SENDER_ID; - private String recipientId = null; - private final List<SenderVerifyingKeysProvider> senderVerifyingKeysProviders = - new ArrayList<SenderVerifyingKeysProvider>(); - private final List<ECPrivateKey> recipientPrivateKeys = new ArrayList<ECPrivateKey>(); - private final List<PaymentMethodTokenRecipientKem> recipientKems = new ArrayList<>(); - - public Builder() {} - - /** Sets the protocolVersion. */ - public Builder protocolVersion(String val) { - protocolVersion = val; - return this; - } - - /** Sets the sender Id. */ - public Builder senderId(String val) { - senderId = val; - return this; - } - - /** Sets the recipient Id. */ - public Builder recipientId(String val) { - recipientId = val; - return this; - } - - /** - * Fetches verifying public keys of the sender using {@link GooglePaymentsPublicKeysManager}. - * - * <p>This is the preferred method of specifying the verifying public keys of the sender. - */ - public Builder fetchSenderVerifyingKeysWith( - final GooglePaymentsPublicKeysManager googlePaymentsPublicKeysManager) - throws GeneralSecurityException { - this.senderVerifyingKeysProviders.add( - new SenderVerifyingKeysProvider() { - @Override - public List<ECPublicKey> get(String protocolVersion) throws GeneralSecurityException { - try { - return parseTrustedSigningKeysJson( - protocolVersion, googlePaymentsPublicKeysManager.getTrustedSigningKeysJson()); - } catch (IOException e) { - throw new GeneralSecurityException("Failed to fetch keys!", e); - } - } - }); - return this; - } - - /** - * Sets the trusted verifying public keys of the sender. - * - * <p><b>IMPORTANT</b>: Instead of using this method to set the verifying public keys of the - * sender, prefer calling {@link #fetchSenderVerifyingKeysWith} passing it an instance of {@link - * GooglePaymentsPublicKeysManager}. It will take care of fetching fresh keys and caching in - * memory. Only use this method if you can't use {@link #fetchSenderVerifyingKeysWith} and be - * aware you will need to handle Google key rotations yourself. - * - * <p>The given string is a JSON object formatted like the following: - * - * <pre> - * { - * "keys": [ - * { - * "keyValue": "encoded public key", - * "protocolVersion": "ECv1" - * }, - * { - * "keyValue": "encoded public key", - * "protocolVersion": "ECv1" - * }, - * ], - * } - * </pre> - * - * <p>Each public key will be a base64 (no wrapping, padded) version of the key encoded in ASN.1 - * type SubjectPublicKeyInfo defined in the X.509 standard. - */ - public Builder senderVerifyingKeys(final String trustedSigningKeysJson) - throws GeneralSecurityException { - this.senderVerifyingKeysProviders.add( - new SenderVerifyingKeysProvider() { - @Override - public List<ECPublicKey> get(String protocolVersion) throws GeneralSecurityException { - return parseTrustedSigningKeysJson(protocolVersion, trustedSigningKeysJson); - } - }); - return this; - } - - /** - * Adds a verifying public key of the sender. - * - * <p><b>IMPORTANT</b>: Instead of using this method to set the verifying public keys of the - * sender, prefer calling {@link #fetchSenderVerifyingKeysWith} passing it an instance of {@link - * GooglePaymentsPublicKeysManager}. It will take care of fetching fresh keys and caching in - * memory. Only use this method if you can't use {@link #fetchSenderVerifyingKeysWith} and be - * aware you will need to handle Google key rotations yourself. - * - * <p>The public key is a base64 (no wrapping, padded) version of the key encoded in ASN.1 type - * SubjectPublicKeyInfo defined in the X.509 standard. - * - * <p>Multiple keys may be added. This utility will then verify any message signed with any of - * the private keys corresponding to the public keys added. Adding multiple keys is useful for - * handling key rotation. - */ - public Builder addSenderVerifyingKey(final String val) throws GeneralSecurityException { - this.senderVerifyingKeysProviders.add( - new SenderVerifyingKeysProvider() { - @Override - public List<ECPublicKey> get(String protocolVersion) throws GeneralSecurityException { - return Collections.singletonList(PaymentMethodTokenUtil.x509EcPublicKey(val)); - } - }); - return this; - } - - /** - * Adds a verifying public key of the sender. - * - * <p><b>IMPORTANT</b>: Instead of using this method to set the verifying public keys of the - * sender, prefer calling {@link #fetchSenderVerifyingKeysWith} passing it an instance of {@link - * GooglePaymentsPublicKeysManager}. It will take care of fetching fresh keys and caching in - * memory. Only use this method if you can't use {@link #fetchSenderVerifyingKeysWith} and be - * aware you will need to handle Google key rotations yourself. - */ - public Builder addSenderVerifyingKey(final ECPublicKey val) throws GeneralSecurityException { - this.senderVerifyingKeysProviders.add( - new SenderVerifyingKeysProvider() { - @Override - public List<ECPublicKey> get(String protocolVersion) throws GeneralSecurityException { - return Collections.singletonList(val); - } - }); - return this; - } - - /** - * Adds the decryption private key of the recipient. - * - * <p>It must be base64 encoded PKCS8 private key. - */ - public Builder addRecipientPrivateKey(String val) throws GeneralSecurityException { - return addRecipientPrivateKey(PaymentMethodTokenUtil.pkcs8EcPrivateKey(val)); - } - - /** Adds the decryption private key of the recipient. */ - public Builder addRecipientPrivateKey(ECPrivateKey val) throws GeneralSecurityException { - recipientPrivateKeys.add(val); - return this; - } - - /** - * Adds a custom {@link PaymentMethodTokenRecipientKem}. - * - * <p>This is useful for clients that store keys in an HSM and need a more control on how the - * key is used. If you are not using an HSM, you probably should just use {@link - * #addRecipientPrivateKey}. - * - * @since 1.1.0 - */ - public Builder addRecipientKem(PaymentMethodTokenRecipientKem kem) { - recipientKems.add(kem); - return this; - } - - public PaymentMethodTokenRecipient build() throws GeneralSecurityException { - return new PaymentMethodTokenRecipient(this); - } - } - - /** - * Unseal the given {@code sealedMessage} by performing the necessary signature verification and - * decryption (if required) steps based on the protocolVersion. - */ - public String unseal(final String sealedMessage) throws GeneralSecurityException { - try { - if (protocolVersion.equals(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1)) { - return unsealECV1(sealedMessage); - } else if (protocolVersion.equals(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2)) { - return unsealECV2(sealedMessage); - } else if (protocolVersion.equals( - PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY)) { - return unsealECV2SigningOnly(sealedMessage); - } - throw new IllegalArgumentException("unsupported version: " + protocolVersion); - } catch (JsonParseException | IllegalStateException e) { - throw new GeneralSecurityException("cannot unseal; invalid JSON message", e); - } - } - - private String unsealECV1(String sealedMessage) throws GeneralSecurityException { - JsonObject jsonMsg = JsonParser.parseString(sealedMessage).getAsJsonObject(); - validateECV1(jsonMsg); - String signedMessage = verifyECV1(jsonMsg); - String decryptedMessage = decrypt(signedMessage); - validateMessage(decryptedMessage); - return decryptedMessage; - } - - private String unsealECV2(String sealedMessage) throws GeneralSecurityException { - JsonObject jsonMsg = JsonParser.parseString(sealedMessage).getAsJsonObject(); - validateECV2(jsonMsg); - String signedMessage = verifyECV2(jsonMsg); - String decryptedMessage = decrypt(signedMessage); - validateMessage(decryptedMessage); - return decryptedMessage; - } - - private String unsealECV2SigningOnly(String sealedMessage) throws GeneralSecurityException { - JsonObject jsonMsg = JsonParser.parseString(sealedMessage).getAsJsonObject(); - validateECV2(jsonMsg); - String message = verifyECV2(jsonMsg); - validateMessage(message); - return message; - } - - private String verifyECV1(final JsonObject jsonMsg) throws GeneralSecurityException { - byte[] signature = - Base64.decode(jsonMsg.get(PaymentMethodTokenConstants.JSON_SIGNATURE_KEY).getAsString()); - String signedMessage = - jsonMsg.get(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY).getAsString(); - byte[] signedBytes = getSignedBytes(protocolVersion, signedMessage); - verify( - protocolVersion, - senderVerifyingKeysProviders, - Collections.singletonList(signature), - signedBytes); - return signedMessage; - } - - private String verifyECV2(final JsonObject jsonMsg) throws GeneralSecurityException { - byte[] signature = - Base64.decode(jsonMsg.get(PaymentMethodTokenConstants.JSON_SIGNATURE_KEY).getAsString()); - String signedMessage = - jsonMsg.get(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY).getAsString(); - byte[] signedBytes = getSignedBytes(protocolVersion, signedMessage); - verify( - protocolVersion, - verifyIntermediateSigningKey(jsonMsg), - Collections.singletonList(signature), - signedBytes); - return signedMessage; - } - - private byte[] getSignedBytes(String protocolVersion, String signedMessage) - throws GeneralSecurityException { - return PaymentMethodTokenUtil.toLengthValue( - // The order of the parameters matters. - senderId, recipientId, protocolVersion, signedMessage); - } - - private void validateMessage(String decryptedMessage) throws GeneralSecurityException { - JsonObject decodedMessage; - - try { - decodedMessage = JsonParser.parseString(decryptedMessage).getAsJsonObject(); - } catch (JsonParseException | IllegalStateException e) { - // Message wasn't a valid JSON, so nothing to validate. - return; - } - - // If message expiration is present, checking it. - if (decodedMessage.has(PaymentMethodTokenConstants.JSON_MESSAGE_EXPIRATION_KEY)) { - long expirationInMillis = - Long.parseLong( - decodedMessage - .get(PaymentMethodTokenConstants.JSON_MESSAGE_EXPIRATION_KEY) - .getAsString()); - if (expirationInMillis <= Instant.now().getMillis()) { - throw new GeneralSecurityException("expired payload"); - } - } - } - - private static void verify( - final String protocolVersion, - final List<SenderVerifyingKeysProvider> senderVerifyingKeysProviders, - final List<byte[]> signatures, - final byte[] signedBytes) - throws GeneralSecurityException { - boolean verified = false; - for (SenderVerifyingKeysProvider verifyingKeysProvider : senderVerifyingKeysProviders) { - for (ECPublicKey publicKey : verifyingKeysProvider.get(protocolVersion)) { - EcdsaVerifyJce verifier = - new EcdsaVerifyJce( - publicKey, PaymentMethodTokenConstants.ECDSA_HASH_SHA256, EcdsaEncoding.DER); - for (byte[] signature : signatures) { - try { - verifier.verify(signature, signedBytes); - // No exception means the signature is valid. - verified = true; - } catch (GeneralSecurityException e) { - // ignored, try again - } - } - } - } - if (!verified) { - throw new GeneralSecurityException("cannot verify signature"); - } - } - - private String decrypt(String ciphertext) throws GeneralSecurityException { - for (HybridDecrypt hybridDecrypter : hybridDecrypters) { - try { - byte[] cleartext = - hybridDecrypter.decrypt( - ciphertext.getBytes(UTF_8), PaymentMethodTokenConstants.GOOGLE_CONTEXT_INFO_ECV1); - return new String(cleartext, UTF_8); - } catch (GeneralSecurityException e) { - // ignored, try again - } - } - throw new GeneralSecurityException("cannot decrypt"); - } - - private void validateECV1(final JsonObject jsonMsg) throws GeneralSecurityException { - if (!jsonMsg.has(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY) - || !jsonMsg.has(PaymentMethodTokenConstants.JSON_SIGNATURE_KEY) - || !jsonMsg.has(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY) - || jsonMsg.size() != 3) { - throw new GeneralSecurityException( - "ECv1 message must contain exactly protocolVersion, signature and signedMessage"); - } - String version = - jsonMsg.get(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY).getAsString(); - if (!version.equals(protocolVersion)) { - throw new GeneralSecurityException("invalid version: " + version); - } - } - - private void validateECV2(final JsonObject jsonMsg) throws GeneralSecurityException { - if (!jsonMsg.has(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY) - || !jsonMsg.has(PaymentMethodTokenConstants.JSON_SIGNATURE_KEY) - || !jsonMsg.has(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY) - || !jsonMsg.has(PaymentMethodTokenConstants.JSON_INTERMEDIATE_SIGNING_KEY) - || jsonMsg.size() != 4) { - throw new GeneralSecurityException( - protocolVersion - + " message must contain exactly protocolVersion, intermediateSigningKey, " - + "signature and signedMessage"); - } - String version = - jsonMsg.get(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY).getAsString(); - if (!version.equals(protocolVersion)) { - throw new GeneralSecurityException("invalid version: " + version); - } - } - - /** - * Verifies the intermediate key and returns a singleton list containing of {@code - * SenderVerifyingKeysProvider} that provides the verified key. - */ - private List<SenderVerifyingKeysProvider> verifyIntermediateSigningKey(JsonObject jsonMsg) - throws GeneralSecurityException { - JsonObject intermediateSigningKey = - jsonMsg.get(PaymentMethodTokenConstants.JSON_INTERMEDIATE_SIGNING_KEY).getAsJsonObject(); - validateIntermediateSigningKey(intermediateSigningKey); - ArrayList<byte[]> signatures = new ArrayList<>(); - JsonArray signaturesJson = - intermediateSigningKey - .get(PaymentMethodTokenConstants.JSON_SIGNATURES_KEY) - .getAsJsonArray(); - for (int i = 0; i < signaturesJson.size(); i++) { - signatures.add(Base64.decode(signaturesJson.get(i).getAsString())); - } - String signedKeyAsString = - intermediateSigningKey.get(PaymentMethodTokenConstants.JSON_SIGNED_KEY_KEY).getAsString(); - byte[] signedBytes = - PaymentMethodTokenUtil.toLengthValue( - // The order of the parameters matters. - senderId, protocolVersion, signedKeyAsString); - verify(protocolVersion, senderVerifyingKeysProviders, signatures, signedBytes); - JsonObject signedKey = JsonParser.parseString(signedKeyAsString).getAsJsonObject(); - validateSignedKey(signedKey); - final String key = signedKey.get(PaymentMethodTokenConstants.JSON_KEY_VALUE_KEY).getAsString(); - SenderVerifyingKeysProvider provider = - new SenderVerifyingKeysProvider() { - @Override - public List<ECPublicKey> get(String protocolVersion) throws GeneralSecurityException { - if (PaymentMethodTokenRecipient.this.protocolVersion.equals(protocolVersion)) { - return Collections.singletonList(PaymentMethodTokenUtil.x509EcPublicKey(key)); - } else { - return Collections.emptyList(); - } - } - }; - return Collections.singletonList(provider); - } - - private JsonObject validateIntermediateSigningKey(final JsonObject intermediateSigningKey) - throws GeneralSecurityException { - if (!intermediateSigningKey.has(PaymentMethodTokenConstants.JSON_SIGNATURES_KEY) - || !intermediateSigningKey.has(PaymentMethodTokenConstants.JSON_SIGNED_KEY_KEY) - || intermediateSigningKey.size() != 2) { - throw new GeneralSecurityException( - "intermediateSigningKey must contain exactly signedKey and signatures"); - } - return intermediateSigningKey; - } - - private void validateSignedKey(final JsonObject signedKey) throws GeneralSecurityException { - // Note: allowing further keys to be added so we can extend the protocol if needed in the future - if (!signedKey.has(PaymentMethodTokenConstants.JSON_KEY_VALUE_KEY) - || !signedKey.has(PaymentMethodTokenConstants.JSON_KEY_EXPIRATION_KEY)) { - throw new GeneralSecurityException( - "intermediateSigningKey.signedKey must contain keyValue and keyExpiration"); - } - - // If message expiration is present, checking it. - long expirationInMillis = - Long.parseLong( - signedKey.get(PaymentMethodTokenConstants.JSON_KEY_EXPIRATION_KEY).getAsString()); - if (expirationInMillis <= Instant.now().getMillis()) { - throw new GeneralSecurityException("expired intermediateSigningKey"); - } - } - - private static List<ECPublicKey> parseTrustedSigningKeysJson( - String protocolVersion, String trustedSigningKeysJson) throws GeneralSecurityException { - List<ECPublicKey> senderVerifyingKeys = new ArrayList<>(); - try { - JsonArray keys = - JsonParser.parseString(trustedSigningKeysJson) - .getAsJsonObject() - .get("keys") - .getAsJsonArray(); - for (int i = 0; i < keys.size(); i++) { - JsonObject key = keys.get(i).getAsJsonObject(); - if (protocolVersion.equals( - key.get(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY).getAsString())) { - - if (key.has(PaymentMethodTokenConstants.JSON_KEY_EXPIRATION_KEY)) { - // If message expiration is present, checking it. - long expirationInMillis = - Long.parseLong( - key.get(PaymentMethodTokenConstants.JSON_KEY_EXPIRATION_KEY).getAsString()); - if (expirationInMillis <= Instant.now().getMillis()) { - // Ignore expired keys - continue; - } - } else if (!protocolVersion.equals(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1)) { - // keyExpiration is required in all versions except ECv1, so if it is missing we should - // skip using this key. - // In ECv1 the expiration is optional because it is assumed that the caller is - // respecting the HTTP cache headers and not using the trustedSigningKeysJson that are - // expired according to the headers. - continue; - } - - senderVerifyingKeys.add( - PaymentMethodTokenUtil.x509EcPublicKey(key.get("keyValue").getAsString())); - } - } - } catch (JsonParseException | IllegalStateException e) { - throw new GeneralSecurityException("failed to extract trusted signing public keys", e); - } - if (senderVerifyingKeys.isEmpty()) { - throw new GeneralSecurityException("no trusted keys are available for this protocol version"); - } - return senderVerifyingKeys; - } - - private interface SenderVerifyingKeysProvider { - List<ECPublicKey> get(String protocolVersion) throws GeneralSecurityException; - } -}
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipientKem.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipientKem.java deleted file mode 100644 index 47d322b..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipientKem.java +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2018 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import java.security.GeneralSecurityException; - -/** - * Interface for recipient's key encapsulation mechanism (KEM). - * - * <p>Google Pay's tokens are encrypted using ECIES which is a hybrid encryption mode consisting of - * two steps: key encapsulation mechanisam (KEM) using Elliptic Curve Diffie Hellman (ECDH) and HKDF - * and data encapsulation mechanism (DEM) using AES-CTR-HMAC. - * - * <p>During encryption, the KEM step takes the recipient's public key and produces a DEM key and an - * ephemeral public key. The DEM key is then used to encrypt the credit card data, and the ephemeral - * public key is sent as the <b>ephemeralPublicKey</b> field of the payload. - * - * <p>To decrypt, the recipient must use their private key to compute an ECDH shared secret from the - * ephemeral public key, and from that derive the DEM key using HKDF. If the recipient keeps the - * private key in a HSM, they cannot load the private key in Tink, but they can implement this - * interface and configure Tink to use their custom KEM implementation with {@link - * PaymentMethodTokenRecipient.Builder#addRecipientKem}. - * - * @see <a href="https://developers.google.com/pay/api/payment-data-cryptography">Google Payment - * Method Token standard</a> - * @since 1.1.0 - */ -public interface PaymentMethodTokenRecipientKem { - /** - * Computes a shared secret from the {@code ephemeralPublicKey}, using ECDH. - * - * <p>{@code ephemeralPublicKey} is a point on the elliptic curve defined in the <a - * href="https://developers.google.com/pay/api/payment-data-cryptography">Google Payment Method - * Token standard</a>, encoded in uncompressed point format. In version ECv1 and ECv2 of the - * standard, the elliptic curve is NIST P-256. - * - * <p>Note that you only needs to compute the shared secret, but you don't have to derive the DEM - * key with HKDF -- that process is handled by Tink. - */ - byte[] computeSharedSecret(final byte[] ephemeralPublicKey) throws GeneralSecurityException; -}
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipientKeyGen.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipientKeyGen.java deleted file mode 100644 index 2713854..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipientKeyGen.java +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EllipticCurves; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.interfaces.ECPublicKey; - -/** - * A util that generates key pairs for the recipient side of <a - * href="https://developers.google.com/android-pay/integration/payment-token-cryptography">Google - * Payment Method Token</a>. - * - * <h3>Usage</h3> - * - * <pre> - * bazel build apps/paymentmethodtoken/... - * ./bazel-bin/apps/paymentmethodtoken/recipientkeygen - * </pre> - * - * <p>Running that command will generate a fresh key pair. The private/public key can be found in - * private_key.bin/public_key.bin. The content of private_key.bin can be passed to {@link - * PaymentMethodTokenRecipient.Builder#addRecipientPrivateKey} and the content of public_key.bin can - * be passed to {@link PaymentMethodTokenSender.Builder#rawUncompressedRecipientPublicKey}. - */ -public final class PaymentMethodTokenRecipientKeyGen { - private static final String PRIVATE_KEY_FILE = "private_key.bin"; - - private static final String PUBLIC_KEY_FILE = "public_key.bin"; - - private static void generateKey() throws GeneralSecurityException, IOException { - KeyPair keyPair = EllipticCurves.generateKeyPair(PaymentMethodTokenConstants.P256_CURVE_TYPE); - writeBase64(PRIVATE_KEY_FILE, keyPair.getPrivate().getEncoded()); - - ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic(); - writeBase64( - PUBLIC_KEY_FILE, - EllipticCurves.pointEncode( - PaymentMethodTokenConstants.P256_CURVE_TYPE, - PaymentMethodTokenConstants.UNCOMPRESSED_POINT_FORMAT, - publicKey.getW())); - } - - private static void writeBase64(String pathname, byte[] content) throws IOException { - File out = new File(pathname); - if (out.exists()) { - System.out.println("Please make sure that " + pathname + " does not exist."); - System.exit(-1); - } - FileOutputStream stream = new FileOutputStream(out); - stream.write(Base64.encode(content, Base64.DEFAULT | Base64.NO_WRAP)); - stream.close(); - } - - public static void main(String[] args) throws GeneralSecurityException, IOException { - System.out.println("Generating key...."); - generateKey(); - System.out.println("done."); - } - - private PaymentMethodTokenRecipientKeyGen() {} -}
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenSender.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenSender.java deleted file mode 100644 index 25d0fe2..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenSender.java +++ /dev/null
@@ -1,335 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.crypto.tink.HybridEncrypt; -import com.google.crypto.tink.PublicKeySign; -import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EcdsaSignJce; -import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.google.gson.internal.Streams; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.security.GeneralSecurityException; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; - -/** - * An implementation of the sender side of <a - * href="https://developers.google.com/pay/api/payment-data-cryptography">Google Payment Method - * Token</a>. - * - * <h3>Warning</h3> - * - * <p>This implementation supports only versions {@code ECv1} and {@code ECv2}. - * - * <h3>Usage</h3> - * - * <pre>{@code - * PaymentMethodTokenSender sender = new PaymentMethodTokenSender.Builder() - * .senderId(senderId) - * .senderSigningKey(senderPrivateKey) - * .recipientId(recipientId) - * .recipientPublicKey(recipientPublicKey) - * .build(); - * String plaintext = "blah"; - * String ciphertext = sender.seal(plaintext); - * }</pre> - * - * @see <a href="https://developers.google.com/pay/api/payment-data-cryptography">Google Payment - * Method Token standard</a> - * @since 1.0.0 - */ -public final class PaymentMethodTokenSender { - private final String protocolVersion; - private final ProtocolVersionConfig protocolVersionConfig; - private final PublicKeySign signer; - private final String senderIntermediateCert; - private final String senderId; - private final String recipientId; - - private HybridEncrypt hybridEncrypter; - - PaymentMethodTokenSender(Builder builder) throws GeneralSecurityException { - switch (builder.protocolVersion) { - case PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1: - validateV1(builder); - break; - case PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2: - validateV2(builder); - break; - case PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY: - validateV2SigningOnly(builder); - break; - default: - throw new IllegalArgumentException("invalid version: " + builder.protocolVersion); - } - - this.protocolVersion = builder.protocolVersion; - this.protocolVersionConfig = ProtocolVersionConfig.forProtocolVersion(protocolVersion); - this.signer = - new EcdsaSignJce( - builder.senderIntermediateSigningKey != null - ? builder.senderIntermediateSigningKey - : builder.senderSigningKey, - PaymentMethodTokenConstants.ECDSA_HASH_SHA256, - EcdsaEncoding.DER); - this.senderId = builder.senderId; - if (protocolVersionConfig.isEncryptionRequired) { - this.hybridEncrypter = - new PaymentMethodTokenHybridEncrypt(builder.recipientPublicKey, protocolVersionConfig); - } - if (builder.recipientId == null) { - throw new IllegalArgumentException("must set recipient Id using Builder.recipientId"); - } - this.recipientId = builder.recipientId; - this.senderIntermediateCert = builder.senderIntermediateCert; - } - - /** - * Builder for {@link PaymentMethodTokenSender}. - * - * @since 1.0.0 - */ - public static class Builder { - private String protocolVersion = PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1; - private String senderId = PaymentMethodTokenConstants.GOOGLE_SENDER_ID; - private String recipientId = null; - private ECPrivateKey senderSigningKey = null; - private ECPrivateKey senderIntermediateSigningKey = null; - private String senderIntermediateCert = null; - private ECPublicKey recipientPublicKey = null; - - public Builder() {} - - /** Sets the protocolVersion. */ - public Builder protocolVersion(String val) { - protocolVersion = val; - return this; - } - - /** Sets the sender Id. */ - public Builder senderId(String val) { - senderId = val; - return this; - } - - /** Sets the recipient Id. */ - public Builder recipientId(String val) { - recipientId = val; - return this; - } - - /** - * Sets the signing key of the sender. - * - * <p>It must be base64 encoded PKCS8 private key. - */ - public Builder senderSigningKey(String val) throws GeneralSecurityException { - senderSigningKey = PaymentMethodTokenUtil.pkcs8EcPrivateKey(val); - return this; - } - - public Builder senderSigningKey(ECPrivateKey val) throws GeneralSecurityException { - senderSigningKey = val; - return this; - } - - /** - * Sets the intermediate signing key of the sender. - * - * <p>It must be base64 encoded PKCS8 private key. - * - * @since 1.1.0 - */ - public Builder senderIntermediateSigningKey(String val) throws GeneralSecurityException { - return senderIntermediateSigningKey(PaymentMethodTokenUtil.pkcs8EcPrivateKey(val)); - } - - /** - * Sets the intermediate signing key of the sender. - * - * @since 1.1.0 - */ - public Builder senderIntermediateSigningKey(ECPrivateKey val) throws GeneralSecurityException { - senderIntermediateSigningKey = val; - return this; - } - - /** - * JSON containing sender intermediate signing key and a signature of it by the sender signing - * key. - * - * <p>This can be generated by {@link SenderIntermediateCertFactory}. - * - * @since 1.1.0 - */ - public Builder senderIntermediateCert(String val) throws GeneralSecurityException { - this.senderIntermediateCert = val; - return this; - } - - /** - * Sets the encryption public key of the recipient. - * - * <p>The public key is a base64 (no wrapping, padded) version of the key encoded in ASN.1 type - * SubjectPublicKeyInfo defined in the X.509 standard. - */ - public Builder recipientPublicKey(String val) throws GeneralSecurityException { - recipientPublicKey = PaymentMethodTokenUtil.x509EcPublicKey(val); - return this; - } - - public Builder recipientPublicKey(ECPublicKey val) throws GeneralSecurityException { - recipientPublicKey = val; - return this; - } - - /** - * Sets the encryption public key of the recipient. - * - * <p>The public key must be formatted as base64 encoded uncompressed point format. This format - * is described in more detail in "Public Key Cryptography For The Financial Services Industry: - * The Elliptic Curve Digital Signature Algorithm (ECDSA)", ANSI X9.62, 1998 - */ - public Builder rawUncompressedRecipientPublicKey(String val) throws GeneralSecurityException { - recipientPublicKey = PaymentMethodTokenUtil.rawUncompressedEcPublicKey(val); - return this; - } - - public PaymentMethodTokenSender build() throws GeneralSecurityException { - return new PaymentMethodTokenSender(this); - } - } - - /** Seals the input message according to the Payment Method Token specification. */ - public String seal(final String message) throws GeneralSecurityException { - if (protocolVersion.equals(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1) - || protocolVersion.equals(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - || protocolVersion.equals( - PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY)) { - return sealV1OrV2(message); - } - throw new GeneralSecurityException("Unsupported version: " + protocolVersion); - } - - private String sealV1OrV2(final String message) throws GeneralSecurityException { - String signedMessage = - protocolVersionConfig.isEncryptionRequired - ? new String( - hybridEncrypter.encrypt( - message.getBytes(UTF_8), PaymentMethodTokenConstants.GOOGLE_CONTEXT_INFO_ECV1), - UTF_8) - : message; - return signV1OrV2(signedMessage); - } - - static String jsonEncodeSignedMessage( - String message, String protocolVersion, byte[] signature, String senderIntermediateCert) - throws GeneralSecurityException { - try { - JsonObject result = new JsonObject(); - result.addProperty(PaymentMethodTokenConstants.JSON_SIGNATURE_KEY, Base64.encode(signature)); - if (senderIntermediateCert != null) { - result.add( - PaymentMethodTokenConstants.JSON_INTERMEDIATE_SIGNING_KEY, - JsonParser.parseString(senderIntermediateCert).getAsJsonObject()); - } - result.addProperty(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, protocolVersion); - result.addProperty(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY, message); - StringWriter stringWriter = new StringWriter(); - JsonWriter jsonWriter = new JsonWriter(stringWriter); - jsonWriter.setHtmlSafe(true); - Streams.write(result, jsonWriter); - return stringWriter.toString(); - } catch (JsonParseException | IllegalStateException | IOException e) { - throw new GeneralSecurityException("cannot seal; JSON error", e); - } - } - - private String signV1OrV2(String message) throws GeneralSecurityException { - byte[] toSignBytes = - PaymentMethodTokenUtil.toLengthValue( - // The order of the parameters matters. - senderId, recipientId, protocolVersion, message); - byte[] signature = signer.sign(toSignBytes); - return jsonEncodeSignedMessage(message, protocolVersion, signature, senderIntermediateCert); - } - - private static void validateV1(Builder builder) { - // ECv1 signed payloads directly. - if (builder.senderSigningKey == null) { - throw new IllegalArgumentException( - "must set sender's signing key using Builder.senderSigningKey"); - } - if (builder.senderIntermediateSigningKey != null) { - throw new IllegalArgumentException( - "must not set sender's intermediate signing key using " - + "Builder.senderIntermediateSigningKey"); - } - if (builder.senderIntermediateCert != null) { - throw new IllegalArgumentException( - "must not set signed sender's intermediate signing key using " - + "Builder.senderIntermediateCert"); - } - if (builder.recipientPublicKey == null) { - throw new IllegalArgumentException( - "must set recipient's public key using Builder.recipientPublicKey"); - } - } - - private static void validateV2(Builder builder) { - validateIntermediateSigningKeys(builder); - if (builder.recipientPublicKey == null) { - throw new IllegalArgumentException( - "must set recipient's public key using Builder.recipientPublicKey"); - } - } - - private static void validateV2SigningOnly(Builder builder) { - validateIntermediateSigningKeys(builder); - if (builder.recipientPublicKey != null) { - throw new IllegalArgumentException( - "must not set recipient's public key using Builder.recipientPublicKey"); - } - } - - private static void validateIntermediateSigningKeys(Builder builder) { - // ECv2 and newer protocols use an intermediate signing key. - if (builder.senderSigningKey != null) { - throw new IllegalArgumentException( - "must not set sender's signing key using Builder.senderSigningKey"); - } - if (builder.senderIntermediateSigningKey == null) { - throw new IllegalArgumentException( - "must set sender's intermediate signing key using " - + "Builder.senderIntermediateSigningKey"); - } - if (builder.senderIntermediateCert == null) { - throw new IllegalArgumentException( - "must set signed sender's intermediate signing key using " - + "Builder.senderIntermediateCert"); - } - } -}
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenUtil.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenUtil.java deleted file mode 100644 index 185038d..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenUtil.java +++ /dev/null
@@ -1,79 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.Bytes; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.EngineFactory; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import javax.crypto.Cipher; -import javax.crypto.Mac; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** Various helpers. */ -final class PaymentMethodTokenUtil { - static ECPublicKey rawUncompressedEcPublicKey(String rawUncompressedPublicKey) - throws GeneralSecurityException { - return EllipticCurves.getEcPublicKey( - PaymentMethodTokenConstants.P256_CURVE_TYPE, - PaymentMethodTokenConstants.UNCOMPRESSED_POINT_FORMAT, - Base64.decode(rawUncompressedPublicKey)); - } - - static ECPublicKey x509EcPublicKey(String x509PublicKey) throws GeneralSecurityException { - return EllipticCurves.getEcPublicKey(Base64.decode(x509PublicKey)); - } - - static ECPrivateKey pkcs8EcPrivateKey(String pkcs8PrivateKey) throws GeneralSecurityException { - return EllipticCurves.getEcPrivateKey(Base64.decode(pkcs8PrivateKey)); - } - - static byte[] toLengthValue(String... chunks) throws GeneralSecurityException { - byte[] out = new byte[0]; - for (String chunk : chunks) { - byte[] bytes = chunk.getBytes(StandardCharsets.UTF_8); - out = Bytes.concat(out, Bytes.intToByteArray(4, bytes.length)); - out = Bytes.concat(out, bytes); - } - return out; - } - - static byte[] aesCtr(final byte[] encryptionKey, final byte[] message) - throws GeneralSecurityException { - Cipher cipher = EngineFactory.CIPHER.getInstance(PaymentMethodTokenConstants.AES_CTR_ALGO); - cipher.init( - Cipher.ENCRYPT_MODE, - new SecretKeySpec(encryptionKey, "AES"), - new IvParameterSpec(PaymentMethodTokenConstants.AES_CTR_ZERO_IV)); - return cipher.doFinal(message); - } - - static byte[] hmacSha256(final byte[] macKey, final byte[] encryptedMessage) - throws GeneralSecurityException { - SecretKeySpec key = new SecretKeySpec(macKey, PaymentMethodTokenConstants.HMAC_SHA256_ALGO); - Mac mac = EngineFactory.MAC.getInstance(PaymentMethodTokenConstants.HMAC_SHA256_ALGO); - mac.init(key); - return mac.doFinal(encryptedMessage); - } - - private PaymentMethodTokenUtil() {} -}
diff --git a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/SenderIntermediateCertFactory.java b/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/SenderIntermediateCertFactory.java deleted file mode 100644 index 0e3bfc7..0000000 --- a/apps/paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken/SenderIntermediateCertFactory.java +++ /dev/null
@@ -1,218 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import com.google.crypto.tink.PublicKeySign; -import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EcdsaSignJce; -import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.internal.Streams; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.security.GeneralSecurityException; -import java.security.interfaces.ECPrivateKey; -import java.util.ArrayList; -import java.util.List; - -/** - * Creates a signed certificate with the intermediate signing keys used by the sender in certain - * protocol versions. - * - * @since 1.1.0 - */ -public class SenderIntermediateCertFactory { - private final List<PublicKeySign> signers; - private final String intermediateSigningKey; - private final String protocolVersion; - private final String senderId; - private final long expiration; - - private SenderIntermediateCertFactory( - String protocolVersion, - String senderId, - List<ECPrivateKey> senderSigningKeys, - String intermediateSigningKey, - long expiration) - throws GeneralSecurityException { - if (!ProtocolVersionConfig.forProtocolVersion(protocolVersion) - .supportsIntermediateSigningKeys) { - throw new IllegalArgumentException("invalid version: " + protocolVersion); - } - if (senderSigningKeys.isEmpty()) { - throw new IllegalArgumentException( - "must add at least one sender's signing key using Builder.addSenderSigningKey"); - } - if (expiration == 0) { - throw new IllegalArgumentException("must set expiration using Builder.expiration"); - } - if (expiration < 0) { - throw new IllegalArgumentException("invalid negative expiration"); - } - this.protocolVersion = protocolVersion; - this.senderId = senderId; - this.signers = new ArrayList<>(); - for (ECPrivateKey senderSigningKey : senderSigningKeys) { - this.signers.add( - new EcdsaSignJce( - senderSigningKey, PaymentMethodTokenConstants.ECDSA_HASH_SHA256, EcdsaEncoding.DER)); - } - this.intermediateSigningKey = intermediateSigningKey; - this.expiration = expiration; - } - - /** - * Builder for {@link SenderIntermediateCertFactory}. - * - * @since 1.1.0 - */ - public static class Builder { - private final List<ECPrivateKey> senderSigningKeys = new ArrayList<>(); - private String intermediateSigningKey; - private String protocolVersion = PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2; - private String senderId = PaymentMethodTokenConstants.GOOGLE_SENDER_ID; - private long expiration; - - public Builder() {} - - /** Sets the protocolVersion. */ - public Builder protocolVersion(String val) { - protocolVersion = val; - return this; - } - - /** Sets the sender Id. */ - public Builder senderId(String val) { - senderId = val; - return this; - } - - /** Sets the expiration in millis since epoch. */ - public Builder expiration(long millisSinceEpoch) { - expiration = millisSinceEpoch; - return this; - } - - /** - * Adds a signing key of the sender. - * - * <p>It must be base64 encoded PKCS8 private key. - */ - public Builder addSenderSigningKey(String val) throws GeneralSecurityException { - return addSenderSigningKey(PaymentMethodTokenUtil.pkcs8EcPrivateKey(val)); - } - - /** - * Adds a signing key of the sender. - * - * <p>It must be base64 encoded PKCS8 private key. - */ - public Builder addSenderSigningKey(ECPrivateKey val) throws GeneralSecurityException { - this.senderSigningKeys.add(val); - return this; - } - - /** - * Sets the intermediate signing key being signed. - * - * <p>The public key specified here is a base64 (no wrapping, padded) version of the key encoded - * in ASN.1 type SubjectPublicKeyInfo defined in the X.509 standard. - */ - public Builder senderIntermediateSigningKey(String val) throws GeneralSecurityException { - // Parsing to validate the format - PaymentMethodTokenUtil.x509EcPublicKey(val); - intermediateSigningKey = val; - return this; - } - - public SenderIntermediateCertFactory build() throws GeneralSecurityException { - return new SenderIntermediateCertFactory( - protocolVersion, senderId, senderSigningKeys, intermediateSigningKey, expiration); - } - } - - static String jsonEncodeSignedKey(String intermediateSigningKey, long expiration) { - try { - JsonObject jsonObj = new JsonObject(); - jsonObj.addProperty(PaymentMethodTokenConstants.JSON_KEY_VALUE_KEY, intermediateSigningKey); - jsonObj.addProperty( - PaymentMethodTokenConstants.JSON_KEY_EXPIRATION_KEY, Long.toString(expiration)); - StringWriter stringWriter = new StringWriter(); - JsonWriter jsonWriter = new JsonWriter(stringWriter); - jsonWriter.setHtmlSafe(true); - Streams.write(jsonObj, jsonWriter); - return stringWriter.toString(); - } catch (JsonParseException | IllegalStateException | IOException e) { - throw new AssertionError("Failed to perform JSON encoding", e); - } - } - - static String jsonEncodeCertificate(String signedKey, ArrayList<String> signatures) { - try { - JsonArray jsonSignatures = new JsonArray(); - for (String signature : signatures) { - jsonSignatures.add(signature); - } - JsonObject result = new JsonObject(); - result.addProperty(PaymentMethodTokenConstants.JSON_SIGNED_KEY_KEY, signedKey); - result.add(PaymentMethodTokenConstants.JSON_SIGNATURES_KEY, jsonSignatures); - StringWriter stringWriter = new StringWriter(); - JsonWriter jsonWriter = new JsonWriter(stringWriter); - jsonWriter.setHtmlSafe(true); - Streams.write(result, jsonWriter); - return stringWriter.toString(); - } catch (JsonParseException | IllegalStateException | IOException e) { - throw new AssertionError("Failed to perform JSON encoding", e); - } - } - - /** - * Creates the certificate. - * - * <p>This will return a serialized JSONObject in the following format: - * - * <pre> - * { - * // { - * // // A string that identifies this cert - * // "keyValue": "ZXBoZW1lcmFsUHVibGljS2V5" - * // // string (UTC milliseconds since epoch) - * // "expiration": "1520836260646", - * // } - * "signedKey": "... serialized JSON shown in comment above ...", - * "signatures": ["signature1", "signature2", ...], - * } - * </pre> - */ - public String create() throws GeneralSecurityException { - String signedKey = jsonEncodeSignedKey(intermediateSigningKey, expiration); - byte[] toSignBytes = - PaymentMethodTokenUtil.toLengthValue( - // The order of the parameters matters. - senderId, protocolVersion, signedKey); - ArrayList<String> signatures = new ArrayList<>(); - for (PublicKeySign signer : signers) { - byte[] signature = signer.sign(toSignBytes); - signatures.add(Base64.encode(signature)); - } - return jsonEncodeCertificate(signedKey, signatures); - } -}
diff --git a/apps/paymentmethodtoken/src/test/BUILD.bazel b/apps/paymentmethodtoken/src/test/BUILD.bazel deleted file mode 100644 index c6f52d3..0000000 --- a/apps/paymentmethodtoken/src/test/BUILD.bazel +++ /dev/null
@@ -1,43 +0,0 @@ -load("@tink_java//tools:gen_java_test_rules.bzl", "gen_java_test_rules") - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -# Tests - -java_library( - name = "generator_test", - testonly = 1, - srcs = glob([ - "**/*.java", - ]), - deps = [ - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:google_payments_public_keys_manager", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_constants", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_hybrid_decrypt", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_hybrid_encrypt", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_recipient", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_recipient_kem", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_sender", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:payment_method_token_util", - "//paymentmethodtoken/src/main/java/com/google/crypto/tink/apps/paymentmethodtoken:sender_intermediate_cert_factory", - "@maven//:com_google_code_gson_gson", - "@maven//:com_google_http_client_google_http_client", - "@maven//:joda_time_joda_time", - "@maven//:junit_junit", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_decrypt", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_encrypt", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:random", - ], -) - -gen_java_test_rules( - test_files = glob(["**/*Test.java"], - ), - deps = [ - ":generator_test", - ], -)
diff --git a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/GooglePaymentsPublicKeyManagerTest.java b/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/GooglePaymentsPublicKeyManagerTest.java deleted file mode 100644 index 59cf9ad..0000000 --- a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/GooglePaymentsPublicKeyManagerTest.java +++ /dev/null
@@ -1,61 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.fail; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Tests for {@link GooglePaymentsPublicKeysManager}. */ -@RunWith(JUnit4.class) -public class GooglePaymentsPublicKeyManagerTest { - @Test - public void builderShouldReturnSingletonsWhenMatching() { - assertSame( - GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION, - new GooglePaymentsPublicKeysManager.Builder().build()); - assertSame( - GooglePaymentsPublicKeysManager.INSTANCE_TEST, - new GooglePaymentsPublicKeysManager.Builder() - .setKeysUrl(GooglePaymentsPublicKeysManager.KEYS_URL_TEST) - .build()); - } - - @Test - public void builderShouldReturnDifferentInstanceWhenNotMatchingSingletons() { - assertNotSame( - GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION, - new GooglePaymentsPublicKeysManager.Builder().setKeysUrl("https://abc").build()); - assertNotSame( - GooglePaymentsPublicKeysManager.INSTANCE_TEST, - new GooglePaymentsPublicKeysManager.Builder().setKeysUrl("https://abc").build()); - } - - @Test - public void builderShouldThrowIllegalArgumentExceptionWhenUrlIsNotHttps() { - try { - new GooglePaymentsPublicKeysManager.Builder().setKeysUrl("http://abc").build(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException ex) { - // expected. - } - } -}
diff --git a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodJsonEncodingTest.java b/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodJsonEncodingTest.java deleted file mode 100644 index 70b8ed4..0000000 --- a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodJsonEncodingTest.java +++ /dev/null
@@ -1,139 +0,0 @@ -// Copyright 2021 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Tests for the exact Json-Encoding produced. - * - * These tests test implementation details. Do not depend on the this. For example, the particular - * ordering of the elements or the particular character escaping used may change in the future. - * */ -@RunWith(JUnit4.class) -public final class PaymentMethodJsonEncodingTest { - - @Test - public void testExactOutputOfJsonEncodeCiphertext() throws Exception { - byte[] ciphertext = "CiPhErTeXt".getBytes(UTF_8); - byte[] tag = "taaag".getBytes(UTF_8); - byte[] ephemeralPublicKey = "ephemeral Public Key".getBytes(UTF_8); - - String jsonEncodedCiphertext = - PaymentMethodTokenHybridEncrypt.jsonEncodeCiphertext(ciphertext, tag, ephemeralPublicKey); - - // JSONObject uses a HashMap, where the ordering is not defined. The ordering is however - // deterministic. And for jsonEncodeCiphertext, the order happens to be first "encryptedMessage" - // then "ephemeralPublicKey", and finally "tag". Also, JSONObject uses HTML-safe encoding. - assertEquals( - "{\"encryptedMessage\":\"Q2lQaEVyVGVYdA\\u003d\\u003d\",\"ephemeralPublicKey\":" - + "\"ZXBoZW1lcmFsIFB1YmxpYyBLZXk\\u003d\",\"tag\":\"dGFhYWc\\u003d\"}", - jsonEncodedCiphertext); - } - - @Test - public void testExactOutputOfJsonEncodeSignedMessage() throws Exception { - String senderIntermediateCert = - "{\"signedKey\":\"{\\\"keyValue\\\":\\\"abcde\\\\u003d\\\\u003d\\\",\\\"keyExpiration\\\"" - + ":\\\"1615299372858\\\"}\",\"signatures\":[\"fghijkl\\u003d\"]}"; - String version = "ECv1"; - String message = - "{\"encryptedMessage\":\"Q2lQaEVyVGVYdA\\u003d\\u003d\",\"ephemeralPublicKey\":\"ZXBoZW1l" - + "cmFsIFB1YmxpYyBLZXk\\u003d\",\"tag\":\"dGFhYWc\\u003d\"}"; - byte[] signature = "the signature".getBytes(UTF_8); - - String jsonEncodedSignedMessage = - PaymentMethodTokenSender.jsonEncodeSignedMessage( - message, version, signature, senderIntermediateCert); - - String expected = - "{\"signature\":\"dGhlIHNpZ25hdHVyZQ\\u003d\\u003d\",\"intermediateSigningKey\":{\"signe" - + "dKey\":\"{\\\"keyValue\\\":\\\"abcde\\\\u003d\\\\u003d\\\",\\\"keyExpiration\\\":" - + "\\\"1615299372858\\\"}\",\"signatures\":[\"fghijkl\\u003d\"]},\"protocolVersion\"" - + ":\"ECv1\",\"signedMessage\":\"{\\\"encryptedMessage\\\":\\\"Q2lQaEVyVGVYdA\\\\u00" - + "3d\\\\u003d\\\",\\\"ephemeralPublicKey\\\":\\\"ZXBoZW1lcmFsIFB1YmxpYyBLZXk\\\\u00" - + "3d\\\",\\\"tag\\\":\\\"dGFhYWc\\\\u003d\\\"}\"}"; - assertEquals(expected, jsonEncodedSignedMessage); - - String expected2 = - "{\"signature\":\"dGhlIHNpZ25hdHVyZQ\\u003d\\u003d\",\"protocolVersion\":\"ECv1\",\"sign" - + "edMessage\":\"{\\\"encryptedMessage\\\":\\\"Q2lQaEVyVGVYdA\\\\u003d\\\\u003d\\\"," - + "\\\"ephemeralPublicKey\\\":\\\"ZXBoZW1lcmFsIFB1YmxpYyBLZXk\\\\u003d\\\",\\\"tag\\" - + "\":\\\"dGFhYWc\\\\u003d\\\"}\"}"; - - String jsonEncodedSignedMessage2 = - PaymentMethodTokenSender.jsonEncodeSignedMessage(message, version, signature, null); - assertEquals(expected2, jsonEncodedSignedMessage2); - } - - @Test - public void testExactOutputOfJsonEncodedSignedKey() throws Exception { - String intermediateSigningKey = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSA" - + "M43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw=="; - long expiration = 1520836260646L; - assertEquals( - "{\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1" - + "TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\",\"keyExpiration\"" - + ":\"1520836260646\"}", - SenderIntermediateCertFactory.jsonEncodeSignedKey(intermediateSigningKey, expiration)); - } - - @Test - public void testExactOutputOfJsonEncodeCertificate() throws Exception { - String intermediateSigningKey = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSA" - + "M43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw=="; - long expiration = 1520836260646L; - String signedKey = - SenderIntermediateCertFactory.jsonEncodeSignedKey(intermediateSigningKey, expiration); - ArrayList<String> signatures = new ArrayList<>(); - signatures.add("iTzFvzFRxyCw=="); - signatures.add("abcde090/+=="); - signatures.add("xyz"); - String expected = - "{\"signedKey\":\"{\\\"keyValue\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE" - + "/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRx" - + "yCw\\\\u003d\\\\u003d\\\",\\\"keyExpiration\\\":\\\"1520836260646\\\"}\",\"signatur" - + "es\":[\"iTzFvzFRxyCw\\u003d\\u003d\",\"abcde090/+\\u003d\\u003d\",\"xyz\"]}"; - assertEquals( - expected, SenderIntermediateCertFactory.jsonEncodeCertificate(signedKey, signatures)); - } - - @Test - public void testExactOutputOfWeirdJsonEncodeCertificate() throws Exception { - String intermediateSigningKey = - "\"\\=="; - long expiration = -123; - String signedKey = - SenderIntermediateCertFactory.jsonEncodeSignedKey(intermediateSigningKey, expiration); - ArrayList<String> signatures = new ArrayList<>(); - signatures.add(""); - signatures.add("\\\"/+=="); - String expected = - "{\"signedKey\":\"{\\\"keyValue\\\":\\\"\\\\\\\"\\\\\\\\\\\\u003d\\\\u003d" - + "\\\",\\\"keyExpiration\\\":\\\"-123\\\"}\",\"signatures\":[\"\",\"\\\\\\\"/+\\u003d" - + "\\u003d\"]}"; - assertEquals( - expected, SenderIntermediateCertFactory.jsonEncodeCertificate(signedKey, signatures)); - } -}
diff --git a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridDecryptTest.java b/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridDecryptTest.java deleted file mode 100644 index 387c3aa..0000000 --- a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridDecryptTest.java +++ /dev/null
@@ -1,119 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.fail; - -import com.google.crypto.tink.HybridDecrypt; -import com.google.crypto.tink.HybridEncrypt; -import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.Random; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECParameterSpec; -import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@code PaymentMethodTokenHybridDecrypt}. */ -@RunWith(JUnit4.class) -public class PaymentMethodTokenHybridDecryptTest { - @Test - public void testModifyDecrypt() throws Exception { - ECParameterSpec spec = EllipticCurves.getNistP256Params(); - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - keyGen.initialize(spec); - KeyPair recipientKey = keyGen.generateKeyPair(); - ECPublicKey recipientPublicKey = (ECPublicKey) recipientKey.getPublic(); - ECPrivateKey recipientPrivateKey = (ECPrivateKey) recipientKey.getPrivate(); - - HybridEncrypt hybridEncrypt = - new PaymentMethodTokenHybridEncrypt(recipientPublicKey, ProtocolVersionConfig.EC_V1); - HybridDecrypt hybridDecrypt = - new PaymentMethodTokenHybridDecrypt(recipientPrivateKey, ProtocolVersionConfig.EC_V1); - testModifyDecrypt(hybridEncrypt, hybridDecrypt); - } - - public void testModifyDecrypt(HybridEncrypt hybridEncrypt, HybridDecrypt hybridDecrypt) - throws Exception { - byte[] plaintext = Random.randBytes(111); - byte[] context = "context info".getBytes(UTF_8); - - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, context); - byte[] decrypted = hybridDecrypt.decrypt(ciphertext, context); - assertArrayEquals(plaintext, decrypted); - - JsonObject json = JsonParser.parseString(new String(ciphertext, UTF_8)).getAsJsonObject(); - - // Modify public key. - byte[] kem = - Base64.decode( - json.get(PaymentMethodTokenConstants.JSON_EPHEMERAL_PUBLIC_KEY).getAsString()); - for (int bytes = 0; bytes < kem.length; bytes++) { - for (int bit = 0; bit < 8; bit++) { - byte[] modifiedPublicKey = Arrays.copyOf(kem, kem.length); - modifiedPublicKey[bytes] ^= (byte) (1 << bit); - json.addProperty( - PaymentMethodTokenConstants.JSON_EPHEMERAL_PUBLIC_KEY, - Base64.encode(modifiedPublicKey)); - try { - hybridDecrypt.decrypt(json.toString().getBytes(UTF_8), context); - fail("Invalid ciphertext, should have thrown exception"); - } catch (GeneralSecurityException expected) { - // Expected - } - } - } - - // Modify payload. - byte[] payload = - Base64.decode( - json.get(PaymentMethodTokenConstants.JSON_ENCRYPTED_MESSAGE_KEY).getAsString()); - for (int bytes = 0; bytes < payload.length; bytes++) { - for (int bit = 0; bit < 8; bit++) { - byte[] modifiedPayload = Arrays.copyOf(payload, payload.length); - modifiedPayload[bytes] ^= (byte) (1 << bit); - json.addProperty( - PaymentMethodTokenConstants.JSON_ENCRYPTED_MESSAGE_KEY, Base64.encode(modifiedPayload)); - try { - hybridDecrypt.decrypt(json.toString().getBytes(UTF_8), context); - fail("Invalid ciphertext, should have thrown exception"); - } catch (GeneralSecurityException expected) { - // Expected - } - } - } - - // Modify context. - try { - hybridDecrypt.decrypt(ciphertext, Arrays.copyOf(context, context.length - 1)); - fail("Invalid context, should have thrown exception"); - } catch (GeneralSecurityException expected) { - // Expected - } - } -}
diff --git a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridEncryptTest.java b/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridEncryptTest.java deleted file mode 100644 index 3fe9459..0000000 --- a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenHybridEncryptTest.java +++ /dev/null
@@ -1,76 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -import com.google.crypto.tink.HybridDecrypt; -import com.google.crypto.tink.HybridEncrypt; -import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.Random; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECParameterSpec; -import java.util.Set; -import java.util.TreeSet; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@code PaymentMethodTokenHybridEncrypt}. */ -@RunWith(JUnit4.class) -public class PaymentMethodTokenHybridEncryptTest { - @Test - public void testBasicMultipleEncrypts() throws Exception { - ECParameterSpec spec = EllipticCurves.getNistP256Params(); - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - keyGen.initialize(spec); - KeyPair recipientKey = keyGen.generateKeyPair(); - ECPublicKey recipientPublicKey = (ECPublicKey) recipientKey.getPublic(); - ECPrivateKey recipientPrivateKey = (ECPrivateKey) recipientKey.getPrivate(); - - HybridEncrypt hybridEncrypt = - new PaymentMethodTokenHybridEncrypt(recipientPublicKey, ProtocolVersionConfig.EC_V1); - HybridDecrypt hybridDecrypt = - new PaymentMethodTokenHybridDecrypt(recipientPrivateKey, ProtocolVersionConfig.EC_V1); - testBasicMultipleEncrypts(hybridEncrypt, hybridDecrypt); - } - - public void testBasicMultipleEncrypts(HybridEncrypt hybridEncrypt, HybridDecrypt hybridDecrypt) - throws Exception { - byte[] plaintext = Random.randBytes(111); - byte[] context = "context info".getBytes(StandardCharsets.UTF_8); - // Makes sure that the encryption is randomized. - Set<String> ciphertexts = new TreeSet<String>(); - for (int j = 0; j < 100; j++) { - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, context); - if (ciphertexts.contains(new String(ciphertext, StandardCharsets.UTF_8))) { - throw new GeneralSecurityException("Encryption is not randomized"); - } - ciphertexts.add(new String(ciphertext, StandardCharsets.UTF_8)); - byte[] decrypted = hybridDecrypt.decrypt(ciphertext, context); - assertArrayEquals(plaintext, decrypted); - } - assertEquals(100, ciphertexts.size()); - } -}
diff --git a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipientTest.java b/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipientTest.java deleted file mode 100644 index 4bce9f5..0000000 --- a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenRecipientTest.java +++ /dev/null
@@ -1,1403 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import com.google.api.client.testing.http.MockHttpTransport; -import com.google.api.client.testing.http.MockLowLevelHttpResponse; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import java.security.GeneralSecurityException; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import org.joda.time.Duration; -import org.joda.time.Instant; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@code PaymentMethodTokenRecipient}. */ -@RunWith(JUnit4.class) -public class PaymentMethodTokenRecipientTest { - - /** - * Sample merchant public key. - * - * <p>Corresponds to public key of {@link #MERCHANT_PRIVATE_KEY_PKCS8_BASE64} - * - * <p>Created with: - * - * <pre> - * openssl ec -in merchant-key.pem -pubout -text -noout 2> /dev/null | grep "pub:" -A5 \ - * | xxd -r -p | base64 - * </pre> - */ - private static final String MERCHANT_PUBLIC_KEY_BASE64 = - "BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y="; - - /** - * Sample merchant private key. - * - * <p>Corresponds to the private key of {@link #MERCHANT_PUBLIC_KEY_BASE64} - * - * <pre> - * openssl pkcs8 -topk8 -inform PEM -outform PEM -in merchant-key.pem -nocrypt - * </pre> - */ - private static final String MERCHANT_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgCPSuFr4iSIaQprjj" - + "chHPyDu2NXFe0vDBoTpPkYaK9dehRANCAATnaFz/vQKuO90pxsINyVNWojabHfbx" - + "9qIJ6uD7Q7ZSxmtyo/Ez3/o2kDT8g0pIdyVIYktCsq65VoQIDWSh2Bdm"; - - /** An alternative merchant private key used during the tests. */ - private static final String ALTERNATE_MERCHANT_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgOUIzccyJ3rTx6SVm" - + "XrWdtwUP0NU26nvc8KIYw2GmYZKhRANCAAR5AjmTNAE93hQEQE+PryLlgr6Q7FXyN" - + "XoZRk+1Fikhq61mFhQ9s14MOwGBxd5O6Jwn/sdUrWxkYk3idtNEN1Rz"; - - /** Sample Google provided JSON with its public signing keys. */ - private static final String GOOGLE_VERIFYING_PUBLIC_KEYS_JSON = - "{\n" - + " \"keys\": [\n" - + " {\n" - + " \"keyValue\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPYnHwS8uegWAewQtlxizmLFynw" - + "HcxRT1PK07cDA6/C4sXrVI1SzZCUx8U8S0LjMrT6ird/VW7be3Mz6t/srtRQ==\",\n" - + " \"protocolVersion\": \"ECv1\"\n" - + " },\n" - + " {\n" - + " \"keyValue\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM" - + "43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw==\",\n" - + " \"keyExpiration\": \"" - + Instant.now().plus(Duration.standardDays(1)).getMillis() - + "\",\n" - + " \"protocolVersion\": \"ECv2\"\n" - + " },\n" - + " {\n" - + " \"keyValue\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENXvYqxD5WayKYhuXQevdGdLA8i" - + "fV4LsRS2uKvFo8wwyiwgQHB9DiKzG6T/P1Fu9Bl7zWy/se5Dy4wk1mJoPuxg==\",\n" - + " \"keyExpiration\": \"" - + Instant.now().plus(Duration.standardDays(1)).getMillis() - + "\",\n" - + " \"protocolVersion\": \"ECv2SigningOnly\"\n" - + " }\n" - + " ]\n" - + "}"; - - /** Index within {@link #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON} of the ECv1 Google signing key. */ - private static final int INDEX_OF_GOOGLE_SIGNING_EC_V1 = 0; - - /** Index within {@link #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON} of the ECv2 Google signing key. */ - private static final int INDEX_OF_GOOGLE_SIGNING_EC_V2 = 1; - - /** - * Index within {@link #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON} of the ECv2SigningOnly Google signing - * key. - */ - private static final int INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY = 2; - - /** - * Sample Google private signing key for the ECv1 protocolVersion. - * - * <p>Corresponds to the ECv1 private key of the key in {@link - * #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON}. - */ - private static final String GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZj/Dldxz8fvKVF5O" - + "TeAtK6tY3G1McmvhMppe6ayW6GahRANCAAQ9icfBLy56BYB7BC2XGLOYsXKfAdzF" - + "FPU8rTtwMDr8LixetUjVLNkJTHxTxLQuMytPqKt39Vbtt7czPq3+yu1F"; - - /** - * Sample Google private signing key for the ECv2 protocolVersion. - * - * <p>Corresponds to ECv2 private key of the key in {@link #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON}. - */ - private static final String GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKvEdSS8f0mjTCNKev" - + "aKXIzfNC5b4A104gJWI9TsLIMqhRANCAAT/X7ccFVJt2/6Ps1oCt2AzKhIAz" - + "jfJHJ3Op2DVPGh1LMD3oOPgxzUSIqujHG6dq9Ui93Eacl4VWJPMW/MVHHIL"; - - /** - * Sample Google intermediate public signing key for the ECv2 protocolVersion. - * - * <p>Base64 version of the public key encoded in ASN.1 type SubjectPublicKeyInfo defined in the - * X.509 standard. - * - * <p>The intermediate public key will be signed by {@link - * #GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64}. - */ - private static final String GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64 = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yR" - + "ydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw=="; - - /** - * Sample Google intermediate private signing key for the ECv2 protocolVersion. - * - * <p>Corresponds to private key of the key in {@link - * #GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64}. - */ - private static final String GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKvEdSS8f0mjTCNKev" - + "aKXIzfNC5b4A104gJWI9TsLIMqhRANCAAT/X7ccFVJt2/6Ps1oCt2AzKhIAz" - + "jfJHJ3Op2DVPGh1LMD3oOPgxzUSIqujHG6dq9Ui93Eacl4VWJPMW/MVHHIL"; - - /** - * Sample Google private signing key for the ECv2SigningOnly protocolVersion. - * - * <p>Corresponds to ECv2SigningOnly private key of the key in {@link - * #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON}. - */ - private static final String GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgRi9hSdY+knJ08odnY" - + "tZFMRi7ZYeMoasAijLhD4GiQ1yhRANCAAQ1e9irEPlZrIpiG5dB690Z0sDy" - + "J9XguxFLa4q8WjzDDKLCBAcH0OIrMbpP8/UW70GXvNbL+x7kPLjCTWYmg+7G"; - - /** - * Sample Google intermediate public signing key for the ECv2SigningOnly protocolVersion. - * - * <p>Base64 version of the public key encoded in ASN.1 type SubjectPublicKeyInfo defined in the - * X.509 standard. - * - * <p>The intermediate public key will be signed by {@link - * #GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_PRIVATE_KEY_PKCS8_BASE64}. - */ - private static final String - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PUBLIC_KEY_X509_BASE64 = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8OaurwvbyYm8JWDgFPRTIDg0/" - + "kcQTFAQ4txi5IP0AyM1QiagwRhDUfjpqZkpw8xt/DXwyWYM0DdHqoeV" - + "TKqmYQ=="; - - /** - * Sample Google intermediate private signing key for the ECv2SigningOnly protocolVersion. - * - * <p>Corresponds to private key of the key in {@link - * #GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PUBLIC_KEY_X509_BASE64}. - */ - private static final String - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+Jvpkq26tpZ0s" - + "TTZVh4teEI41SnJdmkBzM8VZ5ZirE2hRANCAATw5q6vC9vJibwlYOAU" - + "9FMgODT+RxBMUBDi3GLkg/QDIzVCJqDBGENR+OmpmSnDzG38NfDJZgz" - + "QN0eqh5VMqqZh"; - - private static final String RECIPIENT_ID = "someRecipient"; - - private static final String PLAINTEXT = "plaintext"; - - /** - * The result of {@link #PLAINTEXT} encrypted with {@link #MERCHANT_PRIVATE_KEY_PKCS8_BASE64} and - * signed with the only key in {@link #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON} using the ECv1 - * protocolVersion. - */ - private static final String CIPHERTEXT_EC_V1 = - "{" - + "\"protocolVersion\":\"ECv1\"," - + "\"signedMessage\":" - + ("\"{" - + "\\\"tag\\\":\\\"ZVwlJt7dU8Plk0+r8rPF8DmPTvDiOA1UAoNjDV+SqDE\\\\u003d\\\"," - + "\\\"ephemeralPublicKey\\\":\\\"BPhVspn70Zj2Kkgu9t8+ApEuUWsI/zos5whGCQBlgOkuYagOis7" - + "qsrcbQrcprjvTZO3XOU+Qbcc28FSgsRtcgQE\\\\u003d\\\"," - + "\\\"encryptedMessage\\\":\\\"12jUObueVTdy\\\"}\",") - + "\"signature\":\"MEQCIDxBoUCoFRGReLdZ/cABlSSRIKoOEFoU3e27c14vMZtfAiBtX3pGMEpnw6mSAbnagC" - + "CgHlCk3NcFwWYEyxIE6KGZVA\\u003d\\u003d\"}"; - - private static final String ALTERNATE_PUBLIC_SIGNING_KEY = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEU8E6JppGKFG40r5dDU1idHRN52NuwsemFzXZh1oUqh3bGUPgPioH+RoW" - + "nmVSUQz1WfM2426w9f0GADuXzpUkcw=="; - - private static final class MyPaymentMethodTokenRecipientKem - implements PaymentMethodTokenRecipientKem { - private final ECPrivateKey privateKey; - - public MyPaymentMethodTokenRecipientKem(String recipientPrivateKey) - throws GeneralSecurityException { - privateKey = PaymentMethodTokenUtil.pkcs8EcPrivateKey(recipientPrivateKey); - } - - @Override - public byte[] computeSharedSecret(final byte[] ephemeralPublicKey) - throws GeneralSecurityException { - ECPublicKey publicKey = - EllipticCurves.getEcPublicKey( - privateKey.getParams(), - PaymentMethodTokenConstants.UNCOMPRESSED_POINT_FORMAT, - ephemeralPublicKey); - return EllipticCurves.computeSharedSecret(privateKey, publicKey); - } - } - - @Test - public void testShouldDecryptECV1() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); - } - - @Test - public void testShouldDecryptECV1WithNonStrictJsonEncoding() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - String ciphertextEcV1WithNonStrictJsonEncoding = - "{" - + "# comment \n" // python-style comment terminated with new line - + "protocolVersion:'ECv1'," // protocolVersion has no quotes, ECv1 has single quotes - + "/* a comment */" // c-style comment - + "\"signedMessage\"=" // use = instead of : - + "// another comment \n" // c-style comment terminated with new line - + ("\"{" - + "\\\"tag\\\":\\\"ZVwlJt7dU8Plk0+r8rPF8DmPTvDiOA1UAoNjDV+SqDE\\\\u003d\\\"," - + "\\\"ephemeralPublicKey\\\":\\\"BPhVspn70Zj2Kkgu9t8+ApEuUWsI/zos5whGCQBlgOkuYagOis7" - + "qsrcbQrcprjvTZO3XOU+Qbcc28FSgsRtcgQE\\\\u003d\\\"," - + "\\\"encryptedMessage\\\":\\\"12jUObueVTdy\\\"}\";") // ; instead of , - + "\"signature\":\"MEQCIDxBoUCoFRGReLdZ/cABlSSRIKoOEFoU3e27c14vMZtfAiBtX3pGMEpnw6mSAbnagC" - + "CgHlCk3NcFwWYEyxIE6KGZVA\\u003d\\u003d\"}"; - - assertEquals(PLAINTEXT, recipient.unseal(ciphertextEcV1WithNonStrictJsonEncoding)); - } - - @Test - public void testShouldDecryptECV1WhenUsingCustomKem() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientKem( - new MyPaymentMethodTokenRecipientKem(MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); - } - - @Test - public void testShouldDecryptECV1WhenFetchingSenderVerifyingKeys() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .fetchSenderVerifyingKeysWith( - new GooglePaymentsPublicKeysManager.Builder() - .setHttpTransport( - new MockHttpTransport.Builder() - .setLowLevelHttpResponse( - new MockLowLevelHttpResponse() - .setContent(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON)) - .build()) - .build()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); - } - - @Test - public void testShouldTryAllKeysToDecryptECV1() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(ALTERNATE_MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); - } - - @Test - public void testShouldTryAllCustomKemsToDecryptECV1() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientKem( - new MyPaymentMethodTokenRecipientKem(ALTERNATE_MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) - .addRecipientKem( - new MyPaymentMethodTokenRecipientKem(MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); - } - - @Test - public void testShouldFailIfDecryptingWithDifferentKeyECV1() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(ALTERNATE_MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - try { - recipient.unseal(CIPHERTEXT_EC_V1); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot decrypt", e.getMessage()); - } - } - - @Test - public void testShouldFailIfDecryptingWithDifferentCustomKemECV1() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientKem( - new MyPaymentMethodTokenRecipientKem(ALTERNATE_MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) - .build(); - - try { - recipient.unseal(CIPHERTEXT_EC_V1); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot decrypt", e.getMessage()); - } - } - - @Test - public void testShouldFailIfVerifyingWithDifferentKeyECV1() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - trustedKeysJson - .get("keys") - .getAsJsonArray() - .get(INDEX_OF_GOOGLE_SIGNING_EC_V1) - .getAsJsonObject() - .addProperty("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - try { - recipient.unseal(CIPHERTEXT_EC_V1); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldTryAllKeysToVerifySignatureECV1() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject correctKey = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V1).getAsJsonObject(); - JsonObject wrongKey = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V1).getAsJsonObject().deepCopy(); - wrongKey.addProperty("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); - JsonArray newKeys = new JsonArray(); - newKeys.add(wrongKey); - newKeys.add(correctKey); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); - } - - @Test - public void testShouldFailIfSignedECV1WithKeyForWrongProtocolVersion() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject correctKeyButWrongProtocol = - keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V1).getAsJsonObject(); - correctKeyButWrongProtocol.addProperty( - PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, "ECv2"); - JsonObject wrongKeyButRightProtocol = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V1).getAsJsonObject(); - wrongKeyButRightProtocol.addProperty("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); - wrongKeyButRightProtocol.addProperty( - PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, - PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1); - JsonArray newKeys = new JsonArray(); - newKeys.add(correctKeyButWrongProtocol); - newKeys.add(wrongKeyButRightProtocol); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - try { - recipient.unseal(CIPHERTEXT_EC_V1); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldFailIfNoSigningKeysForProtocolVersion() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject key1 = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V1).getAsJsonObject(); - key1.addProperty(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, "ECv2"); - JsonObject key2 = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V1).getAsJsonObject(); - - key2.addProperty("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); - key2.addProperty(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, "ECv3"); - JsonArray newKeys = new JsonArray(); - newKeys.add(key1); - newKeys.add(key2); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - try { - recipient.unseal(CIPHERTEXT_EC_V1); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("no trusted keys are available for this protocol version", e.getMessage()); - } - } - - @Test - public void testShouldFailIfSignedMessageWasChangedInECV1() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - JsonObject payload = JsonParser.parseString(CIPHERTEXT_EC_V1).getAsJsonObject(); - payload.addProperty( - PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY, - payload.get(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY).getAsString() + " "); - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldFailIfWrongRecipientInECV1() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId("not " + RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - try { - recipient.unseal(CIPHERTEXT_EC_V1); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldFailIfECV1SetsWrongProtocolVersion() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - JsonObject payload = JsonParser.parseString(CIPHERTEXT_EC_V1).getAsJsonObject(); - String invalidVersion = "ECv2"; - payload.addProperty(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, invalidVersion); - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("invalid version: " + invalidVersion, e.getMessage()); - } - } - - @Test - public void testShouldFailIfProtocolSetToAnInt() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - JsonObject payload = JsonParser.parseString(CIPHERTEXT_EC_V1).getAsJsonObject(); - payload.addProperty(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, 1); - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - // expected - } - } - - @Test - public void testShouldFailIfProtocolSetToAnFloat() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - JsonObject payload = JsonParser.parseString(CIPHERTEXT_EC_V1).getAsJsonObject(); - payload.addProperty(PaymentMethodTokenConstants.JSON_PROTOCOL_VERSION_KEY, 1.1); - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - // expected - } - } - - @Test - public void testShouldSucceedIfMessageIsNotExpired() throws Exception { - PaymentMethodTokenSender sender = - new PaymentMethodTokenSender.Builder() - .senderSigningKey(GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64) - .recipientId(RECIPIENT_ID) - .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) - .build(); - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - JsonObject plaintext = new JsonObject(); - plaintext.addProperty( - "messageExpiration", - // One day in the future - String.valueOf(Instant.now().plus(Duration.standardDays(1)).getMillis())); - plaintext.addProperty("someKey", "someValue"); - String ciphertext = sender.seal(plaintext.toString()); - JsonObject decrypted = JsonParser.parseString(recipient.unseal(ciphertext)).getAsJsonObject(); - - assertEquals("someValue", decrypted.get("someKey").getAsString()); - } - - @Test - public void testShouldFailIfMessageIsExpired() throws Exception { - PaymentMethodTokenSender sender = - new PaymentMethodTokenSender.Builder() - .senderSigningKey(GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64) - .recipientId(RECIPIENT_ID) - .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) - .build(); - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - JsonObject expired = new JsonObject(); - expired.addProperty( - "messageExpiration", - // One day in the past - String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())); - - String ciphertext = sender.seal(expired.toString()); - try { - recipient.unseal(ciphertext); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("expired payload", e.getMessage()); - } - } - - @Test - public void testShouldFailIfTrustedKeyIsExpiredInECV1() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject key1 = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V1).getAsJsonObject(); - key1.addProperty( - "keyExpiration", // One day in the past - String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())); - JsonArray newKeys = new JsonArray(); - newKeys.add(key1); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - try { - recipient.unseal(CIPHERTEXT_EC_V1); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("no trusted keys are available for this protocol version", e.getMessage()); - } - } - - @Test - public void testShouldSucceedIfKeyExpirationIsMissingInTrustedKeyIsExpiredForECV1() - throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject key1 = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V1).getAsJsonObject(); - key1.remove("keyExpiration"); - JsonArray newKeys = new JsonArray(); - newKeys.add(key1); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(CIPHERTEXT_EC_V1)); - } - - @Test - public void testUnsealECV2() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(sealECV2(PLAINTEXT))); - } - - @Test - public void testUnsealECV2WithCustomKem() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientKem( - new MyPaymentMethodTokenRecipientKem(MERCHANT_PRIVATE_KEY_PKCS8_BASE64)) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(sealECV2(PLAINTEXT))); - } - - @Test - public void testShouldFailIfSignedMessageWasChangedInECV2() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - JsonObject payload = JsonParser.parseString(sealECV2(PLAINTEXT)).getAsJsonObject(); - payload.addProperty( - PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY, - payload.get(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY).getAsString() + " "); - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldThrowIfECV2UseWrongSenderId() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .senderId("not-" + PaymentMethodTokenConstants.GOOGLE_SENDER_ID) - .build(); - - try { - recipient.unseal(sealECV2(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldFailIfVerifyingWithDifferentKeyECV2() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject key1 = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2).getAsJsonObject(); - key1.addProperty("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); - JsonArray newKeys = new JsonArray(); - newKeys.add(key1); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - try { - recipient.unseal(sealECV2(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldFailIfTrustedKeyIsExpiredInECV2() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject key1 = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2).getAsJsonObject(); - key1.addProperty( - "keyExpiration", // One day in the past - String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())); - JsonArray newKeys = new JsonArray(); - newKeys.add(key1); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - try { - recipient.unseal(sealECV2(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("no trusted keys are available for this protocol version", e.getMessage()); - } - } - - @Test - public void testShouldFailIfKeyExpirationIsMissingInTrustedKeyECV2() throws Exception { - // Key expiration is required for V2 - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject key1 = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2).getAsJsonObject(); - key1.remove("keyExpiration"); - JsonArray newKeys = new JsonArray(); - newKeys.add(key1); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - try { - recipient.unseal(sealECV2(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("no trusted keys are available for this protocol version", e.getMessage()); - } - } - - @Test - public void testShouldTryAllKeysToVerifySignatureECV2() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject correctKey = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2).getAsJsonObject(); - JsonObject wrongKey = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2).getAsJsonObject().deepCopy(); - wrongKey.addProperty("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); - JsonArray newKeys = new JsonArray(); - newKeys.add(wrongKey); - newKeys.add(correctKey); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(sealECV2(PLAINTEXT))); - } - - @Test - public void testShouldFailIfSignedKeyWasChangedInECV2() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - JsonObject payload = JsonParser.parseString(sealECV2(PLAINTEXT)).getAsJsonObject(); - JsonObject intermediateSigningKey = payload.get("intermediateSigningKey").getAsJsonObject(); - intermediateSigningKey.addProperty( - "signedKey", intermediateSigningKey.get("signedKey").getAsString() + " "); - payload.add("intermediateSigningKey", intermediateSigningKey); - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldThrowIfSignatureForSignedKeyIsIncorrectInECV2() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - JsonObject payload = JsonParser.parseString(sealECV2(PLAINTEXT)).getAsJsonObject(); - JsonObject intermediateSigningKey = payload.get("intermediateSigningKey").getAsJsonObject(); - JsonArray signatures = intermediateSigningKey.get("signatures").getAsJsonArray(); - String correctSignature = signatures.get(0).getAsString(); - byte[] wrongSignatureBytes = Base64.decode(correctSignature); - wrongSignatureBytes[0] = (byte) ~wrongSignatureBytes[0]; - JsonArray newSignatures = new JsonArray(); - newSignatures.add(Base64.encode(wrongSignatureBytes)); - intermediateSigningKey.add("signatures", newSignatures); - payload.add("intermediateSigningKey", intermediateSigningKey); - - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldTryVerifyingAllSignaturesForSignedKeyInECV2() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - JsonObject payload = JsonParser.parseString(sealECV2(PLAINTEXT)).getAsJsonObject(); - JsonObject intermediateSigningKey = payload.get("intermediateSigningKey").getAsJsonObject(); - JsonArray signatures = intermediateSigningKey.get("signatures").getAsJsonArray(); - String correctSignature = signatures.get(0).getAsString(); - byte[] wrongSignatureBytes = Base64.decode(correctSignature); - wrongSignatureBytes[0] = (byte) ~wrongSignatureBytes[0]; - JsonArray newSignatures = new JsonArray(); - newSignatures.add(Base64.encode(wrongSignatureBytes)); - newSignatures.add(correctSignature); - intermediateSigningKey.add("signatures", newSignatures); - payload.add("intermediateSigningKey", intermediateSigningKey); - - assertEquals(PLAINTEXT, recipient.unseal(sealECV2(PLAINTEXT))); - } - - @Test - public void testShouldThrowIfECV2UseWrongRecipientId() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId("not" + RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - try { - recipient.unseal(sealECV2(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldAcceptNonExpiredECV2Message() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - JsonObject payload = new JsonObject(); - payload.addProperty( - "messageExpiration", - // One day in the future - String.valueOf(Instant.now().plus(Duration.standardDays(1)).getMillis())); - String plaintext = payload.toString(); - assertEquals(plaintext, recipient.unseal(sealECV2(plaintext))); - } - - @Test - public void testShouldFailIfECV2MessageIsExpired() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - JsonObject payload = new JsonObject(); - payload.addProperty( - "messageExpiration", - // One day in the past - String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())); - - String ciphertext = sealECV2(payload.toString()); - try { - recipient.unseal(ciphertext); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("expired payload", e.getMessage()); - } - } - - @Test - public void testShouldFailIfIntermediateSigningKeyIsExpiredInECV2() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - PaymentMethodTokenSender sender = - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateCert( - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - // Expiration date in the past. - .expiration(Instant.now().minus(Duration.standardDays(1)).getMillis()) - .build() - .create()) - .recipientId(RECIPIENT_ID) - .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) - .build(); - - try { - recipient.unseal(sender.seal(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("expired intermediateSigningKey", e.getMessage()); - } - } - - private static String sealECV2(String plaintext) throws GeneralSecurityException { - return new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderIntermediateSigningKey(GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateCert( - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .expiration(Instant.now().plus(Duration.standardDays(1)).getMillis()) - .build() - .create()) - .recipientId(RECIPIENT_ID) - .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) - .build() - .seal(plaintext); - } - - @Test - public void testVerifyECV2SigningOnly() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(signECV2SigningOnly(PLAINTEXT))); - } - - @Test - public void testShouldFailIfSignedMessageWasChangedInECV2SigningOnly() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .build(); - JsonObject payload = JsonParser.parseString(signECV2SigningOnly(PLAINTEXT)).getAsJsonObject(); - payload.addProperty( - PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY, - payload.get(PaymentMethodTokenConstants.JSON_SIGNED_MESSAGE_KEY).getAsString() + " "); - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldThrowIfECV2SigningOnlyUseWrongSenderId() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .senderId("not-" + PaymentMethodTokenConstants.GOOGLE_SENDER_ID) - .build(); - - try { - recipient.unseal(signECV2SigningOnly(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldFailIfVerifyingWithDifferentKeyECV2SigningOnly() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject key = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY).getAsJsonObject(); - key.addProperty("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); - JsonArray newKeys = new JsonArray(); - newKeys.add(key); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .build(); - - try { - recipient.unseal(signECV2SigningOnly(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldFailIfTrustedKeyIsExpiredInECV2SigningOnly() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject key = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY).getAsJsonObject(); - key.addProperty( - "keyExpiration", // One day in the past - String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())); - JsonArray newKeys = new JsonArray(); - newKeys.add(key); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .build(); - - try { - recipient.unseal(signECV2SigningOnly(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("no trusted keys are available for this protocol version", e.getMessage()); - } - } - - @Test - public void testShouldFailIfKeyExpirationIsMissingInTrustedKeyECV2SigningOnly() throws Exception { - // Key expiration is required for ECv2SigningOnly - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject key = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY).getAsJsonObject(); - key.remove("keyExpiration"); - JsonArray newKeys = new JsonArray(); - newKeys.add(key); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .build(); - - try { - recipient.unseal(signECV2SigningOnly(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("no trusted keys are available for this protocol version", e.getMessage()); - } - } - - @Test - public void testShouldTryAllKeysToVerifySignatureECV2SigningOnly() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - JsonArray keys = trustedKeysJson.get("keys").getAsJsonArray(); - JsonObject correctKey = keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY).getAsJsonObject(); - JsonObject wrongKey = - keys.get(INDEX_OF_GOOGLE_SIGNING_EC_V2_SIGNING_ONLY).getAsJsonObject().deepCopy(); - wrongKey.addProperty("keyValue", ALTERNATE_PUBLIC_SIGNING_KEY); - JsonArray newKeys = new JsonArray(); - newKeys.add(wrongKey); - newKeys.add(correctKey); - trustedKeysJson.add("keys", newKeys); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(trustedKeysJson.toString()) - .recipientId(RECIPIENT_ID) - .build(); - - assertEquals(PLAINTEXT, recipient.unseal(signECV2SigningOnly(PLAINTEXT))); - } - - @Test - public void testShouldFailIfSignedKeyWasChangedInECV2SigningOnly() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .build(); - - JsonObject payload = JsonParser.parseString(signECV2SigningOnly(PLAINTEXT)).getAsJsonObject(); - JsonObject intermediateSigningKey = payload.get("intermediateSigningKey").getAsJsonObject(); - intermediateSigningKey.addProperty( - "signedKey", intermediateSigningKey.get("signedKey").getAsString() + " "); - payload.add("intermediateSigningKey", intermediateSigningKey); - - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldThrowIfSignatureForSignedKeyIsIncorrectInECV2SigningOnly() - throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .build(); - JsonObject payload = JsonParser.parseString(signECV2SigningOnly(PLAINTEXT)).getAsJsonObject(); - JsonArray signatures = - payload.get("intermediateSigningKey").getAsJsonObject().get("signatures").getAsJsonArray(); - String correctSignature = signatures.get(0).getAsString(); - byte[] wrongSignatureBytes = Base64.decode(correctSignature); - wrongSignatureBytes[0] = (byte) ~wrongSignatureBytes[0]; - JsonArray newSignatures = new JsonArray(); - newSignatures.add(Base64.encode(wrongSignatureBytes)); - JsonObject intermediateSigningKey = payload.get("intermediateSigningKey").getAsJsonObject(); - intermediateSigningKey.add("signatures", newSignatures); - payload.add("intermediateSigningKey", intermediateSigningKey); - - try { - recipient.unseal(payload.toString()); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldTryVerifyingAllSignaturesForSignedKeyInECV2SigningOnly() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .build(); - JsonObject payload = JsonParser.parseString(signECV2SigningOnly(PLAINTEXT)).getAsJsonObject(); - JsonArray signatures = - payload.get("intermediateSigningKey").getAsJsonObject().get("signatures").getAsJsonArray(); - String correctSignature = signatures.get(0).getAsString(); - byte[] wrongSignatureBytes = Base64.decode(correctSignature); - wrongSignatureBytes[0] = (byte) ~wrongSignatureBytes[0]; - JsonArray newSignatures = new JsonArray(); - newSignatures.add(Base64.encode(wrongSignatureBytes)); - newSignatures.add(correctSignature); - JsonObject intermediateSigningKey = payload.get("intermediateSigningKey").getAsJsonObject(); - intermediateSigningKey.add("signatures", newSignatures); - payload.add("intermediateSigningKey", intermediateSigningKey); - - assertEquals(PLAINTEXT, recipient.unseal(signECV2SigningOnly(PLAINTEXT))); - } - - @Test - public void testShouldThrowIfECV2SigningOnlyUseWrongRecipientId() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId("not" + RECIPIENT_ID) - .build(); - - try { - recipient.unseal(signECV2SigningOnly(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("cannot verify signature", e.getMessage()); - } - } - - @Test - public void testShouldAcceptNonExpiredECV2SigningOnlyMessage() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .build(); - - JsonObject payload = new JsonObject(); - payload.addProperty( - "messageExpiration", - // One day in the future - String.valueOf(Instant.now().plus(Duration.standardDays(1)).getMillis())); - String plaintext = payload.toString(); - assertEquals(plaintext, recipient.unseal(signECV2SigningOnly(plaintext))); - } - - @Test - public void testShouldFailIfECV2SigningOnlyMessageIsExpired() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .build(); - - JsonObject payload = new JsonObject(); - payload.addProperty( - "messageExpiration", - // One day in the past - String.valueOf(Instant.now().minus(Duration.standardDays(1)).getMillis())); - String ciphertext = signECV2SigningOnly(payload.toString()); - try { - recipient.unseal(ciphertext); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("expired payload", e.getMessage()); - } - } - - @Test - public void testShouldFailIfIntermediateSigningKeyIsExpiredInECV2SigningOnly() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .build(); - PaymentMethodTokenSender sender = - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateCert( - new SenderIntermediateCertFactory.Builder() - .protocolVersion( - PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - // Expiration date in the past. - .expiration(Instant.now().minus(Duration.standardDays(1)).getMillis()) - .build() - .create()) - .recipientId(RECIPIENT_ID) - .build(); - - try { - recipient.unseal(sender.seal(PLAINTEXT)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("expired intermediateSigningKey", e.getMessage()); - } - } - - private static String signECV2SigningOnly(String plaintext) throws GeneralSecurityException { - return new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateCert( - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .expiration(Instant.now().plus(Duration.standardDays(1)).getMillis()) - .build() - .create()) - .recipientId(RECIPIENT_ID) - .build() - .seal(plaintext); - } -}
diff --git a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenSenderTest.java b/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenSenderTest.java deleted file mode 100644 index 5ca98a0..0000000 --- a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/PaymentMethodTokenSenderTest.java +++ /dev/null
@@ -1,577 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import com.google.crypto.tink.subtle.EllipticCurves; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECParameterSpec; -import org.joda.time.Duration; -import org.joda.time.Instant; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@code PaymentMethodTokenSender}. */ -@RunWith(JUnit4.class) -public class PaymentMethodTokenSenderTest { - private static final String MERCHANT_PUBLIC_KEY_BASE64 = - "BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y="; - /** - * Created with: - * - * <pre> - * openssl pkcs8 -topk8 -inform PEM -outform PEM -in merchant-key.pem -nocrypt - * </pre> - */ - private static final String MERCHANT_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgCPSuFr4iSIaQprjj" - + "chHPyDu2NXFe0vDBoTpPkYaK9dehRANCAATnaFz/vQKuO90pxsINyVNWojabHfbx" - + "9qIJ6uD7Q7ZSxmtyo/Ez3/o2kDT8g0pIdyVIYktCsq65VoQIDWSh2Bdm"; - - /** Sample Google provided JSON with its public signing keys. */ - private static final String GOOGLE_VERIFYING_PUBLIC_KEYS_JSON = - "{\n" - + " \"keys\": [\n" - + " {\n" - + " \"keyValue\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPYnHwS8uegWAewQtlxizmLFynw" - + "HcxRT1PK07cDA6/C4sXrVI1SzZCUx8U8S0LjMrT6ird/VW7be3Mz6t/srtRQ==\",\n" - + " \"protocolVersion\": \"ECv1\"\n" - + " },\n" - + " {\n" - + " \"keyValue\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM" - + "43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw==\",\n" - + " \"keyExpiration\": \"" - + Instant.now().plus(Duration.standardDays(1)).getMillis() - + "\",\n" - + " \"protocolVersion\": \"ECv2\"\n" - + " },\n" - + " {\n" - + " \"keyValue\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENXvYqxD5WayKYhuXQevdGdLA8i" - + "fV4LsRS2uKvFo8wwyiwgQHB9DiKzG6T/P1Fu9Bl7zWy/se5Dy4wk1mJoPuxg==\",\n" - + " \"keyExpiration\": \"" - + Instant.now().plus(Duration.standardDays(1)).getMillis() - + "\",\n" - + " \"protocolVersion\": \"ECv2SigningOnly\"\n" - + " }\n" - + " ]\n" - + "}"; - - /** - * Sample Google private signing key for the ECv1 protocolVersion. - * - * <p>Corresponds to the ECv1 private key of the key in {@link - * #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON}. - */ - private static final String GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZj/Dldxz8fvKVF5O" - + "TeAtK6tY3G1McmvhMppe6ayW6GahRANCAAQ9icfBLy56BYB7BC2XGLOYsXKfAdzF" - + "FPU8rTtwMDr8LixetUjVLNkJTHxTxLQuMytPqKt39Vbtt7czPq3+yu1F"; - - /** - * Sample Google private signing key for the ECv2 protocolVersion. - * - * <p>Corresponds to ECv2 private key of the key in {@link #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON}. - */ - private static final String GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKvEdSS8f0mjTCNKev" - + "aKXIzfNC5b4A104gJWI9TsLIMqhRANCAAT/X7ccFVJt2/6Ps1oCt2AzKhIAz" - + "jfJHJ3Op2DVPGh1LMD3oOPgxzUSIqujHG6dq9Ui93Eacl4VWJPMW/MVHHIL"; - - /** - * Sample Google intermediate public signing key for the ECv2 protocolVersion. - * - * <p>Base64 version of the public key encoded in ASN.1 type SubjectPublicKeyInfo defined in the - * X.509 standard. - * - * <p>The intermediate public key will be signed by {@link - * #GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64}. - */ - private static final String GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64 = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yR" - + "ydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw=="; - - /** - * Sample Google intermediate private signing key for the ECv2 protocolVersion. - * - * <p>Corresponds to private key of the key in {@link - * #GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64}. - */ - private static final String GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKvEdSS8f0mjTCNKev" - + "aKXIzfNC5b4A104gJWI9TsLIMqhRANCAAT/X7ccFVJt2/6Ps1oCt2AzKhIAz" - + "jfJHJ3Op2DVPGh1LMD3oOPgxzUSIqujHG6dq9Ui93Eacl4VWJPMW/MVHHIL"; - - /** - * Sample Google private signing key for the ECV2SigningOnly protocolVersion. - * - * <p>Corresponds to ECV2SigningOnly private key of the key in {@link - * #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON}. - */ - private static final String GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgRi9hSdY+knJ08odnY" - + "tZFMRi7ZYeMoasAijLhD4GiQ1yhRANCAAQ1e9irEPlZrIpiG5dB690Z0sDy" - + "J9XguxFLa4q8WjzDDKLCBAcH0OIrMbpP8/UW70GXvNbL+x7kPLjCTWYmg+7G"; - - /** - * Sample Google intermediate public signing key for the ECV2SigningOnly protocolVersion. - * - * <p>Base64 version of the public key encoded in ASN.1 type SubjectPublicKeyInfo defined in the - * X.509 standard. - * - * <p>The intermediate public key will be signed by {@link - * #GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_PRIVATE_KEY_PKCS8_BASE64}. - */ - private static final String - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PUBLIC_KEY_X509_BASE64 = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8OaurwvbyYm8JWDgFPRTIDg0/" - + "kcQTFAQ4txi5IP0AyM1QiagwRhDUfjpqZkpw8xt/DXwyWYM0DdHqoeV" - + "TKqmYQ=="; - - /** - * Sample Google intermediate private signing key for the ECV2SigningOnly protocolVersion. - * - * <p>Corresponds to private key of the key in {@link - * #GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PUBLIC_KEY_X509_BASE64}. - */ - private static final String - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+Jvpkq26tpZ0s" - + "TTZVh4teEI41SnJdmkBzM8VZ5ZirE2hRANCAATw5q6vC9vJibwlYOAU" - + "9FMgODT+RxBMUBDi3GLkg/QDIzVCJqDBGENR+OmpmSnDzG38NfDJZgz" - + "QN0eqh5VMqqZh"; - - private static final String RECIPIENT_ID = "someRecipient"; - - @Test - public void testECV1WithPrecomputedKeys() throws Exception { - PaymentMethodTokenSender sender = - new PaymentMethodTokenSender.Builder() - .senderSigningKey(GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64) - .recipientId(RECIPIENT_ID) - .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) - .build(); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - String plaintext = "blah"; - assertEquals(plaintext, recipient.unseal(sender.seal(plaintext))); - } - - @Test - public void testECV1WithTestdataSeal() throws Exception { - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - // Seal where "=" has been escaped by \u003d. - // - tag is "bkd9lJMV/8Na34/9ZtZmDmgrZcxJ71ALhT/KpraqzR8=", = gets escapced by \u003d, so - // signedMessage contains "tag": "bkd9lJMV/8Na34/9ZtZmDmgrZcxJ71ALhT/KpraqzR8\u003d" - // - this gets escaped in the 2nd encoding with \ -> \\ and " -> \". So the final seal - // contains \"tag\":\"bkd9lJMV/8Na34/9ZtZmDmgrZcxJ71ALhT/KpraqzR8\\u003d\" - // Note that to encode this string in Java, we have to escape \ -> \\ and " -> \" again. - String testdataSeal = - "{\"signedMessage\":\"{\\\"tag\\\":\\\"bkd9lJMV/8Na34/9ZtZmDmgrZcxJ71ALhT/KpraqzR8\\\\u003d" - + "\\\",\\\"ephemeralPublicKey\\\":\\\"BLyk1Iwcx+yRQbCSsZgAwb8s/ChNDTlcCovgL5/37qDdia3x" - + "PM5GUb+HmbW9bF/c12p0ySxtU/MDcNkJZ0nWCVs\\\\u003d\\\",\\\"encryptedMessage\\\":\\\"ue" - + "63Gg\\\\u003d\\\\u003d\\\"}\",\"protocolVersion\":\"ECv1\",\"signature\":\"MEQCIFFdW" - + "ve35+jh7CT9QC9W6Leqx32P41oNxG2NDm6PaY1fAiAKHvklWHDPiLKYPb0zyXRGIl6GYvfQ3LAl1z5sR3CbS" - + "w\\u003d\\u003d\"}"; - - // Seal where "=" has not been escaped, but " and \ have to be escaped as before. - String testdataSeal2 = - "{\"signedMessage\":\"{\\\"encryptedMessage\\\":\\\"Ih3e7g==\\\",\\\"tag\\\":\\\"GlZ332kL5u" - + "ZICWrCsSSQ6KrFFqQyKI84SH2Wh6UTv8c=\\\",\\\"ephemeralPublicKey\\\":\\\"BBb/VaSEYJphhs" - + "ma1X24QrjdFr/DHo7IDu8owi6NR0tW9p5F9jn6wxu5by5Rs/bYkVdr8HNT9+MEpBOnL7Si/dQ=\\\"}\",\"" - + "protocolVersion\":\"ECv1\",\"signature\":\"MEYCIQD6rOV4l9pm/MDr2jPkqnU2GSHbbnUaLYQow" - + "/0Y+S1axAIhAKUUO7N9eoBNuXySDDWDYrdu7r0IHxeeFtkubDxhplYU\"}"; - - // Seal where "=" has only been escaped in the outer encoding: - // - tag is "Jv2KH2lCVyNgGcQMbWSMf+N8RQCgTfkRq3J3f9qrBKE=", so - // signedMessage contains "tag": "Jv2KH2lCVyNgGcQMbWSMf+N8RQCgTfkRq3J3f9qrBKE=" - // - this gets escaped in the 2nd encoding with \ -> \\, " -> \" and = -> \u003d. So the final - // seal contains \"tag\":\"Jv2KH2lCVyNgGcQMbWSMf+N8RQCgTfkRq3J3f9qrBKE\u003d\" - // Note that to encode this string in Java, we have to escape \ -> \\ and " -> \" again. - String testdataSeal3 = - "{\"signedMessage\":\"{\\\"encryptedMessage\\\":\\\"eTBsng\\u003d\\u003d\\\",\\\"tag\\\":\\" - + "\"Jv2KH2lCVyNgGcQMbWSMf+N8RQCgTfkRq3J3f9qrBKE\\u003d\\\",\\\"ephemeralPublicKey\\\":" - + "\\\"BGPS6h2ddaOA+H71e28xZ2OQZBJuVrCK3qUXc6G6ykHTr8ab9pmS7B87n9jg8qwYAWpFcRNgC2fxnrPL" - + "+p2Gk5U\\u003d\\\"}\",\"protocolVersion\":\"ECv1\",\"signature\":\"MEUCIQC+8NTo1xbem" - + "5lPhXuEwcYc0W03jdj3Q1YNI4XvoVAbuAIgT5WXy1wZ2GNXoaLauNGo0iHeOjsZT3wYUziZPHkSAlk\\u003" - + "d\"}"; - - // Weird token where for no reason the characters { -> \u007b and s -> \u0073 have been escaped. - String testdataSeal4 = - "{\"\\u0073ignedMe\\u0073\\u0073age\":\"\\u007b\\\"encryptedMe\\\\u0073\\\\u0073age\\\":\\" - + "\"tfAPcA\\\\u003d\\\\u003d\\\",\\\"tag\\\":\\\"bB72KLdziA4Oca77w7g7a4fbKpgFXVrtUr56W" - + "NpIL6M\\\\u003d\\\",\\\"ephemeralPublicKey\\\":\\\"BKYBrgWkOP7gqCcIqRjT1t0HjMdbEIMeP" - + "82VtfC7IaCNbHgL9vojnQ/Yxy8Kutn+MCvG3gRdTxGN+Pwl+1QjM6w\\\\u003d\\\"}\",\"protocolVer" - + "\\u0073ion\":\"ECv1\",\"\\u0073ignature\":\"MEUCIHDW7JS51iy1LeyiNri7tNqe0HYdPwmDoi/M" - + "j2RD+3f1AiEA9FBSpqDhQzEvn849IrxXWQlIkuJdUfLE1NxCBQ8mCj0\\u003d\"}"; - - String plaintext = "blah"; - assertEquals(plaintext, recipient.unseal(testdataSeal)); - assertEquals(plaintext, recipient.unseal(testdataSeal2)); - assertEquals(plaintext, recipient.unseal(testdataSeal3)); - assertEquals(plaintext, recipient.unseal(testdataSeal4)); - } - - @Test - public void testECV2WithPrecomputedKeys() throws Exception { - PaymentMethodTokenSender sender = - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateCert( - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .expiration(Instant.now().plus(Duration.standardDays(1)).getMillis()) - .build() - .create()) - .recipientId(RECIPIENT_ID) - .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) - .build(); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .addRecipientPrivateKey(MERCHANT_PRIVATE_KEY_PKCS8_BASE64) - .build(); - - String plaintext = "blah"; - assertEquals(plaintext, recipient.unseal(sender.seal(plaintext))); - } - - @Test - public void testECV2SigningOnlyWithPrecomputedKeys() throws Exception { - PaymentMethodTokenSender sender = - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateCert( - new SenderIntermediateCertFactory.Builder() - .protocolVersion( - PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .expiration(Instant.now().plus(Duration.standardDays(1)).getMillis()) - .build() - .create()) - .recipientId(RECIPIENT_ID) - .build(); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderVerifyingKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .recipientId(RECIPIENT_ID) - .build(); - - String plaintext = "blah"; - assertEquals(plaintext, recipient.unseal(sender.seal(plaintext))); - } - - @Test - public void testShouldThrowWithUnsupportedProtocolVersion() throws Exception { - try { - new PaymentMethodTokenSender.Builder().protocolVersion("ECv99").build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals("invalid version: ECv99", expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfSignedIntermediateSigningKeyIsSetForECV1() throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1) - .senderSigningKey(GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateCert(newSignedIntermediateSigningKey()) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must not set signed sender's intermediate signing key using " - + "Builder.senderIntermediateCert", - expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfIntermediateSigningKeyIsSetForECV1() throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1) - .senderSigningKey(GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateSigningKey(GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must not set sender's intermediate signing key using " - + "Builder.senderIntermediateSigningKey", - expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfSenderSigningKeyIsSetForECV2() throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderSigningKey(GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must not set sender's signing key using Builder.senderSigningKey", - expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfSignedIntermediateSigningKeyIsNotSetForECV2() throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - // no calls to senderIntermediateCert - .senderIntermediateSigningKey(GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must set signed sender's intermediate signing key using " - + "Builder.senderIntermediateCert", - expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfIntermediateSigningKeyIsNotSetForECV2() throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - // no calls to senderIntermediateSigningKey - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must set sender's intermediate signing key using Builder.senderIntermediateSigningKey", - expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfSenderSigningKeyIsNotSetForECV1() throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1) - // no calls to senderSigningKey - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must set sender's signing key using Builder.senderSigningKey", expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfSenderSigningKeyIsSetForECV2SigningOnly() throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderSigningKey(GOOGLE_SIGNING_EC_V1_PRIVATE_KEY_PKCS8_BASE64) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must not set sender's signing key using Builder.senderSigningKey", - expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfSignedIntermediateSigningKeyIsNotSetForECV2SigningOnly() - throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - // no calls to senderIntermediateCert - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must set signed sender's intermediate signing key using " - + "Builder.senderIntermediateCert", - expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfRecipientPublicKeyIsSetForECV2SigningOnly() throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - // no calls to senderIntermediateCert - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PRIVATE_KEY_PKCS8_BASE64) - .rawUncompressedRecipientPublicKey(MERCHANT_PUBLIC_KEY_BASE64) - .senderIntermediateCert( - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateSigningKey( - GOOGLE_SIGNING_EC_V2_SIGNING_ONLY_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .expiration(Instant.now().plus(Duration.standardDays(1)).getMillis()) - .build() - .create()) - .recipientId(RECIPIENT_ID) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must not set recipient's public key using Builder.recipientPublicKey", - expected.getMessage()); - } - } - - @Test - public void testShouldThrowIfIntermediateSigningKeyIsNotSetForECV2SigningOnly() throws Exception { - try { - new PaymentMethodTokenSender.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - // no calls to senderIntermediateSigningKey - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must set sender's intermediate signing key using Builder.senderIntermediateSigningKey", - expected.getMessage()); - } - } - - @Test - public void testSendReceive() throws Exception { - ECParameterSpec spec = EllipticCurves.getNistP256Params(); - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - keyGen.initialize(spec); - String senderId = "foo"; - String recipientId = "bar"; - - KeyPair senderKey = keyGen.generateKeyPair(); - ECPublicKey senderPublicKey = (ECPublicKey) senderKey.getPublic(); - ECPrivateKey senderPrivateKey = (ECPrivateKey) senderKey.getPrivate(); - - KeyPair recipientKey = keyGen.generateKeyPair(); - ECPublicKey recipientPublicKey = (ECPublicKey) recipientKey.getPublic(); - ECPrivateKey recipientPrivateKey = (ECPrivateKey) recipientKey.getPrivate(); - - PaymentMethodTokenSender sender = - new PaymentMethodTokenSender.Builder() - .senderId(senderId) - .senderSigningKey(senderPrivateKey) - .recipientId(recipientId) - .recipientPublicKey(recipientPublicKey) - .build(); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderId(senderId) - .addSenderVerifyingKey(senderPublicKey) - .recipientId(recipientId) - .addRecipientPrivateKey(recipientPrivateKey) - .build(); - - String plaintext = "blah"; - assertEquals(plaintext, recipient.unseal(sender.seal(plaintext))); - } - - @Test - public void testWithKeysGeneratedByRecipientKeyGen() throws Exception { - ECParameterSpec spec = EllipticCurves.getNistP256Params(); - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - keyGen.initialize(spec); - String senderId = "foo"; - String recipientId = "bar"; - - KeyPair senderKey = keyGen.generateKeyPair(); - ECPublicKey senderPublicKey = (ECPublicKey) senderKey.getPublic(); - ECPrivateKey senderPrivateKey = (ECPrivateKey) senderKey.getPrivate(); - - // The keys here are generated by PaymentMethodTokenRecipientKeyGen. - String recipientPrivateKey = - "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAP5/1502pXYdMion22yiWK" - + "GoTBJN/wAAfdjBU6puyEMw=="; - String recipientPublicKey = - "BJ995jnw2Ppn4BMP/ZKtlTOOIBQC+/L3PDcFRjowZuCkRqUZ/kGWE8c+zimZNHOZPzLB" - + "NVGJ3V8M/fM4g4o02Mc="; - - PaymentMethodTokenSender sender = - new PaymentMethodTokenSender.Builder() - .senderId(senderId) - .senderSigningKey(senderPrivateKey) - .recipientId(recipientId) - .rawUncompressedRecipientPublicKey(recipientPublicKey) - .build(); - - PaymentMethodTokenRecipient recipient = - new PaymentMethodTokenRecipient.Builder() - .senderId(senderId) - .addSenderVerifyingKey(senderPublicKey) - .recipientId(recipientId) - .addRecipientPrivateKey(recipientPrivateKey) - .build(); - - String plaintext = "blah"; - assertEquals(plaintext, recipient.unseal(sender.seal(plaintext))); - } - - private String newSignedIntermediateSigningKey() throws GeneralSecurityException { - return new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64) - .senderIntermediateSigningKey(GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .expiration(Instant.now().plus(Duration.standardDays(1)).getMillis()) - .build() - .create(); - } -}
diff --git a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/SenderIntermediateCertFactoryTest.java b/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/SenderIntermediateCertFactoryTest.java deleted file mode 100644 index 566ebde..0000000 --- a/apps/paymentmethodtoken/src/test/java/com/google/crypto/tink/apps/paymentmethodtoken/SenderIntermediateCertFactoryTest.java +++ /dev/null
@@ -1,161 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.paymentmethodtoken; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import org.joda.time.Duration; -import org.joda.time.Instant; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Tests for {@link SenderIntermediateCertFactory}. */ -@RunWith(JUnit4.class) -public class SenderIntermediateCertFactoryTest { - - /** - * Sample Google private signing key for the ECv2 protocolVersion. - * - * <p>Base64 version of the private key encoded in PKCS8 format. - */ - private static final String GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKvEdSS8f0mjTCNKev" - + "aKXIzfNC5b4A104gJWI9TsLIMqhRANCAAT/X7ccFVJt2/6Ps1oCt2AzKhIAz" - + "jfJHJ3Op2DVPGh1LMD3oOPgxzUSIqujHG6dq9Ui93Eacl4VWJPMW/MVHHIL"; - - /** - * Sample Google intermediate public signing key for the ECv2 protocolVersion. - * - * <p>Base64 version of the public key encoded in ASN.1 type SubjectPublicKeyInfo defined in the - * X.509 standard. - */ - private static final String GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64 = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yR" - + "ydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw=="; - - @Test - public void shouldProduceSenderIntermediateCertJson() throws Exception { - String encoded = - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderIntermediateSigningKey(GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64) - .expiration(123456) - .build() - .create(); - - JsonObject decodedSignedIntermediateSigningKey = - JsonParser.parseString(encoded).getAsJsonObject(); - assertTrue(decodedSignedIntermediateSigningKey.get("signedKey").isJsonPrimitive()); - assertTrue(decodedSignedIntermediateSigningKey.get("signatures").isJsonArray()); - assertEquals(2, decodedSignedIntermediateSigningKey.size()); - - JsonObject signedKey = - JsonParser.parseString(decodedSignedIntermediateSigningKey.get("signedKey").getAsString()) - .getAsJsonObject(); - assertTrue(signedKey.get("keyValue").getAsJsonPrimitive().isString()); - assertTrue(signedKey.get("keyExpiration").getAsJsonPrimitive().isString()); - assertEquals(2, signedKey.size()); - assertEquals( - GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64, - signedKey.get("keyValue").getAsString()); - assertEquals("123456", signedKey.get("keyExpiration").getAsString()); - assertEquals(1, decodedSignedIntermediateSigningKey.get("signatures").getAsJsonArray().size()); - assertFalse( - decodedSignedIntermediateSigningKey - .get("signatures") - .getAsJsonArray() - .get(0) - .getAsString() - .isEmpty()); - } - - @Test - public void shouldThrowIfExpirationNotSet() throws Exception { - try { - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderIntermediateSigningKey(GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64) - // no expiration - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals("must set expiration using Builder.expiration", expected.getMessage()); - } - } - - @Test - public void shouldThrowIfExpirationIsNegative() throws Exception { - try { - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderIntermediateSigningKey(GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64) - .expiration(-1) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals("invalid negative expiration", expected.getMessage()); - } - } - - @Test - public void shouldThrowIfNoSenderSigningKeyAdded() throws Exception { - try { - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2) - .senderIntermediateSigningKey(GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - // no call to addSenderSigningKey - .expiration(Instant.now().plus(Duration.standardDays(1)).getMillis()) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals( - "must add at least one sender's signing key using Builder.addSenderSigningKey", - expected.getMessage()); - } - } - - @Test - public void shouldSupportECV2SigningOnly() throws Exception { - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V2_SIGNING_ONLY) - .senderIntermediateSigningKey(GOOGLE_SIGNING_EC_V2_INTERMEDIATE_PUBLIC_KEY_X509_BASE64) - .addSenderSigningKey(GOOGLE_SIGNING_EC_V2_PRIVATE_KEY_PKCS8_BASE64) - .expiration(123456) - .build(); - } - - @Test - public void shouldThrowIfInvalidProtocolVersionSet() throws Exception { - try { - new SenderIntermediateCertFactory.Builder() - .protocolVersion(PaymentMethodTokenConstants.PROTOCOL_VERSION_EC_V1) - .build(); - fail("Should have thrown!"); - } catch (IllegalArgumentException expected) { - assertEquals("invalid version: ECv1", expected.getMessage()); - } - } -}
diff --git a/apps/rewardedads/BUILD.bazel b/apps/rewardedads/BUILD.bazel deleted file mode 100644 index a8f1330..0000000 --- a/apps/rewardedads/BUILD.bazel +++ /dev/null
@@ -1,15 +0,0 @@ -load("@tink_java//tools:gen_maven_jar_rules.bzl", "gen_maven_jar_rules") - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -gen_maven_jar_rules( - name = "maven", - doctitle = "Tink Cryptography API for Google Mobile Rewarded Video Ads SSV", - manifest_lines = [ - "Automatic-Module-Name: com.google.crypto.tink.apps.rewardedads", - ], - root_packages = ["com.google.crypto.tink.apps.rewardedads"], - deps = ["//rewardedads/src/main/java/com/google/crypto/tink/apps/rewardedads:rewarded_ads_verifier"], -)
diff --git a/apps/rewardedads/README.md b/apps/rewardedads/README.md deleted file mode 100644 index b92e374..0000000 --- a/apps/rewardedads/README.md +++ /dev/null
@@ -1,55 +0,0 @@ -# An implementation of Google AdMob Rewarded Ads - -This app implements the verifier side of Server-Side Verification of Google -AdMob Rewarded Ads. - -## Latest Release - -The most recent release is -[1.7.0](https://github.com/google/tink/releases/tag/v1.7.0), released -2022-08-09. API docs can be found -[here](https://google.github.io/tink/javadoc/apps-rewardedads/1.7.0). - -The Maven group ID is `com.google.crypto.tink`, and the artifact ID is -`apps-rewardedads`. - -To add a dependency using Maven: - -```xml -<dependency> - <groupId>com.google.crypto.tink</groupId> - <artifactId>apps-rewardedads</artifactId> - <version>1.7.0</version> -</dependency> -``` - -## Snapshots - -Snapshots of this app built from the master branch are available through Maven -using version `HEAD-SNAPSHOT`. API docs can be found -[here](https://google.github.io/tink/javadoc/apps-rewardedads/HEAD-SNAPSHOT). - -To add a dependency using Maven: - -```xml -<repositories> -<repository> - <id>sonatype-snapshots</id> - <name>sonatype-snapshots</name> - <url>https://oss.sonatype.org/content/repositories/snapshots/</url> - <snapshots> - <enabled>true</enabled> - <updatePolicy>always</updatePolicy> - </snapshots> - <releases> - <updatePolicy>always</updatePolicy> - </releases> -</repository> -</repositories> - -<dependency> - <groupId>com.google.crypto.tink</groupId> - <artifactId>apps-rewardedads</artifactId> - <version>HEAD-SNAPSHOT</version> -</dependency> -```
diff --git a/apps/rewardedads/src/main/java/com/google/crypto/tink/apps/rewardedads/BUILD.bazel b/apps/rewardedads/src/main/java/com/google/crypto/tink/apps/rewardedads/BUILD.bazel deleted file mode 100644 index a44132c..0000000 --- a/apps/rewardedads/src/main/java/com/google/crypto/tink/apps/rewardedads/BUILD.bazel +++ /dev/null
@@ -1,17 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -java_library( - name = "rewarded_ads_verifier", - srcs = ["RewardedAdsVerifier.java"], - deps = [ - "@maven//:com_google_code_gson_gson", - "@maven//:com_google_http_client_google_http_client", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:ecdsa_verify_jce", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:enums", - "@tink_java//src/main/java/com/google/crypto/tink/util:keys_downloader", - ], -)
diff --git a/apps/rewardedads/src/main/java/com/google/crypto/tink/apps/rewardedads/RewardedAdsVerifier.java b/apps/rewardedads/src/main/java/com/google/crypto/tink/apps/rewardedads/RewardedAdsVerifier.java deleted file mode 100644 index 85d26df..0000000 --- a/apps/rewardedads/src/main/java/com/google/crypto/tink/apps/rewardedads/RewardedAdsVerifier.java +++ /dev/null
@@ -1,325 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.rewardedads; - -import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EcdsaVerifyJce; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding; -import com.google.crypto.tink.subtle.Enums.HashType; -import com.google.crypto.tink.util.KeysDownloader; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.Charset; -import java.security.GeneralSecurityException; -import java.security.interfaces.ECPublicKey; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -/** - * An implementation of the verifier side of Server-Side Verification of Google AdMob Rewarded Ads. - * - * <p>Typical usage: - * - * <pre>{@code - * RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder() - * .fetchVerifyingPublicKeysWith( - * RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD) - * .build(); - * String rewardUrl = ...; - * verifier.verify(rewardUrl); - * }</pre> - * - * <p>This usage ensures that you always have the latest public keys, even when the keys were - * recently rotated. It will fetch and cache the latest public keys from {#PUBLIC_KEYS_URL_PROD}. - * When the cache expires, it will re-fetch the public keys. When initializing your server, we also - * recommend that you call {@link KeysDownloader#refreshInBackground()} of {@link - * RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD} to proactively fetch the public keys. - * - * <p>If you've already downloaded the public keys and have other means to manage key rotation, you - * can use {@link RewardedAdsVerifier.Builder#setVerifyingPublicKeys} to set the public keys. The - * Builder also allows you to customize other properties. - */ -public final class RewardedAdsVerifier { - private static final Charset UTF_8 = Charset.forName("UTF-8"); - - /** Default HTTP transport used by this class. */ - private static final NetHttpTransport DEFAULT_HTTP_TRANSPORT = - new NetHttpTransport.Builder().build(); - - private static final Executor DEFAULT_BACKGROUND_EXECUTOR = Executors.newCachedThreadPool(); - private final List<VerifyingPublicKeysProvider> verifyingPublicKeysProviders; - - public static final String SIGNATURE_PARAM_NAME = "signature="; - public static final String KEY_ID_PARAM_NAME = "key_id="; - - /** URL to fetch keys for environment production. */ - public static final String PUBLIC_KEYS_URL_PROD = - "https://www.gstatic.com/admob/reward/verifier-keys.json"; - - /** URL to fetch keys for environment test. */ - public static final String PUBLIC_KEYS_URL_TEST = - "https://www.gstatic.com/admob/reward/verifier-keys-test.json"; - - /** - * Instance configured to talk to fetch keys from production environment (from {@link - * #PUBLIC_KEYS_URL_PROD}). - */ - public static final KeysDownloader KEYS_DOWNLOADER_INSTANCE_PROD = - new KeysDownloader(DEFAULT_BACKGROUND_EXECUTOR, DEFAULT_HTTP_TRANSPORT, PUBLIC_KEYS_URL_PROD); - /** - * Instance configured to talk to fetch keys from test environment (from {@link - * #PUBLIC_KEYS_URL_TEST}). - */ - public static final KeysDownloader KEYS_DOWNLOADER_INSTANCE_TEST = - new KeysDownloader(DEFAULT_BACKGROUND_EXECUTOR, DEFAULT_HTTP_TRANSPORT, PUBLIC_KEYS_URL_TEST); - - RewardedAdsVerifier(List<VerifyingPublicKeysProvider> verifyingPublicKeysProviders) - throws GeneralSecurityException { - if (verifyingPublicKeysProviders == null || verifyingPublicKeysProviders.isEmpty()) { - throw new IllegalArgumentException( - "must set at least one way to get verifying key using" - + " Builder.fetchVerifyingPublicKeysWith or Builder.setVerifyingPublicKeys"); - } - this.verifyingPublicKeysProviders = verifyingPublicKeysProviders; - } - - private RewardedAdsVerifier(Builder builder) throws GeneralSecurityException { - this(builder.verifyingPublicKeysProviders); - } - - /** - * Verifies that {@code rewardUrl} has a valid signature. - * - * <p>This method requires that the name of the last two query parameters of {@code rewardUrl} are - * {@link #SIGNATURE_PARAM_NAME} and {@link #KEY_ID_PARAM_NAME} in that order. - */ - public void verify(String rewardUrl) throws GeneralSecurityException { - URI uri; - try { - uri = new URI(rewardUrl); - } catch (URISyntaxException ex) { - throw new GeneralSecurityException(ex); - } - String queryString = uri.getQuery(); - int i = queryString.indexOf(SIGNATURE_PARAM_NAME); - if (i <= 0 || queryString.charAt(i - 1) != '&') { - throw new GeneralSecurityException( - "signature and key id must be the last two query parameters"); - } - byte[] tbsData = - queryString.substring(0, i - 1 /* i - 1 instead of i because of & */).getBytes(UTF_8); - - String sigAndKeyId = queryString.substring(i); - i = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME); - if (i == -1 || sigAndKeyId.charAt(i - 1) != '&') { - throw new GeneralSecurityException( - "signature and key id must be the last two query parameters"); - } - String sig = - sigAndKeyId.substring( - SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */); - - // We don't have to check that keyId is the last parameter, because the long conversion would - // fail anyway if there's any trailing data. - try { - long keyId = Long.parseLong(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length())); - verify(tbsData, keyId, Base64.urlSafeDecode(sig)); - } catch (NumberFormatException ex) { - throw new GeneralSecurityException("key_id must be a long"); - } - } - - private void verify(final byte[] tbs, long keyId, final byte[] signature) - throws GeneralSecurityException { - boolean foundKeyId = false; - for (VerifyingPublicKeysProvider provider : verifyingPublicKeysProviders) { - Map<Long, ECPublicKey> publicKeys = provider.get(); - if (publicKeys.containsKey(keyId)) { - foundKeyId = true; - ECPublicKey publicKey = publicKeys.get(keyId); - EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER); - verifier.verify(signature, tbs); - } - } - if (!foundKeyId) { - throw new GeneralSecurityException("cannot find verifying key with key id: " + keyId); - } - } - - /** Builder for RewardedAdsVerifier. */ - public static class Builder { - private final List<VerifyingPublicKeysProvider> verifyingPublicKeysProviders = - new ArrayList<>(); - - public Builder() {} - - /** - * Fetches verifying public keys of the sender using {@link KeysDownloader}. - * - * <p>This is the preferred method of specifying the verifying public keys. - */ - public Builder fetchVerifyingPublicKeysWith(final KeysDownloader downloader) - throws GeneralSecurityException { - this.verifyingPublicKeysProviders.add( - new VerifyingPublicKeysProvider() { - @Override - public Map<Long, ECPublicKey> get() throws GeneralSecurityException { - try { - return parsePublicKeysJson(downloader.download()); - } catch (IOException e) { - throw new GeneralSecurityException("Failed to fetch keys!", e); - } - } - }); - return this; - } - - /** - * Sets the trusted verifying public keys of the sender. - * - * <p><b>IMPORTANT</b>: Instead of using this method to set the verifying public keys of the - * sender, prefer calling {@link #fetchVerifyingPublicKeysWith} passing it an instance of {@link - * KeysDownloader}. It will take care of fetching fresh keys and caching in memory. Only use - * this method if you can't use {@link #fetchVerifyingPublicKeysWith} and be aware you will need - * to handle key rotations yourself. - * - * <p>The given string is a JSON object formatted like the following: - * - * <pre> - * { - * "keys": [ - * { - * keyId: 1916455855, - * pem: "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUaWMKcBHWdhUE+DncSIHhFCLLEln\nUs0LB9oanZ4K/FNICIM8ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw==\n-----END PUBLIC KEY-----" - * base64: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUaWMKcBHWdhUE+DncSIHhFCLLElnUs0LB9oanZ4K/FNICIM8ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw==" - * }, - * { - * keyId: 3901585526, - * pem: "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtxg2BsK/fllIeADtLspezS6YfHFWXZ8tiJncm8LDBa/NxEC84akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw==\n-----END PUBLIC KEY-----" - * base64: "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtxg2BsK/fllIeADtLspezS6YfHFWXZ8tiJncm8LDBa/NxEC84akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw==" - * }, - * ], - * } - * </pre> - * - * <p>Each public key will be a base64 (no wrapping, padded) version of the key encoded in ASN.1 - * type SubjectPublicKeyInfo defined in the X.509 standard. - */ - public Builder setVerifyingPublicKeys(final String publicKeysJson) - throws GeneralSecurityException { - this.verifyingPublicKeysProviders.add( - new VerifyingPublicKeysProvider() { - @Override - public Map<Long, ECPublicKey> get() throws GeneralSecurityException { - return parsePublicKeysJson(publicKeysJson); - } - }); - return this; - } - - /** - * Adds a verifying public key of the sender. - * - * <p><b>IMPORTANT</b>: Instead of using this method to set the verifying public keys of the - * sender, prefer calling {@link #fetchVerifyingPublicKeysWith} passing it an instance of {@link - * KeysDownloader}. It will take care of fetching fresh keys and caching in memory. Only use - * this method if you can't use {@link #fetchVerifyingPublicKeysWith} and be aware you will need - * to handle Google key rotations yourself. - * - * <p>The public key is a base64 (no wrapping, padded) version of the key encoded in ASN.1 type - * SubjectPublicKeyInfo defined in the X.509 standard. - * - * <p>Multiple keys may be added. This utility will then verify any message signed with any of - * the private keys corresponding to the public keys added. Adding multiple keys is useful for - * handling key rotation. - */ - public Builder addVerifyingPublicKey(final long keyId, final String val) - throws GeneralSecurityException { - this.verifyingPublicKeysProviders.add( - new VerifyingPublicKeysProvider() { - @Override - public Map<Long, ECPublicKey> get() throws GeneralSecurityException { - return Collections.singletonMap( - keyId, EllipticCurves.getEcPublicKey(Base64.decode(val))); - } - }); - return this; - } - - /** - * Adds a verifying public key of the sender. - * - * <p><b>IMPORTANT</b>: Instead of using this method to set the verifying public keys of the - * sender, prefer calling {@link #fetchVerifyingPublicKeysWith} passing it an instance of {@link - * KeysDownloader}. It will take care of fetching fresh keys and caching in memory. Only use - * this method if you can't use {@link #fetchVerifyingPublicKeysWith} and be aware you will need - * to handle Google key rotations yourself. - */ - public Builder addVerifyingPublicKey(final long keyId, final ECPublicKey val) - throws GeneralSecurityException { - this.verifyingPublicKeysProviders.add( - new VerifyingPublicKeysProvider() { - @Override - public Map<Long, ECPublicKey> get() throws GeneralSecurityException { - return Collections.singletonMap(keyId, val); - } - }); - return this; - } - - public RewardedAdsVerifier build() throws GeneralSecurityException { - return new RewardedAdsVerifier(this); - } - } - - private static Map<Long, ECPublicKey> parsePublicKeysJson(String publicKeysJson) - throws GeneralSecurityException { - Map<Long, ECPublicKey> publicKeys = new HashMap<>(); - try { - JsonArray keys = - JsonParser.parseString(publicKeysJson).getAsJsonObject().get("keys").getAsJsonArray(); - for (int i = 0; i < keys.size(); i++) { - JsonObject key = keys.get(i).getAsJsonObject(); - publicKeys.put( - key.get("keyId").getAsLong(), - EllipticCurves.getEcPublicKey(Base64.decode(key.get("base64").getAsString()))); - } - } catch (JsonParseException | IllegalStateException e) { - throw new GeneralSecurityException("failed to extract trusted signing public keys", e); - } - if (publicKeys.isEmpty()) { - throw new GeneralSecurityException("no trusted keys are available for this protocol version"); - } - return publicKeys; - } - - private interface VerifyingPublicKeysProvider { - Map<Long, ECPublicKey> get() throws GeneralSecurityException; - } -}
diff --git a/apps/rewardedads/src/test/BUILD.bazel b/apps/rewardedads/src/test/BUILD.bazel deleted file mode 100644 index 244de24..0000000 --- a/apps/rewardedads/src/test/BUILD.bazel +++ /dev/null
@@ -1,35 +0,0 @@ -load("@tink_java//tools:gen_java_test_rules.bzl", "gen_java_test_rules") - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -# Tests - -java_library( - name = "generator_test", - testonly = 1, - srcs = glob([ - "**/*.java", - ]), - deps = [ - "//rewardedads/src/main/java/com/google/crypto/tink/apps/rewardedads:rewarded_ads_verifier", - "@maven//:com_google_code_gson_gson", - "@maven//:com_google_http_client_google_http_client", - "@maven//:junit_junit", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:ecdsa_sign_jce", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:enums", - "@tink_java//src/main/java/com/google/crypto/tink/util:keys_downloader", - ], -) - -gen_java_test_rules( - test_files = glob([ - "**/*Test.java", - ]), - deps = [ - ":generator_test", - ], -)
diff --git a/apps/rewardedads/src/test/java/com/google/crypto/tink/apps/rewardedads/RewardedAdsVerifierTest.java b/apps/rewardedads/src/test/java/com/google/crypto/tink/apps/rewardedads/RewardedAdsVerifierTest.java deleted file mode 100644 index f0e636e..0000000 --- a/apps/rewardedads/src/test/java/com/google/crypto/tink/apps/rewardedads/RewardedAdsVerifierTest.java +++ /dev/null
@@ -1,371 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.rewardedads; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.google.api.client.testing.http.MockHttpTransport; -import com.google.api.client.testing.http.MockLowLevelHttpResponse; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EcdsaSignJce; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding; -import com.google.crypto.tink.subtle.Enums.HashType; -import com.google.crypto.tink.util.KeysDownloader; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import java.net.URI; -import java.nio.charset.Charset; -import java.security.GeneralSecurityException; -import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@code RewardedAdsVerifier}. */ -@RunWith(JUnit4.class) -public class RewardedAdsVerifierTest { - private static final Charset UTF_8 = Charset.forName("UTF-8"); - - /** Sample Google provided JSON with its public signing keys. */ - private static final String GOOGLE_VERIFYING_PUBLIC_KEYS_JSON = - "{\"keys\":[{" - + "\"keyId\":1234," - + "\"base64\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPYnHwS8uegWAewQt" - + "lxizmLFynwHcxRT1PK07cDA6/C4sXrVI1SzZCUx8U8S0LjMrT6ird/VW7be3Mz6t/srtRQ==\"" - + "}]}"; - - /** - * Sample Google private signing key. - * - * <p>Corresponds to private key of the key in {@link #GOOGLE_VERIFYING_PUBLIC_KEYS_JSON}. - */ - private static final String GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64 = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZj/Dldxz8fvKVF5O" - + "TeAtK6tY3G1McmvhMppe6ayW6GahRANCAAQ9icfBLy56BYB7BC2XGLOYsXKfAdzF" - + "FPU8rTtwMDr8LixetUjVLNkJTHxTxLQuMytPqKt39Vbtt7czPq3+yu1F"; - - // must match the value in GOOGLE_VERIFYING_PUBLIC_KEYS_JSON - private static final long KEY_ID = 1234; - private static final String REWARD_HOST_AND_PATH = "https://publisher.com/blah?"; - private static final String REWARD_QUERY = "foo1=bar1&foo2=bar2"; - private static final String REWARD_URL = REWARD_HOST_AND_PATH + REWARD_QUERY; - - private static final String ALTERNATE_PUBLIC_SIGNING_KEY = - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEU8E6JppGKFG40r5dDU1idHRN52NuwsemFzXZh1oUqh3bGUPgPioH+RoW" - + "nmVSUQz1WfM2426w9f0GADuXzpUkcw=="; - - private static String signUrl(String rewardUrl, String privateKey, long keyId) throws Exception { - EcdsaSignJce signer = - new EcdsaSignJce( - EllipticCurves.getEcPrivateKey(Base64.decode(privateKey)), - HashType.SHA256, - EcdsaEncoding.DER); - String queryString = new URI(rewardUrl).getQuery(); - return buildUrl(rewardUrl, signer.sign(queryString.getBytes(UTF_8)), keyId); - } - - private static String buildUrl(String rewardUrl, byte[] sig, long keyId) { - return new StringBuilder(rewardUrl) - .append("&") - .append(RewardedAdsVerifier.SIGNATURE_PARAM_NAME) - .append(Base64.urlSafeEncode(sig)) - .append("&") - .append(RewardedAdsVerifier.KEY_ID_PARAM_NAME) - .append(keyId) - .toString(); - } - - @Test - public void testShouldVerify() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - verifier.verify(signUrl(REWARD_URL, GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64, KEY_ID)); - } - - @Test - public void testShouldVerifyIfKeyIdIsLargerThanMaxInt() throws Exception { - long keyId = Integer.MAX_VALUE + 1; - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - - trustedKeysJson.getAsJsonArray("keys").get(0).getAsJsonObject().addProperty("keyId", keyId); - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(trustedKeysJson.toString()) - .build(); - verifier.verify(signUrl(REWARD_URL, GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64, keyId)); - } - - @Test - public void testShouldVerifyWithEncodedUrl() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - - String rewardUrl = "https://publisher.com/path?foo=hello%20world&bar=user%40gmail.com"; - String decodedQueryString = "foo=hello world&bar=user@gmail.com"; - EcdsaSignJce signer = - new EcdsaSignJce( - EllipticCurves.getEcPrivateKey(Base64.decode(GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64)), - HashType.SHA256, - EcdsaEncoding.DER); - byte[] signature = signer.sign(decodedQueryString.getBytes(UTF_8)); - String signedUrl = buildUrl(rewardUrl, signature, KEY_ID); - verifier.verify(signedUrl); - } - - @Test - public void testShouldDecryptV1WhenFetchingSenderVerifyingKeys() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .fetchVerifyingPublicKeysWith( - new KeysDownloader.Builder() - .setHttpTransport( - new MockHttpTransport.Builder() - .setLowLevelHttpResponse( - new MockLowLevelHttpResponse() - .setContent(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON)) - .build()) - .setUrl("https://someUrl" /* unused */) - .build()) - .build(); - - verifier.verify(signUrl(REWARD_URL, GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64, KEY_ID)); - } - - @Test - public void testShouldFailIfVerifyingWithDifferentKey() throws Exception { - JsonObject trustedKeysJson = - JsonParser.parseString(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON).getAsJsonObject(); - trustedKeysJson - .getAsJsonArray("keys") - .get(0) - .getAsJsonObject() - .addProperty("base64", ALTERNATE_PUBLIC_SIGNING_KEY); - - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(trustedKeysJson.toString()) - .build(); - - try { - verifier.verify(signUrl(REWARD_URL, GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64, KEY_ID)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("Invalid signature", e.getMessage()); - } - } - - @Test - public void testShouldFailIfKeyIdNotFound() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - - try { - verifier.verify(signUrl(REWARD_URL, GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64, KEY_ID + 1)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertTrue(e.getMessage().contains("cannot find verifying key with key id")); - } - } - - @Test - public void testShouldFailIfSignedMessageWasChanged() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - byte[] validSignedUrl = - signUrl(REWARD_URL, GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64, KEY_ID).getBytes(UTF_8); - for (int i = REWARD_HOST_AND_PATH.length(); i < REWARD_URL.length(); i++) { - byte[] modifiedUrl = Arrays.copyOf(validSignedUrl, validSignedUrl.length); - modifiedUrl[i] = (byte) (modifiedUrl[i] ^ 0xff); - try { - verifier.verify(new String(modifiedUrl, UTF_8)); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - assertEquals("Invalid signature", e.getMessage()); - } - } - } - - @Test - public void testShouldFailIfSignatureWasChanged() throws Exception { - EcdsaSignJce signer = - new EcdsaSignJce( - EllipticCurves.getEcPrivateKey(Base64.decode(GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64)), - HashType.SHA256, - EcdsaEncoding.DER); - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - - byte[] validSig = signer.sign(REWARD_URL.getBytes(UTF_8)); - for (int i = 0; i < validSig.length; i++) { - byte[] modifiedSig = Arrays.copyOf(validSig, validSig.length); - modifiedSig[i] = (byte) (modifiedSig[i] ^ 0xff); - String modifiedUrl = buildUrl(REWARD_URL, modifiedSig, KEY_ID); - try { - verifier.verify(modifiedUrl); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException e) { - // Expected. - System.out.println(e); - } - } - } - - @Test - public void testShouldFailWithoutSignature() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - try { - verifier.verify(REWARD_URL); - } catch (GeneralSecurityException e) { - assertEquals("signature and key id must be the last two query parameters", e.getMessage()); - } - } - - @Test - public void testShouldFailWithoutKeyId() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - try { - verifier.verify( - new StringBuilder(REWARD_URL) - .append("&") - .append(RewardedAdsVerifier.SIGNATURE_PARAM_NAME) - .append("foo") - .toString()); - } catch (GeneralSecurityException e) { - assertEquals("signature and key id must be the last two query parameters", e.getMessage()); - } - } - - @Test - public void testShouldFailWithSignatureAndKeyIdNotTheLastParameters() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - try { - verifier.verify( - new StringBuilder(REWARD_HOST_AND_PATH) - .append(RewardedAdsVerifier.SIGNATURE_PARAM_NAME) - .toString()); - } catch (GeneralSecurityException e) { - assertEquals("signature and key id must be the last two query parameters", e.getMessage()); - } - } - - @Test - public void testShouldFailWithSignatureAndKeyIdNotTheLastParameters2() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - try { - verifier.verify( - new StringBuilder(REWARD_HOST_AND_PATH) - .append(RewardedAdsVerifier.SIGNATURE_PARAM_NAME) - .append("foo") - .append("&") - .append(RewardedAdsVerifier.KEY_ID_PARAM_NAME) - .append("123") - .append("bar=baz") - .toString()); - } catch (GeneralSecurityException e) { - assertEquals("signature and key id must be the last two query parameters", e.getMessage()); - } - } - - @Test - public void testShouldFailWithSignatureAndKeyIdNotTheLastParameters3() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - try { - verifier.verify( - new StringBuilder(REWARD_URL) - .append("&") - .append(RewardedAdsVerifier.SIGNATURE_PARAM_NAME) - .append("foo") - .append("&") - .append(RewardedAdsVerifier.KEY_ID_PARAM_NAME) - .append("123") - .append("&bar=baz") // this would be interpreted as part of the key ID - .toString()); - } catch (GeneralSecurityException e) { - assertEquals("key_id must be a long", e.getMessage()); - } - } - - @Test - public void testShouldFailWithSignatureAndKeyIdNoAmpersand() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - try { - verifier.verify( - new StringBuilder(REWARD_URL) - .append("&") - .append(RewardedAdsVerifier.SIGNATURE_PARAM_NAME) - .append("foo") - .append(RewardedAdsVerifier.KEY_ID_PARAM_NAME) - .append("123") - .toString()); - } catch (GeneralSecurityException e) { - assertEquals("signature and key id must be the last two query parameters", e.getMessage()); - } - } - - @Test - public void testShouldFailWithKeyIdNotLong() throws Exception { - RewardedAdsVerifier verifier = - new RewardedAdsVerifier.Builder() - .setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON) - .build(); - try { - verifier.verify( - new StringBuilder(REWARD_URL) - .append("&") - .append(RewardedAdsVerifier.SIGNATURE_PARAM_NAME) - .append("foo") - .append("&") - .append(RewardedAdsVerifier.KEY_ID_PARAM_NAME) - .append("not_long") - .toString()); - } catch (GeneralSecurityException e) { - assertEquals("key_id must be a long", e.getMessage()); - } - } -}
diff --git a/apps/webpush/BUILD.bazel b/apps/webpush/BUILD.bazel deleted file mode 100644 index 7260048..0000000 --- a/apps/webpush/BUILD.bazel +++ /dev/null
@@ -1,20 +0,0 @@ -load("@tink_java//tools:gen_maven_jar_rules.bzl", "gen_maven_jar_rules") - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -gen_maven_jar_rules( - name = "maven", - doctitle = "Tink Cryptography API for Message Encryption for Web Push (RFC 8291)", - manifest_lines = [ - "Automatic-Module-Name: com.google.crypto.tink.apps.webpush", - ], - root_packages = ["com.google.crypto.tink.apps.webpush"], - deps = [ - "//webpush/src/main/java/com/google/crypto/tink/apps/webpush:web_push_constants", - "//webpush/src/main/java/com/google/crypto/tink/apps/webpush:web_push_hybrid_decrypt", - "//webpush/src/main/java/com/google/crypto/tink/apps/webpush:web_push_hybrid_encrypt", - "//webpush/src/main/java/com/google/crypto/tink/apps/webpush:web_push_util", - ], -)
diff --git a/apps/webpush/README.md b/apps/webpush/README.md deleted file mode 100644 index 1018f4d..0000000 --- a/apps/webpush/README.md +++ /dev/null
@@ -1,63 +0,0 @@ -# Message Encryption for Web Push - -This Tink app is an implementation of [RFC 8291 - Message Encryption for Web -Push](https://tools.ietf.org/html/rfc8291). - -The most recent release is -[1.7.0](https://github.com/google/tink/releases/tag/v1.7.0), released -2022-08-09. API docs can be found -[here](https://google.github.io/tink/javadoc/apps-webpush/1.7.0). - -## Installation - -To add a dependency using Maven: - -```xml -<dependency> - <groupId>com.google.crypto.tink</groupId> - <artifactId>apps-webpush</artifactId> - <version>1.7.0</version> -</dependency> -``` - -To add a dependency using Gradle: - -``` -dependencies { - implementation 'com.google.crypto.tink:apps-webpush:1.7.0' -} -``` - -## Encryption - -```java -import com.google.crypto.tink.HybridEncrypt; -import java.security.interfaces.ECPublicKey; - -ECPublicKey reicipientPublicKey = ...; -byte[] authSecret = ...; -HybridEncrypt hybridEncrypt = new WebPushHybridEncrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(recipientPublicKey) - .build(); -byte[] plaintext = ...; -byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null /* contextInfo, must be null */); -``` - -## Decryption - -```java -import com.google.crypto.tink.HybridDecrypt; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; - -ECPrivateKey recipientPrivateKey = ...; -ECPublicKey recipientPublicKey = ...; -HybridDecrypt hybridDecrypt = new WebPushHybridDecrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(recipientPublicKey) - .withRecipientPrivateKey(recipientPrivateKey) - .build(); -byte[] ciphertext = ...; -byte[] plaintext = hybridDecrypt.decrypt(ciphertext, /* contextInfo, must be null */); -```
diff --git a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/BUILD.bazel b/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/BUILD.bazel deleted file mode 100644 index 5d2afbb..0000000 --- a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/BUILD.bazel +++ /dev/null
@@ -1,44 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -java_library( - name = "web_push_hybrid_decrypt", - srcs = ["WebPushHybridDecrypt.java"], - deps = [ - ":web_push_constants", - ":web_push_util", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_decrypt", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:subtle_util_cluster", - ], -) - -java_library( - name = "web_push_util", - srcs = ["WebPushUtil.java"], - deps = [ - ":web_push_constants", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:bytes", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:hkdf", - ], -) - -java_library( - name = "web_push_constants", - srcs = ["WebPushConstants.java"], - deps = ["@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves"], -) - -java_library( - name = "web_push_hybrid_encrypt", - srcs = ["WebPushHybridEncrypt.java"], - deps = [ - ":web_push_constants", - ":web_push_util", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_encrypt", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:random", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:subtle_util_cluster", - ], -)
diff --git a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushConstants.java b/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushConstants.java deleted file mode 100644 index c864021..0000000 --- a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushConstants.java +++ /dev/null
@@ -1,74 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.webpush; - -import com.google.crypto.tink.subtle.EllipticCurves; -import java.nio.charset.Charset; - -/** Various constants. */ -final class WebPushConstants { - static final Charset UTF_8 = Charset.forName("UTF-8"); - static final int AUTH_SECRET_SIZE = 16; - static final int IKM_SIZE = 32; - static final int CEK_KEY_SIZE = 16; - static final int NONCE_SIZE = 12; - static final byte[] IKM_INFO = - new byte[] {'W', 'e', 'b', 'P', 'u', 's', 'h', ':', ' ', 'i', 'n', 'f', 'o', (byte) 0}; - static final byte[] CEK_INFO = - new byte[] { - 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'E', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ':', ' ', - 'a', 'e', 's', '1', '2', '8', 'g', 'c', 'm', (byte) 0 - }; - static final byte[] NONCE_INFO = - new byte[] { - 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'E', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ':', ' ', - 'n', 'o', 'n', 'c', 'e', (byte) 0 - }; - - static final int SALT_SIZE = 16; - static final int RECORD_SIZE_LEN = 4; - static final int PUBLIC_KEY_SIZE_LEN = 1; - static final int PUBLIC_KEY_SIZE = 65; - // * salt: 16 - // * record size: 4 - // * public key size: 1 - // * uncompressed public key: 65 - // * total : 86 - static final int CONTENT_CODING_HEADER_SIZE = - SALT_SIZE + RECORD_SIZE_LEN + PUBLIC_KEY_SIZE_LEN + PUBLIC_KEY_SIZE; - - // the byte 0x2 separating the payload and the padding - static final byte PADDING_DELIMITER_BYTE = (byte) 2; - static final int PADDING_DELIMETER_SIZE = 1; - static final int DEFAULT_PADDING_SIZE = 0; - static final int TAG_SIZE = 16; - // * content coding header: 86 - // * padding delimeter: 1 - // * AES-GCM tag size: 16 - // * Total: 103 - static final int CIPHERTEXT_OVERHEAD = - CONTENT_CODING_HEADER_SIZE + PADDING_DELIMETER_SIZE + DEFAULT_PADDING_SIZE + TAG_SIZE; - - static final int MAX_CIPHERTEXT_SIZE = 4096; - - static final String HMAC_SHA256 = "HMACSHA256"; - static final EllipticCurves.PointFormatType UNCOMPRESSED_POINT_FORMAT = - EllipticCurves.PointFormatType.UNCOMPRESSED; - static final EllipticCurves.CurveType NIST_P256_CURVE_TYPE = EllipticCurves.CurveType.NIST_P256; - - private WebPushConstants() {} -}
diff --git a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushHybridDecrypt.java b/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushHybridDecrypt.java deleted file mode 100644 index 2062c54..0000000 --- a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushHybridDecrypt.java +++ /dev/null
@@ -1,278 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.webpush; - -import com.google.crypto.tink.HybridDecrypt; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.EngineFactory; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECPoint; -import java.util.Arrays; -import javax.crypto.Cipher; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * A {@link HybridDecrypt} implementation for the hybrid encryption used in <a - * href="https://tools.ietf.org/html/rfc8291">RFC 8291 - Web Push Message Encryption</a>. - * - * <h3>Ciphertext format</h3> - * - * <p>When used with <a href="https://tools.ietf.org/html/rfc8291#section-4">AES128-GCM content - * encoding</a>, which is the only content encoding supported in this implementation, the ciphertext - * is formatted according to RFC 8188 section 2, and looks as follows - * - * <pre> - * // NOLINTNEXTLINE - * +-----------+----------------+------------------+--------------------------------------------------- - * | salt (16) | recordsize (4) | publickeylen (1) | publickey (publickeylen) | aes128-gcm-ciphertext | - * +-----------+----------------+------------------+--------------------------------------------------- - * </pre> - * - * <p>RFC 8188 divides messages into records which are encrypted independently. Web Push messages - * cannot be longer than 3993 bytes, and are always encrypted in a single record with default size - * of 4096 bytes. {@code aes128-gcm-ciphertext} is the encryption of the message padded with a - * single byte of value {@code 0x02} (which indicates that this is the last and only record). - * - * <h3>Usage</h3> - * - * <pre>{@code - * import com.google.crypto.tink.HybridDecrypt; - * import com.google.crypto.tink.HybridEncrypt; - * import java.security.interfaces.ECPrivateKey; - * import java.security.interfaces.ECPublicKey; - * - * // Encryption. - * ECPublicKey reicipientPublicKey = ...; - * byte[] authSecret = ...; - * HybridEncrypt hybridEncrypt = new WebPushHybridEncrypt.Builder() - * .withAuthSecret(authSecret) - * .withRecipientPublicKey(recipientPublicKey) - * .build(); - * byte[] plaintext = ...; - * byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null); - * - * // Decryption. - * ECPrivateKey recipientPrivateKey = ...; - * HybridDecrypt hybridDecrypt = new WebPushHybridDecrypt.Builder() - * .withAuthSecret(authSecret) - * .withRecipientPublicKey(recipientPublicKey) - * .withRecipientPrivateKey(recipientPrivateKey) - * .build(); - * byte[] plaintext = hybridDecrypt.decrypt(ciphertext, null); - * }</pre> - * - * @since 1.1.0 - */ -public final class WebPushHybridDecrypt implements HybridDecrypt { - private final ECPrivateKey recipientPrivateKey; - private final byte[] recipientPublicKey; - private final byte[] authSecret; - private final int recordSize; - - private WebPushHybridDecrypt(Builder builder) throws GeneralSecurityException { - if (builder.recipientPrivateKey == null) { - throw new IllegalArgumentException( - "must set recipient's private key with Builder.withRecipientPrivateKey"); - } - this.recipientPrivateKey = builder.recipientPrivateKey; - - if (builder.recipientPublicKey == null - || builder.recipientPublicKey.length != WebPushConstants.PUBLIC_KEY_SIZE) { - throw new IllegalArgumentException( - "recipient public key must have " + WebPushConstants.PUBLIC_KEY_SIZE + " bytes"); - } - this.recipientPublicKey = builder.recipientPublicKey; - - if (builder.authSecret == null) { - throw new IllegalArgumentException("must set auth secret with Builder.withAuthSecret"); - } - if (builder.authSecret.length != WebPushConstants.AUTH_SECRET_SIZE) { - throw new IllegalArgumentException( - "auth secret must have " + WebPushConstants.AUTH_SECRET_SIZE + " bytes"); - } - this.authSecret = builder.authSecret; - - if (builder.recordSize < WebPushConstants.CIPHERTEXT_OVERHEAD - || builder.recordSize > WebPushConstants.MAX_CIPHERTEXT_SIZE) { - throw new IllegalArgumentException( - String.format( - "invalid record size (%s); must be a number between [%s, %s]", - builder.recordSize, - WebPushConstants.CIPHERTEXT_OVERHEAD, - WebPushConstants.MAX_CIPHERTEXT_SIZE)); - } - this.recordSize = builder.recordSize; - } - - /** - * Builder for {@link WebPushHybridDecrypt}. - * - * @since 1.1.0 - */ - public static final class Builder { - private ECPrivateKey recipientPrivateKey = null; - private byte[] recipientPublicKey = null; - private byte[] authSecret = null; - private int recordSize = WebPushConstants.MAX_CIPHERTEXT_SIZE; - - public Builder() {} - - /** - * Sets the record size. - * - * <p>If set, this value must match the record size set with {@link - * WebPushHybridEncrypt.Builder#withRecordSize}. - * - * <p>If not set, a record size of 4096 bytes is used. This value should work for most users. - */ - public Builder withRecordSize(int val) { - recordSize = val; - return this; - } - - /** Sets the authentication secret. */ - public Builder withAuthSecret(final byte[] val) { - authSecret = val.clone(); - return this; - } - - /** Sets the public key of the recipient. */ - public Builder withRecipientPublicKey(ECPublicKey val) throws GeneralSecurityException { - recipientPublicKey = - EllipticCurves.pointEncode( - WebPushConstants.NIST_P256_CURVE_TYPE, - WebPushConstants.UNCOMPRESSED_POINT_FORMAT, - val.getW()); - return this; - } - - /** - * Sets the public key of the recipient. - * - * <p>The public key must be formatted as an uncompressed point format, i.e., it has {@code 65} - * bytes and the first byte must be {@code 0x04}. - */ - public Builder withRecipientPublicKey(final byte[] val) { - recipientPublicKey = val.clone(); - return this; - } - - /** Sets the private key of the recipient. */ - public Builder withRecipientPrivateKey(ECPrivateKey val) throws GeneralSecurityException { - recipientPrivateKey = val; - return this; - } - - /** - * Sets the private key of the recipient. - * - * <p>The private key is the serialized bytes of the BigInteger returned by - * {@link ECPrivateKey#getS()}. - */ - public Builder withRecipientPrivateKey(final byte[] val) throws GeneralSecurityException { - recipientPrivateKey = - EllipticCurves.getEcPrivateKey(WebPushConstants.NIST_P256_CURVE_TYPE, val); - return this; - } - - public WebPushHybridDecrypt build() throws GeneralSecurityException { - return new WebPushHybridDecrypt(this); - } - } - - @Override - public byte[] decrypt(final byte[] ciphertext, final byte[] contextInfo /* unused */) - throws GeneralSecurityException { - if (contextInfo != null) { - throw new GeneralSecurityException("contextInfo must be null because it is unused"); - } - - if (ciphertext.length < WebPushConstants.CIPHERTEXT_OVERHEAD) { - throw new GeneralSecurityException("ciphertext too short"); - } - - // A push service is not required to support more than 4096 octets of - // payload body. See https://tools.ietf.org/html/rfc8291#section-4.0. - if (ciphertext.length > WebPushConstants.MAX_CIPHERTEXT_SIZE) { - throw new GeneralSecurityException("ciphertext too long"); - } - - // Unpacking. - ByteBuffer record = ByteBuffer.wrap(ciphertext); - byte[] salt = new byte[WebPushConstants.SALT_SIZE]; - record.get(salt); - - int recordSize = record.getInt(); - if (recordSize != this.recordSize - || recordSize < ciphertext.length - || recordSize > WebPushConstants.MAX_CIPHERTEXT_SIZE) { - throw new GeneralSecurityException("invalid record size: " + recordSize); - } - - int publicKeySize = (int) record.get(); - if (publicKeySize != WebPushConstants.PUBLIC_KEY_SIZE) { - throw new GeneralSecurityException("invalid ephemeral public key size: " + publicKeySize); - } - - byte[] asPublicKey = new byte[WebPushConstants.PUBLIC_KEY_SIZE]; - record.get(asPublicKey); - ECPoint asPublicPoint = - EllipticCurves.pointDecode( - WebPushConstants.NIST_P256_CURVE_TYPE, - WebPushConstants.UNCOMPRESSED_POINT_FORMAT, - asPublicKey); - - byte[] payload = new byte[ciphertext.length - WebPushConstants.CONTENT_CODING_HEADER_SIZE]; - record.get(payload); - - // See https://tools.ietf.org/html/rfc8291#section-3.4. - byte[] ecdhSecret = EllipticCurves.computeSharedSecret(recipientPrivateKey, asPublicPoint); - byte[] ikm = WebPushUtil.computeIkm(ecdhSecret, authSecret, recipientPublicKey, asPublicKey); - byte[] cek = WebPushUtil.computeCek(ikm, salt); - byte[] nonce = WebPushUtil.computeNonce(ikm, salt); - - return decrypt(cek, nonce, payload); - } - - private byte[] decrypt(final byte[] key, final byte[] nonce, final byte[] ciphertext) - throws GeneralSecurityException { - Cipher cipher = EngineFactory.CIPHER.getInstance("AES/GCM/NoPadding"); - GCMParameterSpec params = new GCMParameterSpec(8 * WebPushConstants.TAG_SIZE, nonce); - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), params); - byte[] plaintext = cipher.doFinal(ciphertext); - if (plaintext.length == 0) { - throw new GeneralSecurityException("decryption failed"); - } - // Remove zero paddings. - int index = plaintext.length - 1; - while (index > 0) { - if (plaintext[index] != 0) { - break; - } - index--; - } - - if (plaintext[index] != WebPushConstants.PADDING_DELIMITER_BYTE) { - throw new GeneralSecurityException("decryption failed"); - } - return Arrays.copyOf(plaintext, index); - } -}
diff --git a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushHybridEncrypt.java b/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushHybridEncrypt.java deleted file mode 100644 index dfb7555..0000000 --- a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushHybridEncrypt.java +++ /dev/null
@@ -1,259 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.webpush; - -import com.google.crypto.tink.HybridEncrypt; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.EngineFactory; -import com.google.crypto.tink.subtle.Random; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECPoint; -import javax.crypto.Cipher; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * A {@link HybridEncrypt} implementation for the hybrid encryption used in <a - * href="https://tools.ietf.org/html/rfc8291">RFC 8291 - Web Push Message Encryption</a>. - * - * <h3>Ciphertext format</h3> - * - * <p>When used with <a href="https://tools.ietf.org/html/rfc8291#section-4">AES128-GCM content - * encoding</a>, which is the only content encoding supported in this implementation, the ciphertext - * is formatted according to RFC 8188 section 2, and looks as follows - * - * <pre> - * // NOLINTNEXTLINE - * +-----------+----------------+------------------+--------------------------------------------------- - * | salt (16) | recordsize (4) | publickeylen (1) | publickey (publickeylen) | aes128-gcm-ciphertext | - * +-----------+----------------+------------------+--------------------------------------------------- - * </pre> - * - * <p>RFC 8188 divides messages into records which are encrypted independently. Web Push messages - * cannot be longer than 3993 bytes, and are always encrypted in a single record with default size - * of 4096 bytes. {@code aes128-gcm-ciphertext} is the encryption of the message padded with a - * single byte of value {@code 0x02} (which indicates that this is the last and only record). - * - * <h3>Usage</h3> - * - * <pre>{@code - * import com.google.crypto.tink.HybridDecrypt; - * import com.google.crypto.tink.HybridEncrypt; - * import java.security.interfaces.ECPrivateKey; - * import java.security.interfaces.ECPublicKey; - * - * // Encryption. - * ECPublicKey reicipientPublicKey = ...; - * byte[] authSecret = ...; - * HybridEncrypt hybridEncrypt = new WebPushHybridEncrypt.Builder() - * .withAuthSecret(authSecret) - * .withRecipientPublicKey(recipientPublicKey) - * .build(); - * byte[] plaintext = ...; - * byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null); - * - * // Decryption. - * ECPrivateKey recipientPrivateKey = ...; - * HybridDecrypt hybridDecrypt = new WebPushHybridDecrypt.Builder() - * .withAuthSecret(authSecret) - * .withRecipientPublicKey(recipientPublicKey) - * .withRecipientPrivateKey(recipientPrivateKey) - * .build(); - * byte[] plaintext = hybridDecrypt.decrypt(ciphertext, null); - * }</pre> - * - * @since 1.1.0 - */ -public final class WebPushHybridEncrypt implements HybridEncrypt { - private final byte[] recipientPublicKey; - private final byte[] authSecret; - private final ECPoint recipientPublicPoint; - private final int recordSize; - private final int paddingSize; - - private WebPushHybridEncrypt(Builder builder) throws GeneralSecurityException { - if (builder.recipientPublicKey == null || builder.recipientPublicPoint == null) { - throw new IllegalArgumentException( - "must set recipient's public key with Builder.withRecipientPublicKey"); - } - this.recipientPublicKey = builder.recipientPublicKey; - this.recipientPublicPoint = builder.recipientPublicPoint; - - if (builder.authSecret == null) { - throw new IllegalArgumentException("must set auth secret with Builder.withAuthSecret"); - } - if (builder.authSecret.length != WebPushConstants.AUTH_SECRET_SIZE) { - throw new IllegalArgumentException( - "auth secret must have " + WebPushConstants.AUTH_SECRET_SIZE + " bytes"); - } - this.authSecret = builder.authSecret; - this.recordSize = builder.recordSize; - this.paddingSize = builder.paddingSize; - } - - /** - * Builder for {@link WebPushHybridEncrypt}. - * - * @since 1.1.0 - */ - public static final class Builder { - private byte[] recipientPublicKey = null; - private ECPoint recipientPublicPoint = null; - private byte[] authSecret = null; - private int recordSize = WebPushConstants.MAX_CIPHERTEXT_SIZE; - private int paddingSize = WebPushConstants.DEFAULT_PADDING_SIZE; - - public Builder() {} - - /** - * Sets the record size. - * - * <p>If set, this value must match the record size set with {@link - * WebPushHybridEncrypt.Builder#withRecordSize}. - * - * <p>If not set, a record size of 4096 bytes is used. This value should work for most users. - */ - public Builder withRecordSize(int val) { - if (val < WebPushConstants.CIPHERTEXT_OVERHEAD - || val > WebPushConstants.MAX_CIPHERTEXT_SIZE) { - throw new IllegalArgumentException( - String.format( - "invalid record size (%s); must be a number between [%s, %s]", - val, WebPushConstants.CIPHERTEXT_OVERHEAD, WebPushConstants.MAX_CIPHERTEXT_SIZE)); - } - - recordSize = val; - return this; - } - - /** - * Sets the padding size which is default to 0. - * - * <p>The padding size cannot be larger than - */ - public Builder withPaddingSize(int val) { - if (val < 0 - || val > WebPushConstants.MAX_CIPHERTEXT_SIZE - WebPushConstants.CIPHERTEXT_OVERHEAD) { - throw new IllegalArgumentException( - String.format( - "invalid padding size (%s); must be a number between [%s, %s]", - val, - 0, - WebPushConstants.MAX_CIPHERTEXT_SIZE - WebPushConstants.CIPHERTEXT_OVERHEAD)); - } - - paddingSize = val; - return this; - } - - /** Sets the authentication secret. */ - public Builder withAuthSecret(final byte[] val) { - authSecret = val.clone(); - return this; - } - - /** Sets the public key of the recipient. */ - public Builder withRecipientPublicKey(ECPublicKey val) throws GeneralSecurityException { - recipientPublicPoint = val.getW(); - recipientPublicKey = - EllipticCurves.pointEncode( - WebPushConstants.NIST_P256_CURVE_TYPE, - WebPushConstants.UNCOMPRESSED_POINT_FORMAT, - val.getW()); - - return this; - } - - /** - * Sets the public key of the recipient. - * - * <p>The public key must be formatted as an uncompressed point format, i.e., it has {@code 65} - * bytes and the first byte must be {@code 0x04}. - */ - public Builder withRecipientPublicKey(final byte[] val) throws GeneralSecurityException { - recipientPublicKey = val.clone(); - recipientPublicPoint = - EllipticCurves.pointDecode( - WebPushConstants.NIST_P256_CURVE_TYPE, - WebPushConstants.UNCOMPRESSED_POINT_FORMAT, - recipientPublicKey); - return this; - } - - public WebPushHybridEncrypt build() throws GeneralSecurityException { - return new WebPushHybridEncrypt(this); - } - } - - @Override - public byte[] encrypt(final byte[] plaintext, final byte[] contextInfo /* unused */) - throws GeneralSecurityException { - if (contextInfo != null) { - throw new GeneralSecurityException("contextInfo must be null because it is unused"); - } - - if (plaintext.length > recordSize - paddingSize - WebPushConstants.CIPHERTEXT_OVERHEAD) { - throw new GeneralSecurityException( - String.format( - "plaintext too long; with record size = %d and padding size = %d, plaintext cannot" - + " be longer than %d", - recordSize, - paddingSize, - recordSize - paddingSize - WebPushConstants.CIPHERTEXT_OVERHEAD)); - } - - // See https://tools.ietf.org/html/rfc8291#section-3.4. - KeyPair keyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPrivateKey ephemeralPrivateKey = (ECPrivateKey) keyPair.getPrivate(); - ECPublicKey ephemeralPublicKey = (ECPublicKey) keyPair.getPublic(); - byte[] ecdhSecret = - EllipticCurves.computeSharedSecret(ephemeralPrivateKey, recipientPublicPoint); - byte[] ephemeralPublicKeyBytes = - EllipticCurves.pointEncode( - WebPushConstants.NIST_P256_CURVE_TYPE, - WebPushConstants.UNCOMPRESSED_POINT_FORMAT, - ephemeralPublicKey.getW()); - byte[] ikm = - WebPushUtil.computeIkm(ecdhSecret, authSecret, recipientPublicKey, ephemeralPublicKeyBytes); - byte[] salt = Random.randBytes(WebPushConstants.SALT_SIZE); - byte[] cek = WebPushUtil.computeCek(ikm, salt); - byte[] nonce = WebPushUtil.computeNonce(ikm, salt); - return ByteBuffer.allocate( - WebPushConstants.CIPHERTEXT_OVERHEAD + plaintext.length + paddingSize) - .put(salt) - .putInt(recordSize) - .put((byte) WebPushConstants.PUBLIC_KEY_SIZE) - .put(ephemeralPublicKeyBytes) - .put(encrypt(cek, nonce, plaintext)) - .array(); - } - - private byte[] encrypt(final byte[] key, final byte[] nonce, final byte[] plaintext) - throws GeneralSecurityException { - Cipher cipher = EngineFactory.CIPHER.getInstance("AES/GCM/NoPadding"); - GCMParameterSpec params = new GCMParameterSpec(8 * WebPushConstants.TAG_SIZE, nonce); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), params); - byte[] paddedPlaintext = new byte[plaintext.length + 1 + paddingSize]; - paddedPlaintext[plaintext.length] = WebPushConstants.PADDING_DELIMITER_BYTE; - System.arraycopy(plaintext, 0, paddedPlaintext, 0, plaintext.length); - return cipher.doFinal(paddedPlaintext); - } -}
diff --git a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushUtil.java b/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushUtil.java deleted file mode 100644 index c2fc72e..0000000 --- a/apps/webpush/src/main/java/com/google/crypto/tink/apps/webpush/WebPushUtil.java +++ /dev/null
@@ -1,61 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.webpush; - -import com.google.crypto.tink.subtle.Bytes; -import com.google.crypto.tink.subtle.Hkdf; -import java.security.GeneralSecurityException; - -/** Various helpers. */ -final class WebPushUtil { - public static byte[] computeIkm( - final byte[] ecdhSecret, - final byte[] authSecret, - final byte[] uaPublic, - final byte[] asPublic) - throws GeneralSecurityException { - byte[] keyInfo = Bytes.concat(WebPushConstants.IKM_INFO, uaPublic, asPublic); - return Hkdf.computeHkdf( - WebPushConstants.HMAC_SHA256, - ecdhSecret /* ikm */, - authSecret /* salt */, - keyInfo, - WebPushConstants.IKM_SIZE); - } - - public static byte[] computeCek(final byte[] ikm, final byte[] salt) - throws GeneralSecurityException { - return Hkdf.computeHkdf( - WebPushConstants.HMAC_SHA256, - ikm, - salt, - WebPushConstants.CEK_INFO, - WebPushConstants.CEK_KEY_SIZE); - } - - public static byte[] computeNonce(final byte[] ikm, final byte[] salt) - throws GeneralSecurityException { - return Hkdf.computeHkdf( - WebPushConstants.HMAC_SHA256, - ikm, - salt, - WebPushConstants.NONCE_INFO, - WebPushConstants.NONCE_SIZE); - } - - private WebPushUtil() {} -}
diff --git a/apps/webpush/src/test/BUILD.bazel b/apps/webpush/src/test/BUILD.bazel deleted file mode 100644 index 309b3ad..0000000 --- a/apps/webpush/src/test/BUILD.bazel +++ /dev/null
@@ -1,38 +0,0 @@ -load("@tink_java//tools:gen_java_test_rules.bzl", "gen_java_test_rules") - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -# Tests - -java_library( - name = "generator_test", - testonly = 1, - srcs = glob([ - "**/*.java", - ]), - deps = [ - "//webpush/src/main/java/com/google/crypto/tink/apps/webpush:web_push_constants", - "//webpush/src/main/java/com/google/crypto/tink/apps/webpush:web_push_hybrid_decrypt", - "//webpush/src/main/java/com/google/crypto/tink/apps/webpush:web_push_hybrid_encrypt", - "//webpush/src/main/java/com/google/crypto/tink/apps/webpush:web_push_util", - "@maven//:junit_junit", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_decrypt", - "@tink_java//src/main/java/com/google/crypto/tink:hybrid_encrypt", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:base64", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:elliptic_curves", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:hex", - "@tink_java//src/main/java/com/google/crypto/tink/subtle:random", - "@tink_java//src/main/java/com/google/crypto/tink/testing:test_util", - ], -) - -gen_java_test_rules( - test_files = glob([ - "**/*Test.java", - ]), - deps = [ - ":generator_test", - ], -)
diff --git a/apps/webpush/src/test/java/com/google/crypto/tink/apps/webpush/WebPushHybridDecryptTest.java b/apps/webpush/src/test/java/com/google/crypto/tink/apps/webpush/WebPushHybridDecryptTest.java deleted file mode 100644 index f8a6987..0000000 --- a/apps/webpush/src/test/java/com/google/crypto/tink/apps/webpush/WebPushHybridDecryptTest.java +++ /dev/null
@@ -1,243 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.webpush; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import com.google.crypto.tink.HybridDecrypt; -import com.google.crypto.tink.HybridEncrypt; -import com.google.crypto.tink.subtle.Base64; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.Random; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@code WebPushHybridDecrypt}. */ -@RunWith(JUnit4.class) -public class WebPushHybridDecryptTest { - // Copied from https://tools.ietf.org/html/rfc8291#section-5. - private static final String PLAINTEXT = "V2hlbiBJIGdyb3cgdXAsIEkgd2FudCB0byBiZSBhIHdhdGVybWVsb24"; - private static final String RECEIVER_PRIVATE_KEY = "q1dXpw3UpT5VOmu_cf_v6ih07Aems3njxI-JWgLcM94"; - private static final String RECEIVER_PUBLIC_KEY = - "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4"; - private static final String AUTH_SECRET = "BTBZMqHH6r4Tts7J_aSIgg"; - private static final String CIPHERTEXT = - "DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27ml" - + "mlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPT" - + "pK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN"; - private static final int RECORD_SIZE = 4096; - - @Test - public void testWithRfc8291TestVector() throws Exception { - byte[] plaintext = Base64.urlSafeDecode(PLAINTEXT); - byte[] recipientPrivateKey = Base64.urlSafeDecode(RECEIVER_PRIVATE_KEY); - byte[] recipientPublicKey = Base64.urlSafeDecode(RECEIVER_PUBLIC_KEY); - byte[] authSecret = Base64.urlSafeDecode(AUTH_SECRET); - byte[] ciphertext = Base64.urlSafeDecode(CIPHERTEXT); - - HybridDecrypt hybridDecrypt = - new WebPushHybridDecrypt.Builder() - .withRecordSize(RECORD_SIZE) - .withAuthSecret(authSecret) - .withRecipientPublicKey(recipientPublicKey) - .withRecipientPrivateKey(recipientPrivateKey) - .build(); - assertArrayEquals(plaintext, hybridDecrypt.decrypt(ciphertext, null /* contextInfo */)); - } - - @Test - public void testEncryptDecryptWithInvalidRecordSizes() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPrivateKey uaPrivateKey = (ECPrivateKey) uaKeyPair.getPrivate(); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] authSecret = Random.randBytes(16); - - // Test with out of range record sizes. - { - try { - new WebPushHybridDecrypt.Builder() - .withRecordSize(WebPushConstants.MAX_CIPHERTEXT_SIZE + 1) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException ex) { - // expected. - } - - try { - new WebPushHybridDecrypt.Builder() - .withRecordSize(WebPushConstants.CIPHERTEXT_OVERHEAD - 1) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - - } catch (IllegalArgumentException ex) { - // expected. - } - } - - // Test with random mismatched record size. - { - for (int i = 0; i < 50; i++) { - int recordSize = - WebPushConstants.CIPHERTEXT_OVERHEAD - + Random.randInt( - WebPushConstants.MAX_CIPHERTEXT_SIZE - - WebPushConstants.CIPHERTEXT_OVERHEAD - - 1); - HybridEncrypt hybridEncrypt = - new WebPushHybridEncrypt.Builder() - .withRecordSize(recordSize) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .build(); - HybridDecrypt hybridDecrypt = - new WebPushHybridDecrypt.Builder() - .withRecordSize(recordSize + 1) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - byte[] plaintext = Random.randBytes(recordSize - WebPushConstants.CIPHERTEXT_OVERHEAD); - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null /* contextInfo */); - - try { - hybridDecrypt.decrypt(ciphertext, null /* contextInfo */); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException ex) { - // expected. - } - } - } - } - - @Test - public void testNonNullContextInfo() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPrivateKey uaPrivateKey = (ECPrivateKey) uaKeyPair.getPrivate(); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] authSecret = Random.randBytes(16); - - HybridEncrypt hybridEncrypt = - new WebPushHybridEncrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .build(); - HybridDecrypt hybridDecrypt = - new WebPushHybridDecrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - byte[] plaintext = Random.randBytes(20); - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null /* contextInfo */); - try { - byte[] contextInfo = new byte[0]; - hybridDecrypt.decrypt(ciphertext, contextInfo); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException ex) { - // expected; - } - } - - @Test - public void testModifyCiphertext() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPrivateKey uaPrivateKey = (ECPrivateKey) uaKeyPair.getPrivate(); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] authSecret = Random.randBytes(16); - - HybridEncrypt hybridEncrypt = - new WebPushHybridEncrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .build(); - HybridDecrypt hybridDecrypt = - new WebPushHybridDecrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - byte[] plaintext = Random.randBytes(20); - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null /* contextInfo */); - - // Flipping bits. - for (int b = 0; b < ciphertext.length; b++) { - for (int bit = 0; bit < 8; bit++) { - byte[] modified = Arrays.copyOf(ciphertext, ciphertext.length); - modified[b] ^= (byte) (1 << bit); - try { - byte[] unused = hybridDecrypt.decrypt(modified, null /* contextInfo */); - fail("Decrypting modified ciphertext should fail"); - } catch (GeneralSecurityException ex) { - // This is expected. - } - } - } - - // Truncate the message. - for (int length = 0; length < ciphertext.length; length++) { - byte[] modified = Arrays.copyOf(ciphertext, length); - try { - byte[] unused = hybridDecrypt.decrypt(modified, null /* contextInfo */); - fail("Decrypting modified ciphertext should fail"); - } catch (GeneralSecurityException ex) { - // This is expected. - } - } - } - - @Test - public void testEncryptDecrypt_withPadding_shouldWork() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPrivateKey uaPrivateKey = (ECPrivateKey) uaKeyPair.getPrivate(); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] authSecret = Random.randBytes(16); - - int paddingSize = 20; - int plaintextSize = 20; - HybridEncrypt hybridEncrypt = - new WebPushHybridEncrypt.Builder() - .withAuthSecret(authSecret) - .withPaddingSize(paddingSize) - .withRecipientPublicKey(uaPublicKey) - .build(); - HybridDecrypt hybridDecrypt = - new WebPushHybridDecrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - byte[] plaintext = Random.randBytes(plaintextSize); - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null /* contextInfo */); - - assertEquals( - ciphertext.length, plaintext.length + paddingSize + WebPushConstants.CIPHERTEXT_OVERHEAD); - assertArrayEquals(plaintext, hybridDecrypt.decrypt(ciphertext, null /* contextInfo */)); - } -}
diff --git a/apps/webpush/src/test/java/com/google/crypto/tink/apps/webpush/WebPushHybridEncryptTest.java b/apps/webpush/src/test/java/com/google/crypto/tink/apps/webpush/WebPushHybridEncryptTest.java deleted file mode 100644 index 7b84b84..0000000 --- a/apps/webpush/src/test/java/com/google/crypto/tink/apps/webpush/WebPushHybridEncryptTest.java +++ /dev/null
@@ -1,244 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.google.crypto.tink.apps.webpush; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import com.google.crypto.tink.HybridDecrypt; -import com.google.crypto.tink.HybridEncrypt; -import com.google.crypto.tink.subtle.EllipticCurves; -import com.google.crypto.tink.subtle.Hex; -import com.google.crypto.tink.subtle.Random; -import com.google.crypto.tink.testing.TestUtil; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.util.Set; -import java.util.TreeSet; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@code WebPushHybridEncrypt}. */ -@RunWith(JUnit4.class) -public class WebPushHybridEncryptTest { - @Test - public void testEncryptDecrypt() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPrivateKey uaPrivateKey = (ECPrivateKey) uaKeyPair.getPrivate(); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] uaPublicKeyBytes = - EllipticCurves.pointEncode( - WebPushConstants.NIST_P256_CURVE_TYPE, - WebPushConstants.UNCOMPRESSED_POINT_FORMAT, - uaPublicKey.getW()); - byte[] authSecret = Random.randBytes(16); - HybridEncrypt hybridEncrypt = - new WebPushHybridEncrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKeyBytes) - .build(); - HybridDecrypt hybridDecrypt = - new WebPushHybridDecrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKeyBytes) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - - Set<String> salts = new TreeSet<>(); - Set<String> ephemeralPublicKeys = new TreeSet<>(); - Set<String> payloads = new TreeSet<>(); - int numTests = 100; - if (TestUtil.isTsan()) { - numTests = 5; - } - for (int j = 0; j < numTests; j++) { - byte[] plaintext = Random.randBytes(j); - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null /* contextInfo */); - assertEquals(ciphertext.length, plaintext.length + WebPushConstants.CIPHERTEXT_OVERHEAD); - assertArrayEquals(plaintext, hybridDecrypt.decrypt(ciphertext, null /* contextInfo */)); - - // Checks that the encryption is randomized. - ByteBuffer record = ByteBuffer.wrap(ciphertext); - byte[] salt = new byte[WebPushConstants.SALT_SIZE]; - record.get(salt); - salts.add(Hex.encode(salt)); - - int unused1 = record.getInt(); - int unused2 = (int) record.get(); - - byte[] ephemeralPublicKey = new byte[WebPushConstants.PUBLIC_KEY_SIZE]; - record.get(ephemeralPublicKey); - ephemeralPublicKeys.add(Hex.encode(ephemeralPublicKey)); - - byte[] payload = new byte[ciphertext.length - WebPushConstants.CONTENT_CODING_HEADER_SIZE]; - record.get(payload); - payloads.add(Hex.encode(payload)); - } - assertEquals(numTests, salts.size()); - assertEquals(numTests, ephemeralPublicKeys.size()); - assertEquals(numTests, payloads.size()); - } - - @Test - public void testEncryptDecryptWithVaryingRecordSizes() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPrivateKey uaPrivateKey = (ECPrivateKey) uaKeyPair.getPrivate(); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] authSecret = Random.randBytes(16); - - int numTests = 100; - if (TestUtil.isTsan()) { - numTests = 5; - } - // Test with random, valid record sizes. - for (int i = 0; i < numTests; i++) { - int recordSize = - WebPushConstants.CIPHERTEXT_OVERHEAD - + Random.randInt( - WebPushConstants.MAX_CIPHERTEXT_SIZE - WebPushConstants.CIPHERTEXT_OVERHEAD); - HybridEncrypt hybridEncrypt = - new WebPushHybridEncrypt.Builder() - .withRecordSize(recordSize) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .build(); - HybridDecrypt hybridDecrypt = - new WebPushHybridDecrypt.Builder() - .withRecordSize(recordSize) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - - byte[] plaintext = Random.randBytes(recordSize - WebPushConstants.CIPHERTEXT_OVERHEAD); - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null /* contextInfo */); - assertEquals(ciphertext.length, plaintext.length + WebPushConstants.CIPHERTEXT_OVERHEAD); - assertArrayEquals(plaintext, hybridDecrypt.decrypt(ciphertext, null /* contextInfo */)); - } - } - - @Test - public void testEncryptDecrypt_largestPossibleRecordSize() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPrivateKey uaPrivateKey = (ECPrivateKey) uaKeyPair.getPrivate(); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] authSecret = Random.randBytes(16); - // Test with largest possible record size. - HybridEncrypt hybridEncrypt = - new WebPushHybridEncrypt.Builder() - .withRecordSize(WebPushConstants.MAX_CIPHERTEXT_SIZE) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .build(); - HybridDecrypt hybridDecrypt = - new WebPushHybridDecrypt.Builder() - .withRecordSize(WebPushConstants.MAX_CIPHERTEXT_SIZE) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - byte[] plaintext = - Random.randBytes( - WebPushConstants.MAX_CIPHERTEXT_SIZE - WebPushConstants.CIPHERTEXT_OVERHEAD); - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null /* contextInfo */); - assertEquals(ciphertext.length, plaintext.length + WebPushConstants.CIPHERTEXT_OVERHEAD); - assertArrayEquals(plaintext, hybridDecrypt.decrypt(ciphertext, null /* contextInfo */)); - } - - @Test - public void testEncryptDecrypt_smallestPossibleRecordSize() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPrivateKey uaPrivateKey = (ECPrivateKey) uaKeyPair.getPrivate(); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] authSecret = Random.randBytes(16); - // Test with smallest possible record size. - HybridEncrypt hybridEncrypt = - new WebPushHybridEncrypt.Builder() - .withRecordSize(WebPushConstants.CIPHERTEXT_OVERHEAD) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .build(); - HybridDecrypt hybridDecrypt = - new WebPushHybridDecrypt.Builder() - .withRecordSize(WebPushConstants.CIPHERTEXT_OVERHEAD) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .withRecipientPrivateKey(uaPrivateKey) - .build(); - byte[] plaintext = new byte[0]; - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, null /* contextInfo */); - assertEquals(ciphertext.length, plaintext.length + WebPushConstants.CIPHERTEXT_OVERHEAD); - assertArrayEquals(plaintext, hybridDecrypt.decrypt(ciphertext, null /* contextInfo */)); - } - - @Test - public void testEncryptDecrypt_outOfRangeRecordSize_throws() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] authSecret = Random.randBytes(16); - - try { - new WebPushHybridEncrypt.Builder() - .withRecordSize(WebPushConstants.MAX_CIPHERTEXT_SIZE + 1) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .build(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException ex) { - // expected. - } - - try { - new WebPushHybridEncrypt.Builder() - .withRecordSize(WebPushConstants.CIPHERTEXT_OVERHEAD - 1) - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .build(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException ex) { - // expected. - } - } - - @Test - public void testNonNullContextInfo() throws Exception { - KeyPair uaKeyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE); - ECPublicKey uaPublicKey = (ECPublicKey) uaKeyPair.getPublic(); - byte[] authSecret = Random.randBytes(16); - - HybridEncrypt hybridEncrypt = - new WebPushHybridEncrypt.Builder() - .withAuthSecret(authSecret) - .withRecipientPublicKey(uaPublicKey) - .build(); - byte[] plaintext = Random.randBytes(20); - byte[] contextInfo = new byte[0]; - - try { - byte[] unusedCiphertext = hybridEncrypt.encrypt(plaintext, contextInfo); - fail("Expected GeneralSecurityException"); - } catch (GeneralSecurityException ex) { - // expected; - } - } -} -
diff --git a/cc/.bazelrc b/cc/.bazelrc index 94aad05..e2435c9 100644 --- a/cc/.bazelrc +++ b/cc/.bazelrc
@@ -1,3 +1,7 @@ # Fix for grpc build error on macOS. # See: https://github.com/bazelbuild/bazel/issues/4341 -build --copt -DGRPC_BAZEL_BUILD +build --copt=-DGRPC_BAZEL_BUILD +# Minumum C++ version. Override it building this project with +# `bazel build --cxxopt='-std=c++<XY>' --host_cxxopt='c++<XY>' ...` +# (Both -std and --host_cxxopt must be set to force the desired version.) +build --cxxopt='-std=c++14' --host_cxxopt='-std=c++14'
diff --git a/cc/.bazelversion b/cc/.bazelversion index ac14c3d..09b254e 100644 --- a/cc/.bazelversion +++ b/cc/.bazelversion
@@ -1 +1 @@ -5.1.1 +6.0.0
diff --git a/cc/BUILD.bazel b/cc/BUILD.bazel index 5064fdb..c88dd8e 100644 --- a/cc/BUILD.bazel +++ b/cc/BUILD.bazel
@@ -23,8 +23,6 @@ "aead_key_templates.h", "binary_keyset_reader.h", "binary_keyset_writer.h", - "catalogue.h", - "config.h", "deterministic_aead.h", "deterministic_aead_config.h", "deterministic_aead_factory.h", @@ -77,6 +75,7 @@ ":input_stream", ":json_keyset_reader", ":json_keyset_writer", + ":key", ":key_manager", ":keyset_handle", ":keyset_manager", @@ -396,41 +395,6 @@ ) cc_library( - name = "catalogue", - hdrs = ["catalogue.h"], - include_prefix = "tink", - deps = [ - ":key_manager", - "//util:statusor", - "@com_google_absl//absl/base:core_headers", - ], -) - -cc_library( - name = "config", - srcs = ["core/config.cc"], - hdrs = ["config.h"], - include_prefix = "tink", - deps = [ - ":catalogue", - ":key_manager", - ":registry", - "//aead:aead_config", - "//daead:deterministic_aead_config", - "//hybrid:hybrid_config", - "//mac:mac_config", - "//proto:config_cc_proto", - "//signature:signature_config", - "//streamingaead:streaming_aead_config", - "//util:errors", - "//util:status", - "//util:statusor", - "@com_google_absl//absl/status", - "@com_google_absl//absl/strings", - ], -) - -cc_library( name = "crypto_format", srcs = ["core/crypto_format.cc"], hdrs = ["crypto_format.h"], @@ -453,6 +417,7 @@ "//proto:tink_cc_proto", "//util:errors", "//util:statusor", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", @@ -507,20 +472,54 @@ visibility = ["//visibility:public"], deps = [ ":aead", + ":configuration", + ":insecure_secret_key_access", + ":key", + ":key_gen_configuration", ":key_manager", + ":key_status", ":keyset_reader", ":keyset_writer", ":primitive_set", ":registry", + "//internal:configuration_impl", + "//internal:key_gen_configuration_impl", "//internal:key_info", + "//internal:key_status_util", + "//internal:mutable_serialization_registry", + "//internal:proto_key_serialization", + "//internal:util", "//proto:tink_cc_proto", "//util:errors", "//util:keyset_util", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + ], +) + +cc_library( + name = "keyset_handle_builder", + srcs = ["core/keyset_handle_builder.cc"], + hdrs = ["keyset_handle_builder.h"], + include_prefix = "tink", + visibility = ["//visibility:public"], + deps = [ + ":key", + ":key_status", + ":keyset_handle", + ":parameters", + "//internal:keyset_handle_builder_entry", + "//proto:tink_cc_proto", + "//subtle:random", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", ], ) @@ -565,9 +564,9 @@ include_prefix = "tink", visibility = ["//visibility:public"], deps = [ + ":key_gen_configuration", ":keyset_handle", - ":keyset_reader", - ":registry", + "//internal:key_gen_configuration_impl", "//proto:tink_cc_proto", "//util:enums", "//util:errors", @@ -703,6 +702,19 @@ ) cc_library( + name = "partial_key_access_token", + hdrs = ["partial_key_access_token.h"], + include_prefix = "tink", +) + +cc_library( + name = "partial_key_access", + hdrs = ["partial_key_access.h"], + include_prefix = "tink", + deps = [":partial_key_access_token"], +) + +cc_library( name = "secret_key_access_token", hdrs = ["secret_key_access_token.h"], include_prefix = "tink", @@ -712,9 +724,31 @@ name = "insecure_secret_key_access", hdrs = ["insecure_secret_key_access.h"], include_prefix = "tink", + visibility = ["//visibility:public"], deps = [":secret_key_access_token"], ) +cc_library( + name = "restricted_data", + srcs = ["core/restricted_data.cc"], + hdrs = ["restricted_data.h"], + include_prefix = "tink", + deps = [ + ":secret_key_access_token", + "//subtle:random", + "//util:secret_data", + "@boringssl//:crypto", + "@com_google_absl//absl/log:check", + ], +) + +cc_library( + name = "key_status", + hdrs = ["key_status.h"], + include_prefix = "tink", + visibility = ["//visibility:public"], +) + # tests cc_test( @@ -723,6 +757,7 @@ srcs = ["core/version_test.cc"], deps = [ ":version", + "//internal:util", "@com_google_googletest//:gtest_main", ], ) @@ -788,18 +823,6 @@ ) cc_test( - name = "config_test", - size = "small", - srcs = ["core/config_test.cc"], - deps = [ - ":config", - ":mac", - "//proto:config_cc_proto", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( name = "crypto_format_test", size = "small", srcs = ["core/crypto_format_test.cc"], @@ -820,6 +843,8 @@ ":core/key_manager_impl", ":json_keyset_reader", ":json_keyset_writer", + ":key_gen_configuration", + ":key_status", ":keyset_handle", ":primitive_set", ":primitive_wrapper", @@ -827,11 +852,16 @@ "//aead:aead_key_templates", "//aead:aead_wrapper", "//aead:aes_gcm_key_manager", + "//config:fips_140_2", + "//config:key_gen_fips_140_2", "//config:tink_config", + "//config/internal:global_registry", + "//internal:fips_utils", + "//internal:key_gen_configuration_impl", + "//proto:aes_gcm_siv_cc_proto", "//proto:tink_cc_proto", "//signature:ecdsa_sign_key_manager", "//signature:signature_key_templates", - "//util:protobuf_helper", "//util:status", "//util:test_keyset_handle", "//util:test_matchers", @@ -843,6 +873,34 @@ ) cc_test( + name = "keyset_handle_builder_test", + srcs = ["core/keyset_handle_builder_test.cc"], + deps = [ + ":insecure_secret_key_access", + ":key_status", + ":keyset_handle_builder", + ":partial_key_access", + "//config:tink_config", + "//internal:legacy_proto_key", + "//internal:legacy_proto_parameters", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//mac:aes_cmac_key", + "//mac:aes_cmac_parameters", + "//mac:mac_key_templates", + "//proto:aes_cmac_cc_proto", + "//proto:tink_cc_proto", + "//subtle:random", + "//util:status", + "//util:test_matchers", + "//util:test_util", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( name = "key_manager_test", size = "small", srcs = ["core/key_manager_test.cc"], @@ -861,7 +919,6 @@ size = "small", srcs = ["core/keyset_manager_test.cc"], deps = [ - ":config", ":keyset_handle", ":keyset_manager", "//aead:aead_config", @@ -893,9 +950,11 @@ size = "small", srcs = ["core/primitive_set_test.cc"], deps = [ + ":cleartext_keyset_handle", ":crypto_format", ":mac", ":primitive_set", + "//keyderivation:keyset_deriver", "//proto:tink_cc_proto", "//util:test_matchers", "//util:test_util", @@ -999,3 +1058,82 @@ "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "core/partial_key_access_token_test", + srcs = ["core/partial_key_access_token_test.cc"], + deps = [ + ":partial_key_access", + ":partial_key_access_token", + "@com_google_absl//absl/base:core_headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "core/restricted_data_test", + srcs = ["core/restricted_data_test.cc"], + deps = [ + ":insecure_secret_key_access", + ":restricted_data", + "//subtle:random", + "//util:secret_data", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "proto_keyset_format", + srcs = ["proto_keyset_format.cc"], + hdrs = ["proto_keyset_format.h"], + include_prefix = "tink", + visibility = ["//visibility:public"], + deps = [ + ":binary_keyset_reader", + ":binary_keyset_writer", + ":cleartext_keyset_handle", + ":keyset_handle", + ":secret_key_access_token", + "//util:secret_data", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "proto_keyset_format_test", + srcs = ["proto_keyset_format_test.cc"], + deps = [ + ":insecure_secret_key_access", + ":keyset_handle_builder", + ":mac", + ":proto_keyset_format", + "//config:tink_config", + "//internal:legacy_proto_parameters", + "//internal:proto_parameters_serialization", + "//mac:mac_key_templates", + "//signature:signature_key_templates", + "//util:secret_data", + "//util:test_matchers", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "configuration", + hdrs = ["configuration.h"], + include_prefix = "tink", + visibility = ["//visibility:public"], + deps = [ + "//internal:key_type_info_store", + "//internal:keyset_wrapper_store", + ], +) + +cc_library( + name = "key_gen_configuration", + hdrs = ["key_gen_configuration.h"], + include_prefix = "tink", + visibility = ["//visibility:public"], + deps = ["//internal:key_type_info_store"], +)
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index 2ef21b2..e13bee0 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -181,19 +181,6 @@ public_configs = [ "//third_party/tink:tink_config" ] } -# CC Library : catalogue -source_set("catalogue") { - configs += [ "//build/config:no_rtti" ] - configs -= [ "//build/config:no_rtti" ] - sources = [ "catalogue.h" ] - public_deps = [ - ":key_manager", - "//third_party/abseil-cpp/absl/base:core_headers", - "//third_party/tink/cc/util:statusor", - ] - public_configs = [ "//third_party/tink:tink_config" ] -} - # CC Library : crypto_format source_set("crypto_format") { configs += [ "//build/config:no_rtti" ] @@ -221,6 +208,7 @@ ] public_deps = [ ":crypto_format", + "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/container:flat_hash_map", "//third_party/abseil-cpp/absl/memory:memory", "//third_party/abseil-cpp/absl/status:status", @@ -268,17 +256,30 @@ ] public_deps = [ ":aead", + ":configuration", + ":insecure_secret_key_access", + ":key", + ":key_gen_configuration", ":key_manager", + ":key_status", ":keyset_reader", ":keyset_writer", ":primitive_set", ":registry", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/container:flat_hash_map", + "//third_party/abseil-cpp/absl/log:check", "//third_party/abseil-cpp/absl/memory:memory", "//third_party/abseil-cpp/absl/status:status", "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc/internal:configuration_impl", + "//third_party/tink/cc/internal:key_gen_configuration_impl", "//third_party/tink/cc/internal:key_info", + "//third_party/tink/cc/internal:key_status_util", + "//third_party/tink/cc/internal:mutable_serialization_registry", + "//third_party/tink/cc/internal:proto_key_serialization", + "//third_party/tink/cc/internal:util", "//third_party/tink/cc/proto:tink_proto", "//third_party/tink/cc/util:errors", "//third_party/tink/cc/util:keyset_util", @@ -433,3 +434,104 @@ ] public_configs = [ "//third_party/tink:tink_config" ] } + +# CC Library : parameters +source_set("parameters") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "parameters.h" ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : key +source_set("key") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "key.h" ] + public_deps = [ + ":parameters", + "//third_party/abseil-cpp/absl/types:optional", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : partial_key_access_token +source_set("partial_key_access_token") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "partial_key_access_token.h" ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : partial_key_access +source_set("partial_key_access") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "partial_key_access.h" ] + public_deps = [ ":partial_key_access_token" ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : secret_key_access_token +source_set("secret_key_access_token") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "secret_key_access_token.h" ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : insecure_secret_key_access +source_set("insecure_secret_key_access") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "insecure_secret_key_access.h" ] + public_deps = [ ":secret_key_access_token" ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : restricted_data +source_set("restricted_data") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "core/restricted_data.cc", + "restricted_data.h", + ] + public_deps = [ + ":secret_key_access_token", + "//third_party/abseil-cpp/absl/log:check", + "//third_party/boringssl:crypto", + "//third_party/tink/cc/subtle:random", + "//third_party/tink/cc/util:secret_data", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : key_status +source_set("key_status") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "key_status.h" ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : configuration +source_set("configuration") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "configuration.h" ] + public_deps = [ + "//third_party/tink/cc/internal:key_type_info_store", + "//third_party/tink/cc/internal:keyset_wrapper_store", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : key_gen_configuration +source_set("key_gen_configuration") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "key_gen_configuration.h" ] + public_deps = [ "//third_party/tink/cc/internal:key_type_info_store" ] + public_configs = [ "//third_party/tink:tink_config" ] +}
diff --git a/cc/CMakeLists.txt b/cc/CMakeLists.txt index a7defba..1cd07e4 100644 --- a/cc/CMakeLists.txt +++ b/cc/CMakeLists.txt
@@ -6,6 +6,7 @@ add_subdirectory(mac) add_subdirectory(monitoring) add_subdirectory(jwt) +add_subdirectory(keyderivation) add_subdirectory(prf) add_subdirectory(signature) add_subdirectory(streamingaead) @@ -16,12 +17,6 @@ # Configuration settings for the build. -if (TINK_USE_ABSL_STATUS) - target_compile_definitions(tink_util_status PUBLIC TINK_USE_ABSL_STATUS) -endif() -if (TINK_USE_ABSL_STATUSOR) - target_compile_definitions(tink_util_statusor INTERFACE TINK_USE_ABSL_STATUSOR) -endif() if(USE_ONLY_FIPS) target_compile_definitions(tink_internal_fips_utils PUBLIC TINK_USE_ONLY_FIPS) endif() @@ -39,9 +34,7 @@ aead_key_templates.h binary_keyset_reader.h binary_keyset_writer.h - catalogue.h cleartext_keyset_handle.h - config.h deterministic_aead.h deterministic_aead_config.h deterministic_aead_factory.h @@ -92,6 +85,7 @@ tink::core::input_stream tink::core::json_keyset_reader tink::core::json_keyset_writer + tink::core::key tink::core::key_manager tink::core::keyset_handle tink::core::keyset_manager @@ -373,39 +367,6 @@ ) tink_cc_library( - NAME catalogue - SRCS - catalogue.h - DEPS - tink::core::key_manager - absl::core_headers - tink::util::statusor -) - -tink_cc_library( - NAME config - SRCS - core/config.cc - config.h - DEPS - tink::core::catalogue - tink::core::key_manager - tink::core::registry - absl::status - absl::strings - tink::aead::aead_config - tink::daead::deterministic_aead_config - tink::hybrid::hybrid_config - tink::mac::mac_config - tink::signature::signature_config - tink::streamingaead::streaming_aead_config - tink::util::errors - tink::util::status - tink::util::statusor - tink::proto::config_cc_proto -) - -tink_cc_library( NAME crypto_format SRCS core/crypto_format.cc @@ -424,6 +385,7 @@ primitive_set.h DEPS tink::core::crypto_format + absl::core_headers absl::flat_hash_map absl::memory absl::status @@ -472,23 +434,55 @@ keyset_handle.h DEPS tink::core::aead + tink::core::configuration + tink::core::insecure_secret_key_access + tink::core::key + tink::core::key_gen_configuration tink::core::key_manager + tink::core::key_status tink::core::keyset_reader tink::core::keyset_writer tink::core::primitive_set tink::core::registry absl::core_headers absl::flat_hash_map + absl::check absl::memory absl::status absl::strings + absl::optional + tink::internal::configuration_impl + tink::internal::key_gen_configuration_impl tink::internal::key_info + tink::internal::key_status_util + tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization + tink::internal::util tink::util::errors tink::util::keyset_util tink::proto::tink_cc_proto ) tink_cc_library( + NAME keyset_handle_builder + SRCS + core/keyset_handle_builder.cc + keyset_handle_builder.h + DEPS + tink::core::key + tink::core::key_status + tink::core::keyset_handle + tink::core::parameters + absl::check + absl::status + absl::strings + absl::optional + tink::internal::keyset_handle_builder_entry + tink::subtle::random + tink::proto::tink_cc_proto +) + +tink_cc_library( NAME cleartext_keyset_handle SRCS core/cleartext_keyset_handle.cc @@ -525,13 +519,13 @@ core/keyset_manager.cc keyset_manager.h DEPS + tink::core::key_gen_configuration tink::core::keyset_handle - tink::core::keyset_reader - tink::core::registry absl::core_headers absl::memory absl::status absl::synchronization + tink::internal::key_gen_configuration_impl tink::util::enums tink::util::errors tink::util::status @@ -658,6 +652,26 @@ ) tink_cc_library( + NAME partial_key_access_token + SRCS + partial_key_access_token.h +) + +tink_cc_library( + NAME partial_key_access + SRCS + partial_key_access.h + DEPS + tink::core::partial_key_access_token +) + +tink_cc_library( + NAME secret_key_access_token + SRCS + secret_key_access_token.h +) + +tink_cc_library( NAME insecure_secret_key_access SRCS insecure_secret_key_access.h @@ -666,9 +680,22 @@ ) tink_cc_library( - NAME secret_key_access_token + NAME restricted_data SRCS - secret_key_access_token.h + core/restricted_data.cc + restricted_data.h + DEPS + tink::core::secret_key_access_token + absl::check + crypto + tink::subtle::random + tink::util::secret_data +) + +tink_cc_library( + NAME key_status + SRCS + key_status.h ) # tests @@ -680,6 +707,7 @@ DEPS tink::core::version gmock + tink::internal::util ) tink_cc_test( @@ -739,17 +767,6 @@ ) tink_cc_test( - NAME config_test - SRCS - core/config_test.cc - DEPS - tink::core::config - tink::core::mac - gmock - tink::proto::config_cc_proto -) - -tink_cc_test( NAME crypto_format_test SRCS core/crypto_format_test.cc @@ -770,6 +787,8 @@ tink::core::key_manager_impl tink::core::json_keyset_reader tink::core::json_keyset_writer + tink::core::key_gen_configuration + tink::core::key_status tink::core::keyset_handle tink::core::primitive_set tink::core::primitive_wrapper @@ -779,14 +798,47 @@ tink::aead::aead_key_templates tink::aead::aead_wrapper tink::aead::aes_gcm_key_manager + tink::config::fips_140_2 + tink::config::key_gen_fips_140_2 tink::config::tink_config + tink::config::internal::global_registry + tink::internal::fips_utils + tink::internal::key_gen_configuration_impl tink::signature::ecdsa_sign_key_manager tink::signature::signature_key_templates - tink::util::protobuf_helper tink::util::status tink::util::test_keyset_handle tink::util::test_matchers tink::util::test_util + tink::proto::aes_gcm_siv_cc_proto + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME keyset_handle_builder_test + SRCS + core/keyset_handle_builder_test.cc + DEPS + tink::core::insecure_secret_key_access + tink::core::key_status + tink::core::keyset_handle_builder + tink::core::partial_key_access + gmock + absl::status + absl::strings + tink::config::tink_config + tink::internal::legacy_proto_key + tink::internal::legacy_proto_parameters + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::mac::aes_cmac_key + tink::mac::aes_cmac_parameters + tink::mac::mac_key_templates + tink::subtle::random + tink::util::status + tink::util::test_matchers + tink::util::test_util + tink::proto::aes_cmac_cc_proto tink::proto::tink_cc_proto ) @@ -808,7 +860,6 @@ SRCS core/keyset_manager_test.cc DEPS - tink::core::config tink::core::keyset_handle tink::core::keyset_manager gmock @@ -838,10 +889,12 @@ SRCS core/primitive_set_test.cc DEPS + tink::core::cleartext_keyset_handle tink::core::crypto_format tink::core::mac tink::core::primitive_set gmock + tink::keyderivation::keyset_deriver tink::util::test_matchers tink::util::test_util tink::proto::tink_cc_proto @@ -942,3 +995,78 @@ tink::core::secret_key_access_testonly gmock ) + +tink_cc_test( + NAME partial_key_access_token_test + SRCS + core/partial_key_access_token_test.cc + DEPS + tink::core::partial_key_access + tink::core::partial_key_access_token + gmock + absl::core_headers +) + +tink_cc_test( + NAME restricted_data_test + SRCS + core/restricted_data_test.cc + DEPS + tink::core::insecure_secret_key_access + tink::core::restricted_data + gmock + tink::subtle::random + tink::util::secret_data +) + +tink_cc_library( + NAME proto_keyset_format + SRCS + proto_keyset_format.cc + proto_keyset_format.h + DEPS + tink::core::binary_keyset_reader + tink::core::binary_keyset_writer + tink::core::cleartext_keyset_handle + tink::core::keyset_handle + tink::core::secret_key_access_token + absl::strings + tink::util::secret_data +) + +tink_cc_test( + NAME proto_keyset_format_test + SRCS + proto_keyset_format_test.cc + DEPS + tink::core::insecure_secret_key_access + tink::core::keyset_handle_builder + tink::core::mac + tink::core::proto_keyset_format + gmock + absl::strings + tink::config::tink_config + tink::internal::legacy_proto_parameters + tink::internal::proto_parameters_serialization + tink::mac::mac_key_templates + tink::signature::signature_key_templates + tink::util::secret_data + tink::util::test_matchers +) + +tink_cc_library( + NAME configuration + SRCS + configuration.h + DEPS + tink::internal::key_type_info_store + tink::internal::keyset_wrapper_store +) + +tink_cc_library( + NAME key_gen_configuration + SRCS + key_gen_configuration.h + DEPS + tink::internal::key_type_info_store +)
diff --git a/cc/MODULE.bazel b/cc/MODULE.bazel new file mode 100644 index 0000000..ab23a48 --- /dev/null +++ b/cc/MODULE.bazel
@@ -0,0 +1,32 @@ +# Copyright 2023 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. + +"""Tink C++ Bazel Module definition.""" +module( + name = "tink_cc", + version = "2.0.0", +) + +bazel_dep(name = "rules_cc", version = "0.0.5") +bazel_dep(name = "rules_proto", version = "5.3.0-21.7") +bazel_dep(name = "platforms", version = "0.0.6") +bazel_dep(name = "bazel_skylib", version = "1.3.0") +bazel_dep(name = "googletest", version = "1.12.1", repo_name = "com_google_googletest") +bazel_dep(name = "protobuf", version = "21.7", repo_name = "com_google_protobuf") +bazel_dep(name = "boringssl", version = "0.0.0-20230215-5c22014") +bazel_dep(name = "rapidjson", version = "1.1.0") +bazel_dep(name = "abseil-cpp", version = "20230125.1", repo_name="com_google_absl") + +wycheproof_extension = use_extension("//:extensions.bzl", "wycheproof_extension") +use_repo(wycheproof_extension, "wycheproof")
diff --git a/cc/WORKSPACE b/cc/WORKSPACE index ca79c62..af5976a 100644 --- a/cc/WORKSPACE +++ b/cc/WORKSPACE
@@ -13,9 +13,3 @@ load("@tink_cc//:tink_cc_deps_init.bzl", "tink_cc_deps_init") tink_cc_deps_init() - -load("@bazel_toolchains//rules:rbe_repo.bzl", "rbe_autoconfig") - -# Creates a default toolchain config for RBE. Use this as is if you are -# using the rbe_ubuntu16_04 container, otherwise refer to RBE docs. -rbe_autoconfig(name = "rbe_default")
diff --git a/cc/WORKSPACE.bzlmod b/cc/WORKSPACE.bzlmod new file mode 100644 index 0000000..057d6aa --- /dev/null +++ b/cc/WORKSPACE.bzlmod
@@ -0,0 +1 @@ +# This replaces the content of the WORKSPACE file when using --enable_bzlmod.
diff --git a/cc/aead.h b/cc/aead.h index 803fc51..45017d6 100644 --- a/cc/aead.h +++ b/cc/aead.h
@@ -51,7 +51,7 @@ absl::string_view ciphertext, absl::string_view associated_data) const = 0; - virtual ~Aead() {} + virtual ~Aead() = default; }; } // namespace tink
diff --git a/cc/aead/BUILD.bazel b/cc/aead/BUILD.bazel index 28b3e8b..10648c4 100644 --- a/cc/aead/BUILD.bazel +++ b/cc/aead/BUILD.bazel
@@ -214,6 +214,7 @@ "//subtle:random", "//util:constants", "//util:enums", + "//util:input_stream_util", "//util:secret_data", "//util:status", "//util:statusor", @@ -281,6 +282,7 @@ deps = [ "//:aead", "//:registry", + "//aead/internal:aead_util", "//proto:tink_cc_proto", "//util:status", "//util:statusor", @@ -302,6 +304,7 @@ "//:core/template_util", "//:kms_client", "//:kms_clients", + "//aead/internal:aead_util", "//internal:fips_utils", "//proto:kms_envelope_cc_proto", "//proto:tink_cc_proto", @@ -329,6 +332,95 @@ ], ) +cc_library( + name = "failing_aead", + testonly = 1, + srcs = ["failing_aead.cc"], + hdrs = ["failing_aead.h"], + include_prefix = "tink/aead", + visibility = ["//visibility:public"], + deps = [ + "//:aead", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "aead_parameters", + hdrs = ["aead_parameters.h"], + include_prefix = "tink/aead", + deps = ["//:parameters"], +) + +cc_library( + name = "aead_key", + hdrs = ["aead_key.h"], + include_prefix = "tink/aead", + deps = [ + ":aead_parameters", + "//:key", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "aes_gcm_parameters", + srcs = ["aes_gcm_parameters.cc"], + hdrs = ["aes_gcm_parameters.h"], + include_prefix = "tink/aead", + deps = [ + ":aead_parameters", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "aes_gcm_key", + srcs = ["aes_gcm_key.cc"], + hdrs = ["aes_gcm_key.h"], + include_prefix = "tink/aead", + deps = [ + ":aead_key", + ":aes_gcm_parameters", + "//:partial_key_access_token", + "//:restricted_data", + "//subtle:subtle_util", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + ], +) + +cc_library( + name = "aes_gcm_proto_serialization", + srcs = ["aes_gcm_proto_serialization.cc"], + hdrs = ["aes_gcm_proto_serialization.h"], + include_prefix = "tink/aead", + deps = [ + ":aes_gcm_key", + ":aes_gcm_parameters", + "//:partial_key_access", + "//:restricted_data", + "//:secret_key_access_token", + "//internal:key_parser", + "//internal:key_serializer", + "//internal:mutable_serialization_registry", + "//internal:parameters_parser", + "//internal:parameters_serializer", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:aes_gcm_cc_proto", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + ], +) + # tests cc_test( @@ -373,11 +465,11 @@ "//:primitive_set", "//:registry", "//config:tink_fips", + "//internal:fips_utils", "//proto:tink_cc_proto", "//util:status", "//util:statusor", "//util:test_matchers", - "//util:test_util", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_googletest//:gtest_main", @@ -516,7 +608,6 @@ deps = [ ":aes_ctr_hmac_aead_key_manager", "//:aead", - "//:mac", "//proto:aes_ctr_cc_proto", "//proto:aes_ctr_hmac_aead_cc_proto", "//proto:common_cc_proto", @@ -526,6 +617,7 @@ "//subtle:aead_test_util", "//subtle:aes_ctr_boringssl", "//util:enums", + "//util:istream_input_stream", "//util:secret_data", "//util:status", "//util:statusor", @@ -589,7 +681,9 @@ ":aead_key_templates", ":kms_envelope_aead", "//:aead", + "//:keyset_handle", "//:registry", + "//internal:ssl_util", "//mac:mac_key_templates", "//proto:aes_gcm_cc_proto", "//util:status", @@ -599,6 +693,7 @@ "@com_google_absl//absl/base:endian", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], ) @@ -608,6 +703,7 @@ size = "small", srcs = ["kms_envelope_aead_key_manager_test.cc"], deps = [ + ":aead_config", ":aead_key_templates", ":aes_eax_key_manager", ":kms_envelope_aead", @@ -616,9 +712,11 @@ "//:kms_client", "//:kms_clients", "//:registry", + "//mac:mac_key_templates", "//proto:kms_envelope_cc_proto", "//proto:tink_cc_proto", "//subtle:aead_test_util", + "//util:fake_kms_client", "//util:status", "//util:statusor", "//util:test_matchers", @@ -650,19 +748,6 @@ ], ) -cc_library( - name = "failing_aead", - testonly = 1, - srcs = ["failing_aead.cc"], - hdrs = ["failing_aead.h"], - include_prefix = "tink/aead", - visibility = ["//visibility:public"], - deps = [ - "//:aead", - "@com_google_absl//absl/strings", - ], -) - cc_test( name = "failing_aead_test", srcs = ["failing_aead_test.cc"], @@ -673,3 +758,51 @@ "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "aes_gcm_parameters_test", + srcs = ["aes_gcm_parameters_test.cc"], + deps = [ + ":aes_gcm_parameters", + "//util:statusor", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "aes_gcm_key_test", + srcs = ["aes_gcm_key_test.cc"], + deps = [ + ":aes_gcm_key", + ":aes_gcm_parameters", + "//:partial_key_access", + "//:restricted_data", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "aes_gcm_proto_serialization_test", + size = "small", + srcs = ["aes_gcm_proto_serialization_test.cc"], + deps = [ + ":aes_gcm_key", + ":aes_gcm_parameters", + ":aes_gcm_proto_serialization", + "//:insecure_secret_key_access", + "//:partial_key_access", + "//:restricted_data", + "//internal:mutable_serialization_registry", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:aes_gcm_cc_proto", + "//proto:tink_cc_proto", + "//subtle:random", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/cc/aead/BUILD.gn b/cc/aead/BUILD.gn index f4dc781..6eae32e 100644 --- a/cc/aead/BUILD.gn +++ b/cc/aead/BUILD.gn
@@ -204,6 +204,7 @@ "//third_party/tink/cc/subtle:random", "//third_party/tink/cc/util:constants", "//third_party/tink/cc/util:enums", + "//third_party/tink/cc/util:input_stream_util", "//third_party/tink/cc/util:secret_data", "//third_party/tink/cc/util:status", "//third_party/tink/cc/util:statusor", @@ -277,6 +278,7 @@ "//third_party/abseil-cpp/absl/strings:strings", "//third_party/tink/cc:aead", "//third_party/tink/cc:registry", + "//third_party/tink/cc/aead/internal:aead_util", "//third_party/tink/cc/proto:tink_proto", "//third_party/tink/cc/util:status", "//third_party/tink/cc/util:statusor", @@ -302,6 +304,7 @@ "//third_party/tink/cc:core/template_util", "//third_party/tink/cc:kms_client", "//third_party/tink/cc:kms_clients", + "//third_party/tink/cc/aead/internal:aead_util", "//third_party/tink/cc/internal:fips_utils", "//third_party/tink/cc/proto:kms_envelope_proto", "//third_party/tink/cc/proto:tink_proto",
diff --git a/cc/aead/CMakeLists.txt b/cc/aead/CMakeLists.txt index c4adc4e..41264b9 100644 --- a/cc/aead/CMakeLists.txt +++ b/cc/aead/CMakeLists.txt
@@ -199,6 +199,7 @@ tink::subtle::random tink::util::constants tink::util::enums + tink::util::input_stream_util tink::util::secret_data tink::util::status tink::util::statusor @@ -266,6 +267,7 @@ absl::strings tink::core::aead tink::core::registry + tink::aead::internal::aead_util tink::util::status tink::util::statusor tink::proto::tink_cc_proto @@ -286,6 +288,7 @@ tink::core::template_util tink::core::kms_client tink::core::kms_clients + tink::aead::internal::aead_util tink::internal::fips_utils tink::util::constants tink::util::status @@ -304,6 +307,91 @@ absl::strings tink::core::aead tink::util::statusor + TESTONLY +) + +tink_cc_library( + NAME failing_aead + SRCS + failing_aead.cc + failing_aead.h + DEPS + absl::strings + tink::core::aead + TESTONLY +) + +tink_cc_library( + NAME aead_parameters + SRCS + aead_parameters.h + DEPS + tink::core::parameters +) + +tink_cc_library( + NAME aead_key + SRCS + aead_key.h + DEPS + tink::aead::aead_parameters + absl::strings + tink::core::key +) + +tink_cc_library( + NAME aes_gcm_parameters + SRCS + aes_gcm_parameters.cc + aes_gcm_parameters.h + DEPS + tink::aead::aead_parameters + absl::strings + tink::util::status + tink::util::statusor +) + +tink_cc_library( + NAME aes_gcm_key + SRCS + aes_gcm_key.cc + aes_gcm_key.h + DEPS + tink::aead::aead_key + tink::aead::aes_gcm_parameters + absl::strings + absl::optional + tink::core::partial_key_access_token + tink::core::restricted_data + tink::subtle::subtle_util + tink::util::status + tink::util::statusor +) + +tink_cc_library( + NAME aes_gcm_proto_serialization + SRCS + aes_gcm_proto_serialization.cc + aes_gcm_proto_serialization.h + DEPS + tink::aead::aes_gcm_key + tink::aead::aes_gcm_parameters + absl::status + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::core::secret_key_access_token + tink::internal::key_parser + tink::internal::key_serializer + tink::internal::mutable_serialization_registry + tink::internal::parameters_parser + tink::internal::parameters_serializer + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::util::status + tink::util::statusor + tink::proto::aes_gcm_cc_proto + tink::proto::tink_cc_proto ) # tests @@ -351,10 +439,10 @@ tink::core::primitive_set tink::core::registry tink::config::tink_fips + tink::internal::fips_utils tink::util::status tink::util::statusor tink::util::test_matchers - tink::util::test_util tink::proto::tink_cc_proto ) @@ -487,11 +575,11 @@ gmock absl::status tink::core::aead - tink::core::mac tink::subtle::subtle tink::subtle::aead_test_util tink::subtle::aes_ctr_boringssl tink::util::enums + tink::util::istream_input_stream tink::util::secret_data tink::util::status tink::util::statusor @@ -558,8 +646,11 @@ absl::endian absl::memory absl::status + absl::strings tink::core::aead + tink::core::keyset_handle tink::core::registry + tink::internal::ssl_util tink::mac::mac_key_templates tink::util::status tink::util::statusor @@ -573,6 +664,7 @@ SRCS kms_envelope_aead_key_manager_test.cc DEPS + tink::aead::aead_config tink::aead::aead_key_templates tink::aead::aes_eax_key_manager tink::aead::kms_envelope_aead @@ -584,7 +676,9 @@ tink::core::kms_client tink::core::kms_clients tink::core::registry + tink::mac::mac_key_templates tink::subtle::aead_test_util + tink::util::fake_kms_client tink::util::status tink::util::statusor tink::util::test_matchers @@ -612,16 +706,6 @@ tink::proto::tink_cc_proto ) -tink_cc_library( - NAME failing_aead - SRCS - failing_aead.cc - failing_aead.h - DEPS - absl::strings - tink::core::aead -) - tink_cc_test( NAME failing_aead_test SRCS @@ -632,3 +716,50 @@ absl::status tink::util::test_matchers ) + +tink_cc_test( + NAME aes_gcm_parameters_test + SRCS + aes_gcm_parameters_test.cc + DEPS + tink::aead::aes_gcm_parameters + gmock + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_test( + NAME aes_gcm_key_test + SRCS + aes_gcm_key_test.cc + DEPS + tink::aead::aes_gcm_key + tink::aead::aes_gcm_parameters + gmock + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_test( + NAME aes_gcm_proto_serialization_test + SRCS + aes_gcm_proto_serialization_test.cc + DEPS + tink::aead::aes_gcm_key + tink::aead::aes_gcm_parameters + tink::aead::aes_gcm_proto_serialization + gmock + tink::core::insecure_secret_key_access + tink::core::partial_key_access + tink::core::restricted_data + tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::subtle::random + tink::util::test_matchers + tink::proto::aes_gcm_cc_proto + tink::proto::tink_cc_proto +)
diff --git a/cc/aead/aead_config.cc b/cc/aead/aead_config.cc index d627064..b5a84ca 100644 --- a/cc/aead/aead_config.cc +++ b/cc/aead/aead_config.cc
@@ -34,15 +34,6 @@ namespace crypto { namespace tink { - -using ::google::crypto::tink::RegistryConfig; - -// static -const RegistryConfig& AeadConfig::Latest() { - static const RegistryConfig* config = new RegistryConfig(); - return *config; -} - // static util::Status AeadConfig::Register() { auto status = MacConfig::Register();
diff --git a/cc/aead/aead_config.h b/cc/aead/aead_config.h index 6431d64..08cb196 100644 --- a/cc/aead/aead_config.h +++ b/cc/aead/aead_config.h
@@ -34,14 +34,6 @@ // class AeadConfig { public: - static constexpr char kCatalogueName[] = "TinkAead"; - static constexpr char kPrimitiveName[] = "Aead"; - - // Returns config of Aead implementations supported - // in the current Tink release. - ABSL_DEPRECATED("This is not supported anymore.") - static const google::crypto::tink::RegistryConfig& Latest(); - // Registers Aead primitive wrapper and key managers for all Aead key types // from the current Tink release. static crypto::tink::util::Status Register();
diff --git a/cc/aead/aead_config_test.cc b/cc/aead/aead_config_test.cc index 726e3d1..4a7945d 100644 --- a/cc/aead/aead_config_test.cc +++ b/cc/aead/aead_config_test.cc
@@ -29,27 +29,24 @@ #include "tink/aead/aead_key_templates.h" #include "tink/aead/aes_gcm_key_manager.h" #include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/keyset_handle.h" #include "tink/primitive_set.h" #include "tink/registry.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" -#include "tink/util/test_util.h" #include "proto/tink.pb.h" namespace crypto { namespace tink { namespace { -using ::crypto::tink::test::DummyAead; using ::crypto::tink::test::IsOk; using ::crypto::tink::test::StatusIs; -using ::google::crypto::tink::KeysetInfo; -using ::google::crypto::tink::KeyStatusType; +using ::crypto::tink::util::StatusOr; using ::google::crypto::tink::KeyTemplate; -using ::google::crypto::tink::OutputPrefixType; -using ::testing::Eq; +using ::testing::IsNull; using ::testing::Not; using ::testing::Test; @@ -80,35 +77,17 @@ ASSERT_THAT(AeadConfig::Register(), IsOk()); - KeysetInfo::KeyInfo key_info; - key_info.set_status(KeyStatusType::ENABLED); - key_info.set_key_id(1234); - key_info.set_output_prefix_type(OutputPrefixType::RAW); - auto primitive_set = absl::make_unique<PrimitiveSet<Aead>>(); - ASSERT_THAT(primitive_set->set_primary(*primitive_set->AddPrimitive( - absl::make_unique<DummyAead>("dummy"), key_info)), - IsOk()); - - util::StatusOr<std::unique_ptr<Aead>> primitive_result = - Registry::Wrap(std::move(primitive_set)); - - ASSERT_THAT(primitive_result, IsOk()); - util::StatusOr<std::string> encryption_result = - (*primitive_result)->Encrypt("secret", ""); - ASSERT_THAT(encryption_result, IsOk()); - - util::StatusOr<std::string> decryption_result = - DummyAead("dummy").Decrypt(*encryption_result, ""); - ASSERT_THAT(decryption_result, IsOk()); - EXPECT_THAT(*decryption_result, Eq("secret")); - - decryption_result = DummyAead("dummy").Decrypt(*encryption_result, "wrong"); - EXPECT_THAT(decryption_result, Not(IsOk())); + StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(keyset_handle.status(), IsOk()); + StatusOr<std::unique_ptr<Aead>> aead = (*keyset_handle)->GetPrimitive<Aead>(); + ASSERT_THAT(aead.status(), IsOk()); + ASSERT_THAT(*aead, Not(IsNull())); } // FIPS-only mode tests TEST_F(AeadConfigTest, RegisterNonFipsTemplates) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-only mode with BoringCrypto."; } @@ -128,7 +107,7 @@ } TEST_F(AeadConfigTest, RegisterFipsValidTemplates) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-only mode with BoringCrypto."; } @@ -148,7 +127,7 @@ } TEST_F(AeadConfigTest, RegisterFailsIfBoringCryptoNotAvailable) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-only mode with BoringCrypto not available."; }
diff --git a/cc/aead/aead_key.h b/cc/aead/aead_key.h new file mode 100644 index 0000000..869ddc8 --- /dev/null +++ b/cc/aead/aead_key.h
@@ -0,0 +1,52 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_AEAD_AEAD_KEY_H_ +#define TINK_AEAD_AEAD_KEY_H_ + +#include "absl/strings/string_view.h" +#include "tink/aead/aead_parameters.h" +#include "tink/key.h" + +namespace crypto { +namespace tink { + +// Represents a function to encrypt and decrypt data using authenticated +// encryption with associated data (AEAD). +class AeadKey : public Key { + public: + // Returns the bytes prefixed to every ciphertext generated by this key. + // + // In order to make key rotation more efficient, Tink allows every AEAD key to + // have an associated ciphertext output prefix. When decrypting a ciphertext, + // only keys with a matching prefix have to be tried. + // + // Note that a priori, the output prefix may not be unique in a keyset + // (i.e., different keys in a keyset may have the same prefix or one prefix + // may be a prefix of another). To avoid this, built-in Tink keys use the + // convention that the prefix is either '0x00<big endian key id>' or + // '0x01<big endian key id>'. + virtual absl::string_view GetOutputPrefix() const = 0; + + const AeadParameters& GetParameters() const override = 0; + + bool operator==(const Key& other) const override = 0; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_AEAD_AEAD_KEY_H_
diff --git a/cc/aead/aead_parameters.h b/cc/aead/aead_parameters.h new file mode 100644 index 0000000..dc5caad --- /dev/null +++ b/cc/aead/aead_parameters.h
@@ -0,0 +1,32 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_AEAD_AEAD_PARAMETERS_H_ +#define TINK_AEAD_AEAD_PARAMETERS_H_ + +#include "tink/parameters.h" + +namespace crypto { +namespace tink { + +// Describes an `AeadKey` (e.g., key attributes), excluding the randomly chosen +// key material. +class AeadParameters : public Parameters {}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_AEAD_AEAD_PARAMETERS_H_
diff --git a/cc/aead/aes_ctr_hmac_aead_key_manager.cc b/cc/aead/aes_ctr_hmac_aead_key_manager.cc index ffe7867..10a0634 100644 --- a/cc/aead/aes_ctr_hmac_aead_key_manager.cc +++ b/cc/aead/aes_ctr_hmac_aead_key_manager.cc
@@ -34,6 +34,7 @@ #include "tink/subtle/ind_cpa_cipher.h" #include "tink/subtle/random.h" #include "tink/util/enums.h" +#include "tink/util/input_stream_util.h" #include "tink/util/secret_data.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -52,12 +53,14 @@ constexpr int kMinTagSizeInBytes = 10; } -using crypto::tink::util::Enums; -using crypto::tink::util::Status; -using crypto::tink::util::StatusOr; -using google::crypto::tink::AesCtrHmacAeadKey; -using google::crypto::tink::AesCtrHmacAeadKeyFormat; -using google::crypto::tink::HashType; +using ::crypto::tink::util::Enums; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; +using ::google::crypto::tink::AesCtrHmacAeadKey; +using ::google::crypto::tink::AesCtrHmacAeadKeyFormat; +using ::google::crypto::tink::AesCtrKey; +using ::google::crypto::tink::HashType; +using ::google::crypto::tink::HmacKey; StatusOr<AesCtrHmacAeadKey> AesCtrHmacAeadKeyManager::CreateKey( const AesCtrHmacAeadKeyFormat& aes_ctr_hmac_aead_key_format) const { @@ -176,5 +179,53 @@ return HmacKeyManager().ValidateKeyFormat(key_format.hmac_key_format()); } +// To ensure the resulting key can provide key commitment, the AES-CTR key must +// be derived first, then the HMAC key. This avoids situation where it's +// possible to brute force raw key material so that the 32th byte of the +// keystream is a 0 Give party A a key with this raw key material, saying that +// the size of the HMAC key is 32 bytes and the size of the AES key is 16 bytes. +// Give party B a key with this raw key material, saying that the size of the +// HMAC key is 31 bytes and the size of the AES key is 16 bytes. Since HMAC will +// pad the key with zeroes, this leads to both parties using the same HMAC key, +// but a different AES key (offset by 1 byte) +StatusOr<AesCtrHmacAeadKey> AesCtrHmacAeadKeyManager::DeriveKey( + const AesCtrHmacAeadKeyFormat& key_format, + InputStream* input_stream) const { + Status status = ValidateKeyFormat(key_format); + if (!status.ok()) { + return status; + } + StatusOr<std::string> aes_ctr_randomness = ReadBytesFromStream( + key_format.aes_ctr_key_format().key_size(), input_stream); + if (!aes_ctr_randomness.ok()) { + if (absl::IsOutOfRange(aes_ctr_randomness.status())) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + "Could not get enough pseudorandomness from input stream"); + } + return aes_ctr_randomness.status(); + } + StatusOr<HmacKey> hmac_key = + HmacKeyManager().DeriveKey(key_format.hmac_key_format(), input_stream); + if (!hmac_key.ok()) { + return hmac_key.status(); + } + + google::crypto::tink::AesCtrHmacAeadKey key; + key.set_version(get_version()); + *key.mutable_hmac_key() = hmac_key.value(); + + AesCtrKey* aes_ctr_key = key.mutable_aes_ctr_key(); + aes_ctr_key->set_version(get_version()); + aes_ctr_key->set_key_value(aes_ctr_randomness.value()); + *aes_ctr_key->mutable_params() = key_format.aes_ctr_key_format().params(); + + status = ValidateKey(key); + if (!status.ok()) { + return status; + } + return key; +} + } // namespace tink } // namespace crypto
diff --git a/cc/aead/aes_ctr_hmac_aead_key_manager.h b/cc/aead/aes_ctr_hmac_aead_key_manager.h index a241a9c..e3b2f42 100644 --- a/cc/aead/aes_ctr_hmac_aead_key_manager.h +++ b/cc/aead/aes_ctr_hmac_aead_key_manager.h
@@ -67,6 +67,10 @@ CreateKey(const google::crypto::tink::AesCtrHmacAeadKeyFormat& key_format) const override; + crypto::tink::util::StatusOr<google::crypto::tink::AesCtrHmacAeadKey> + DeriveKey(const google::crypto::tink::AesCtrHmacAeadKeyFormat& key_format, + InputStream* input_stream) const override; + internal::FipsCompatibility FipsStatus() const override { return internal::FipsCompatibility::kRequiresBoringCrypto; }
diff --git a/cc/aead/aes_ctr_hmac_aead_key_manager_test.cc b/cc/aead/aes_ctr_hmac_aead_key_manager_test.cc index 6815ec3..b84711a 100644 --- a/cc/aead/aes_ctr_hmac_aead_key_manager_test.cc +++ b/cc/aead/aes_ctr_hmac_aead_key_manager_test.cc
@@ -19,6 +19,7 @@ #include <stdint.h> #include <memory> +#include <sstream> #include <string> #include <utility> @@ -26,13 +27,13 @@ #include "gtest/gtest.h" #include "absl/status/status.h" #include "tink/aead.h" -#include "tink/mac.h" #include "tink/subtle/aead_test_util.h" #include "tink/subtle/aes_ctr_boringssl.h" #include "tink/subtle/encrypt_then_authenticate.h" #include "tink/subtle/hmac_boringssl.h" #include "tink/subtle/ind_cpa_cipher.h" #include "tink/util/enums.h" +#include "tink/util/istream_input_stream.h" #include "tink/util/secret_data.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -48,11 +49,11 @@ using ::crypto::tink::test::IsOk; using ::crypto::tink::test::StatusIs; +using ::crypto::tink::util::IstreamInputStream; using ::crypto::tink::util::StatusOr; using ::google::crypto::tink::AesCtrHmacAeadKey; using ::google::crypto::tink::AesCtrHmacAeadKeyFormat; using ::google::crypto::tink::HashType; -using ::google::crypto::tink::KeyData; using ::testing::Eq; using ::testing::Not; using ::testing::SizeIs; @@ -146,14 +147,24 @@ AesCtrHmacAeadKeyFormat key_format = CreateValidKeyFormat(); for (int len = 0; len < 42; ++len) { key_format.mutable_aes_ctr_key_format()->set_key_size(len); + IstreamInputStream input_stream{absl::make_unique<std::stringstream>( + "0123456789abcde0123456789abcdefghijklmnopqrztuvwxyz0123456789abcde01" + "23456789abcdefghijklmnopqrztuvwxyz0123456789abcde0123456789abcdefghi" + "jklmnopqrztuvwxyz")}; if (len == 16 || len == 32) { EXPECT_THAT(AesCtrHmacAeadKeyManager().ValidateKeyFormat(key_format), IsOk()) << "for length " << len; + EXPECT_THAT( + AesCtrHmacAeadKeyManager().DeriveKey(key_format, &input_stream), + IsOk()); } else { EXPECT_THAT(AesCtrHmacAeadKeyManager().ValidateKeyFormat(key_format), Not(IsOk())) << "for length " << len; + EXPECT_THAT( + AesCtrHmacAeadKeyManager().DeriveKey(key_format, &input_stream), + Not(IsOk())); } } } @@ -162,14 +173,24 @@ AesCtrHmacAeadKeyFormat key_format = CreateValidKeyFormat(); for (int len = 0; len < 42; ++len) { key_format.mutable_hmac_key_format()->set_key_size(len); + IstreamInputStream input_stream{absl::make_unique<std::stringstream>( + "0123456789abcde0123456789abcdefghijklmnopqrztuvwxyz0123456789abcde01" + "23456789abcdefghijklmnopqrztuvwxyz0123456789abcde0123456789abcdefghi" + "jklmnopqrztuvwxyz")}; if (len >= 16) { EXPECT_THAT(AesCtrHmacAeadKeyManager().ValidateKeyFormat(key_format), IsOk()) << "for length " << len; + EXPECT_THAT( + AesCtrHmacAeadKeyManager().DeriveKey(key_format, &input_stream), + IsOk()); } else { EXPECT_THAT(AesCtrHmacAeadKeyManager().ValidateKeyFormat(key_format), Not(IsOk())) << "for length " << len; + EXPECT_THAT( + AesCtrHmacAeadKeyManager().DeriveKey(key_format, &input_stream), + Not(IsOk())); } } } @@ -222,6 +243,92 @@ IsOk()); } +TEST(AesCtrHmacAeadKeyManagerTest, Derive16ByteKey) { + AesCtrHmacAeadKeyFormat key_format; + key_format.mutable_aes_ctr_key_format()->set_key_size(16); + key_format.mutable_aes_ctr_key_format()->mutable_params()->set_iv_size(16); + key_format.mutable_hmac_key_format()->set_key_size(16); + key_format.mutable_hmac_key_format()->mutable_params()->set_tag_size(16); + key_format.mutable_hmac_key_format()->mutable_params()->set_hash( + google::crypto::tink::SHA256); + key_format.mutable_hmac_key_format()->set_version(0); + + IstreamInputStream input_stream{absl::make_unique<std::stringstream>( + "0123456789abcde_YELLOW_SUBMARINE_EXTRA")}; + + StatusOr<AesCtrHmacAeadKey> derived_key = + AesCtrHmacAeadKeyManager().DeriveKey(key_format, &input_stream); + ASSERT_THAT(derived_key, IsOk()); + EXPECT_THAT(derived_key.value().aes_ctr_key().key_value(), + Eq("0123456789abcde_")); + EXPECT_THAT(derived_key.value().hmac_key().key_value(), + Eq("YELLOW_SUBMARINE")); + EXPECT_THAT(derived_key.value().hmac_key().params().hash(), + key_format.hmac_key_format().params().hash()); + EXPECT_THAT(derived_key.value().hmac_key().params().tag_size(), + key_format.hmac_key_format().params().tag_size()); + EXPECT_THAT(derived_key.value().aes_ctr_key().params().iv_size(), + Eq(key_format.aes_ctr_key_format().params().iv_size())); +} + +TEST(AesCtrHmacAeadKeyManagerTest, Derive32ByteKey) { + AesCtrHmacAeadKeyFormat format; + format.mutable_aes_ctr_key_format()->set_key_size(32); + format.mutable_aes_ctr_key_format()->mutable_params()->set_iv_size(16); + format.mutable_hmac_key_format()->set_key_size(32); + format.mutable_hmac_key_format()->mutable_params()->set_tag_size(16); + format.mutable_hmac_key_format()->mutable_params()->set_hash( + google::crypto::tink::SHA256); + format.mutable_hmac_key_format()->set_version(0); + + IstreamInputStream input_stream{absl::make_unique<std::stringstream>( + "0123456789abcde0123456789abcdef_YELLOW_SUBMARINE_YELLOW_SUBMARIN")}; + + StatusOr<AesCtrHmacAeadKey> derived_key = + AesCtrHmacAeadKeyManager().DeriveKey(format, &input_stream); + ASSERT_THAT(derived_key, IsOk()); + EXPECT_THAT(derived_key.value().aes_ctr_key().key_value(), + Eq("0123456789abcde0123456789abcdef_")); + EXPECT_THAT(derived_key.value().hmac_key().key_value(), + Eq("YELLOW_SUBMARINE_YELLOW_SUBMARIN")); +} + +TEST(AesCtrHmacAeadKeyManagerTest, DeriveKeyNotEnoughRandomnessForAesCtrKey) { + AesCtrHmacAeadKeyFormat format; + format.mutable_aes_ctr_key_format()->set_key_size(32); + format.mutable_aes_ctr_key_format()->mutable_params()->set_iv_size(16); + format.mutable_hmac_key_format()->set_key_size(32); + format.mutable_hmac_key_format()->mutable_params()->set_tag_size(16); + format.mutable_hmac_key_format()->mutable_params()->set_hash( + google::crypto::tink::SHA256); + format.mutable_hmac_key_format()->set_version(0); + + IstreamInputStream input_stream{ + absl::make_unique<std::stringstream>("0123456789")}; + + ASSERT_THAT( + AesCtrHmacAeadKeyManager().DeriveKey(format, &input_stream).status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesCtrHmacAeadKeyManagerTest, DeriveKeyNotEnoughRandomnessForHmacKey) { + AesCtrHmacAeadKeyFormat format; + format.mutable_aes_ctr_key_format()->set_key_size(16); + format.mutable_aes_ctr_key_format()->mutable_params()->set_iv_size(16); + format.mutable_hmac_key_format()->set_key_size(32); + format.mutable_hmac_key_format()->mutable_params()->set_tag_size(16); + format.mutable_hmac_key_format()->mutable_params()->set_hash( + google::crypto::tink::SHA256); + format.mutable_hmac_key_format()->set_version(0); + + IstreamInputStream input_stream{ + absl::make_unique<std::stringstream>("YELLOW_SUBMARINE")}; + + ASSERT_THAT( + AesCtrHmacAeadKeyManager().DeriveKey(format, &input_stream).status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + } // namespace } // namespace tink } // namespace crypto
diff --git a/cc/aead/aes_gcm_key.cc b/cc/aead/aes_gcm_key.cc new file mode 100644 index 0000000..4e93443 --- /dev/null +++ b/cc/aead/aes_gcm_key.cc
@@ -0,0 +1,107 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/aead/aes_gcm_key.h" + +#include <string> + +#include "absl/strings/escaping.h" +#include "absl/types/optional.h" +#include "tink/aead/aes_gcm_parameters.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/subtle/subtle_util.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace { + +util::StatusOr<std::string> ComputeOutputPrefix( + const AesGcmParameters& parameters, absl::optional<int> id_requirement) { + switch (parameters.GetVariant()) { + case AesGcmParameters::Variant::kNoPrefix: + return std::string(""); // Empty prefix. + case AesGcmParameters::Variant::kCrunchy: + if (!id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "id requirement must have value with kCrunchy or kLegacy"); + } + return absl::StrCat(absl::HexStringToBytes("00"), + subtle::BigEndian32(*id_requirement)); + case AesGcmParameters::Variant::kTink: + if (!id_requirement.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "id requirement must have value with kTink"); + } + return absl::StrCat(absl::HexStringToBytes("01"), + subtle::BigEndian32(*id_requirement)); + default: + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Invalid variant: ", parameters.GetVariant())); + } +} + +} // namespace + +util::StatusOr<AesGcmKey> AesGcmKey::Create(const AesGcmParameters& parameters, + const RestrictedData& key_bytes, + absl::optional<int> id_requirement, + PartialKeyAccessToken token) { + if (parameters.KeySizeInBytes() != key_bytes.size()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Key size does not match AES-GCM parameters"); + } + if (parameters.HasIdRequirement() && !id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key without ID requirement with parameters with ID " + "requirement"); + } + if (!parameters.HasIdRequirement() && id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key with ID requirement with parameters without ID " + "requirement"); + } + util::StatusOr<std::string> output_prefix = + ComputeOutputPrefix(parameters, id_requirement); + if (!output_prefix.ok()) { + return output_prefix.status(); + } + return AesGcmKey(parameters, key_bytes, id_requirement, + *std::move(output_prefix)); +} + +bool AesGcmKey::operator==(const Key& other) const { + const AesGcmKey* that = dynamic_cast<const AesGcmKey*>(&other); + if (that == nullptr) { + return false; + } + if (GetParameters() != that->GetParameters()) { + return false; + } + if (id_requirement_ != that->id_requirement_) { + return false; + } + return key_bytes_ == that->key_bytes_; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/aead/aes_gcm_key.h b/cc/aead/aes_gcm_key.h new file mode 100644 index 0000000..8159ea7 --- /dev/null +++ b/cc/aead/aes_gcm_key.h
@@ -0,0 +1,84 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_AEAD_AES_GCM_KEY_H_ +#define TINK_AEAD_AES_GCM_KEY_H_ + +#include <string> +#include <utility> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/aead/aead_key.h" +#include "tink/aead/aes_gcm_parameters.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +// Represents an AEAD that uses AES-GCM. +class AesGcmKey : public AeadKey { + public: + // Copyable and movable. + AesGcmKey(const AesGcmKey& other) = default; + AesGcmKey& operator=(const AesGcmKey& other) = default; + AesGcmKey(AesGcmKey&& other) = default; + AesGcmKey& operator=(AesGcmKey&& other) = default; + + // Creates a new AES-GCM key. If the parameters specify a variant that uses + // a prefix, then the id is used to compute this prefix. + static util::StatusOr<AesGcmKey> Create(const AesGcmParameters& parameters, + const RestrictedData& key_bytes, + absl::optional<int> id_requirement, + PartialKeyAccessToken token); + + // Returns the underlying AES key. + util::StatusOr<RestrictedData> GetKeyBytes( + PartialKeyAccessToken token) const { + return key_bytes_; + } + + absl::string_view GetOutputPrefix() const override { return output_prefix_; } + + const AesGcmParameters& GetParameters() const override { return parameters_; } + + absl::optional<int> GetIdRequirement() const override { + return id_requirement_; + } + + bool operator==(const Key& other) const override; + + private: + AesGcmKey(const AesGcmParameters& parameters, const RestrictedData& key_bytes, + absl::optional<int> id_requirement, + std::string output_prefix) + : parameters_(parameters), + key_bytes_(key_bytes), + id_requirement_(id_requirement), + output_prefix_(std::move(output_prefix)) {} + + AesGcmParameters parameters_; + RestrictedData key_bytes_; + absl::optional<int> id_requirement_; + std::string output_prefix_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_AEAD_AES_GCM_KEY_H_
diff --git a/cc/aead/aes_gcm_key_test.cc b/cc/aead/aes_gcm_key_test.cc new file mode 100644 index 0000000..06a0a35 --- /dev/null +++ b/cc/aead/aes_gcm_key_test.cc
@@ -0,0 +1,285 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/aead/aes_gcm_key.h" + +#include <string> +#include <tuple> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/types/optional.h" +#include "tink/aead/aes_gcm_parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::Range; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + AesGcmParameters::Variant variant; + absl::optional<int> id_requirement; + std::string output_prefix; +}; + +using AesGcmKeyTest = TestWithParam<std::tuple<int, int, TestCase>>; + +INSTANTIATE_TEST_SUITE_P( + AesGcmKeyTestSuite, AesGcmKeyTest, + Combine(Values(16, 24, 32), Range(12, 16), + Values(TestCase{AesGcmParameters::Variant::kTink, 0x02030400, + std::string("\x01\x02\x03\x04\x00", 5)}, + TestCase{AesGcmParameters::Variant::kCrunchy, 0x01030005, + std::string("\x00\x01\x03\x00\x05", 5)}, + TestCase{AesGcmParameters::Variant::kNoPrefix, absl::nullopt, + ""}))); + +TEST_P(AesGcmKeyTest, CreateSucceeds) { + int key_size; + int iv_and_tag_size; // NOTE: There's no requirement for IV size == tag size. + TestCase test_case; + std::tie(key_size, iv_and_tag_size, test_case) = GetParam(); + + util::StatusOr<AesGcmParameters> params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(key_size) + .SetIvSizeInBytes(iv_and_tag_size) + .SetTagSizeInBytes(iv_and_tag_size) + .SetVariant(test_case.variant) + .Build(); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + util::StatusOr<AesGcmKey> key = AesGcmKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + EXPECT_THAT(key->GetParameters(), Eq(*params)); + EXPECT_THAT(key->GetIdRequirement(), Eq(test_case.id_requirement)); + EXPECT_THAT(key->GetOutputPrefix(), Eq(test_case.output_prefix)); +} + +TEST(AesGcmKeyTest, CreateKeyWithMismatchedKeySizeFails) { + // Key size parameter is 32 bytes. + util::StatusOr<AesGcmParameters> params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(params, IsOk()); + + // Key material is 16 bytes (another valid key length). + RestrictedData mismatched_secret = RestrictedData(/*num_random_bytes=*/16); + + EXPECT_THAT(AesGcmKey::Create(*params, mismatched_secret, + /*id_requirement=*/123, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesGcmKeyTest, CreateKeyWithInvalidIdRequirementFails) { + util::StatusOr<AesGcmParameters> no_prefix_params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build(); + ASSERT_THAT(no_prefix_params, IsOk()); + + util::StatusOr<AesGcmParameters> tink_params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(tink_params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + EXPECT_THAT(AesGcmKey::Create(*no_prefix_params, secret, + /*id_requirement=*/123, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT( + AesGcmKey::Create(*tink_params, secret, + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesGcmKeyTest, GetKeyBytes) { + int key_size; + int iv_and_tag_size; // NOTE: There's no requirement for IV size == tag size. + TestCase test_case; + std::tie(key_size, iv_and_tag_size, test_case) = GetParam(); + + util::StatusOr<AesGcmParameters> params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(key_size) + .SetIvSizeInBytes(iv_and_tag_size) + .SetTagSizeInBytes(iv_and_tag_size) + .SetVariant(test_case.variant) + .Build(); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + + util::StatusOr<AesGcmKey> key = AesGcmKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + EXPECT_THAT(key->GetKeyBytes(GetPartialKeyAccess()), IsOkAndHolds(secret)); +} + +TEST_P(AesGcmKeyTest, KeyEquals) { + int key_size; + int iv_and_tag_size; // NOTE: There's no requirement for IV size == tag size. + TestCase test_case; + std::tie(key_size, iv_and_tag_size, test_case) = GetParam(); + + util::StatusOr<AesGcmParameters> params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(key_size) + .SetIvSizeInBytes(iv_and_tag_size) + .SetTagSizeInBytes(iv_and_tag_size) + .SetVariant(test_case.variant) + .Build(); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + util::StatusOr<AesGcmKey> key = AesGcmKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<AesGcmKey> other_key = AesGcmKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key == *other_key); + EXPECT_TRUE(*other_key == *key); + EXPECT_FALSE(*key != *other_key); + EXPECT_FALSE(*other_key != *key); +} + +TEST(AesGcmKeyTest, DifferentVariantNotEqual) { + util::StatusOr<AesGcmParameters> crunchy_params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kCrunchy) + .Build(); + ASSERT_THAT(crunchy_params, IsOk()); + + util::StatusOr<AesGcmParameters> tink_params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(tink_params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr<AesGcmKey> key = + AesGcmKey::Create(*crunchy_params, secret, /*id_requirement=*/0x01020304, + GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<AesGcmKey> other_key = + AesGcmKey::Create(*tink_params, secret, /*id_requirement=*/0x01020304, + GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(AesGcmKeyTest, DifferentSecretDataNotEqual) { + util::StatusOr<AesGcmParameters> params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret1 = RestrictedData(/*num_random_bytes=*/32); + RestrictedData secret2 = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr<AesGcmKey> key = AesGcmKey::Create( + *params, secret1, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<AesGcmKey> other_key = AesGcmKey::Create( + *params, secret2, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(AesGcmKeyTest, DifferentIdRequirementNotEqual) { + util::StatusOr<AesGcmParameters> params = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr<AesGcmKey> key = AesGcmKey::Create( + *params, secret, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<AesGcmKey> other_key = AesGcmKey::Create( + *params, secret, /*id_requirement=*/0x02030405, GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/aead/aes_gcm_parameters.cc b/cc/aead/aes_gcm_parameters.cc new file mode 100644 index 0000000..d78d888 --- /dev/null +++ b/cc/aead/aes_gcm_parameters.cc
@@ -0,0 +1,103 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/aead/aes_gcm_parameters.h" + +#include <set> + +#include "absl/strings/str_cat.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +AesGcmParameters::Builder& AesGcmParameters::Builder::SetKeySizeInBytes( + int key_size) { + key_size_in_bytes_ = key_size; + return *this; +} + +AesGcmParameters::Builder& AesGcmParameters::Builder::SetIvSizeInBytes( + int iv_size) { + iv_size_in_bytes_ = iv_size; + return *this; +} + +AesGcmParameters::Builder& AesGcmParameters::Builder::SetTagSizeInBytes( + int tag_size) { + tag_size_in_bytes_ = tag_size; + return *this; +} + +AesGcmParameters::Builder& AesGcmParameters::Builder::SetVariant( + Variant variant) { + variant_ = variant; + return *this; +} + +util::StatusOr<AesGcmParameters> AesGcmParameters::Builder::Build() { + if (key_size_in_bytes_ != 16 && key_size_in_bytes_ != 24 && + key_size_in_bytes_ != 32) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Key size should be 16, 24, or 32 bytes, got ", + key_size_in_bytes_, " bytes.")); + } + if (iv_size_in_bytes_ <= 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("IV size should be positive, got ", + iv_size_in_bytes_, " bytes.")); + } + if (tag_size_in_bytes_ < 12 || tag_size_in_bytes_ > 16) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Tag size should be between 12 and 16 bytes, got ", + tag_size_in_bytes_, " bytes.")); + } + static const std::set<Variant>* supported_variants = new std::set<Variant>( + {Variant::kTink, Variant::kCrunchy, Variant::kNoPrefix}); + if (supported_variants->find(variant_) == supported_variants->end()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create AES-GCM parameters with unknown variant."); + } + return AesGcmParameters(key_size_in_bytes_, iv_size_in_bytes_, + tag_size_in_bytes_, variant_); +} + +bool AesGcmParameters::operator==(const Parameters& other) const { + const AesGcmParameters* that = dynamic_cast<const AesGcmParameters*>(&other); + if (that == nullptr) { + return false; + } + if (key_size_in_bytes_ != that->key_size_in_bytes_) { + return false; + } + if (iv_size_in_bytes_ != that->iv_size_in_bytes_) { + return false; + } + if (tag_size_in_bytes_ != that->tag_size_in_bytes_) { + return false; + } + if (variant_ != that->variant_) { + return false; + } + return true; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/aead/aes_gcm_parameters.h b/cc/aead/aes_gcm_parameters.h new file mode 100644 index 0000000..de87b2c --- /dev/null +++ b/cc/aead/aes_gcm_parameters.h
@@ -0,0 +1,105 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_AEAD_AES_GCM_PARAMETERS_H_ +#define TINK_AEAD_AES_GCM_PARAMETERS_H_ + +#include "tink/aead/aead_parameters.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +// Describes the parameters of an `AesGcmKey`. +class AesGcmParameters : public AeadParameters { + public: + // Description of the output prefix prepended to the ciphertext. + enum class Variant : int { + // Prepends '0x01<big endian key id>' to the ciphertext. + kTink = 1, + // Prepends '0x00<big endian key id>' to the ciphertext. + kCrunchy = 2, + // Does not prepend any prefix (i.e., keys must have no ID requirement). + kNoPrefix = 3, + // Added to guard from failures that may be caused by future expansions. + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements = 20, + }; + + // Creates AES-GCM parameters instances. + class Builder { + public: + // Copyable and movable. + Builder(const Builder& other) = default; + Builder& operator=(const Builder& other) = default; + Builder(Builder&& other) = default; + Builder& operator=(Builder&& other) = default; + + // Creates initially empty parameters builder. + Builder() = default; + + Builder& SetKeySizeInBytes(int key_size); + Builder& SetIvSizeInBytes(int iv_size); + Builder& SetTagSizeInBytes(int tag_size); + Builder& SetVariant(Variant variant); + + // Creates AES-GCM parameters object from this builder. + util::StatusOr<AesGcmParameters> Build(); + + private: + int key_size_in_bytes_; + int iv_size_in_bytes_; + int tag_size_in_bytes_; + Variant variant_; + }; + + // Copyable and movable. + AesGcmParameters(const AesGcmParameters& other) = default; + AesGcmParameters& operator=(const AesGcmParameters& other) = default; + AesGcmParameters(AesGcmParameters&& other) = default; + AesGcmParameters& operator=(AesGcmParameters&& other) = default; + + int KeySizeInBytes() const { return key_size_in_bytes_; } + + int IvSizeInBytes() const { return iv_size_in_bytes_; } + + int TagSizeInBytes() const { return tag_size_in_bytes_; } + + Variant GetVariant() const { return variant_; } + + bool HasIdRequirement() const override { + return variant_ != Variant::kNoPrefix; + } + + bool operator==(const Parameters& other) const override; + + private: + AesGcmParameters(int key_size_in_bytes, int iv_size_in_bytes, + int tag_size_in_bytes, Variant variant) + : key_size_in_bytes_(key_size_in_bytes), + iv_size_in_bytes_(iv_size_in_bytes), + tag_size_in_bytes_(tag_size_in_bytes), + variant_(variant) {} + + int key_size_in_bytes_; + int iv_size_in_bytes_; + int tag_size_in_bytes_; + Variant variant_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_AEAD_AES_GCM_PARAMETERS_H_
diff --git a/cc/aead/aes_gcm_parameters_test.cc b/cc/aead/aes_gcm_parameters_test.cc new file mode 100644 index 0000000..7238bb9 --- /dev/null +++ b/cc/aead/aes_gcm_parameters_test.cc
@@ -0,0 +1,386 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/aead/aes_gcm_parameters.h" + +#include <tuple> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::Range; +using ::testing::TestWithParam; +using ::testing::Values; + +struct BuildTestCase { + AesGcmParameters::Variant variant; + int key_size; + int iv_size; + int tag_size; + bool has_id_requirement; +}; + +using AesGcmParametersBuildTest = TestWithParam<BuildTestCase>; + +INSTANTIATE_TEST_SUITE_P( + AesGcmParametersBuildTestSuite, AesGcmParametersBuildTest, + Values(BuildTestCase{AesGcmParameters::Variant::kTink, /*key_size=*/16, + /*iv_size=*/12, /*tag_size=*/12, + /*has_id_requirement=*/true}, + BuildTestCase{AesGcmParameters::Variant::kCrunchy, /*key_size=*/24, + /*iv_size=*/14, /*tag_size=*/14, + /*has_id_requirement=*/true}, + BuildTestCase{AesGcmParameters::Variant::kNoPrefix, + /*key_size=*/32, /*iv_size=*/16, /*tag_size=*/16, + /*has_id_requirement=*/false})); + +TEST_P(AesGcmParametersBuildTest, Build) { + BuildTestCase test_case = GetParam(); + + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(test_case.key_size) + .SetIvSizeInBytes(test_case.iv_size) + .SetTagSizeInBytes(test_case.tag_size) + .SetVariant(test_case.variant) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + EXPECT_THAT(parameters->KeySizeInBytes(), Eq(test_case.key_size)); + EXPECT_THAT(parameters->IvSizeInBytes(), Eq(test_case.iv_size)); + EXPECT_THAT(parameters->TagSizeInBytes(), Eq(test_case.tag_size)); + EXPECT_THAT(parameters->GetVariant(), Eq(test_case.variant)); + EXPECT_THAT(parameters->HasIdRequirement(), Eq(test_case.has_id_requirement)); +} + +TEST(AesGcmParametersTest, BuildWithoutSettingVariantFails) { + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesGcmParametersTest, BuildWithInvalidVariantFails) { + EXPECT_THAT( + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant:: + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesGcmParametersTest, BuildWithoutSettingKeySizeFails) { + EXPECT_THAT(AesGcmParameters::Builder() + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesGcmParametersTest, BuildWithInvalidKeySizeFails) { + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(15) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(17) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(23) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(25) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(31) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(33) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesGcmParametersTest, BuildWithoutSettingIvSizeFails) { + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesGcmParametersTest, BuildWithInvalidIvSizeFails) { + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(0) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesGcmParametersTest, BuildWithoutSettingTagSizeFails) { + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesGcmParametersTest, BuildWithInvalidTagSizeFails) { + // Too small. + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(11) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Too big. + EXPECT_THAT(AesGcmParameters::Builder() + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(17) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build() + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesGcmParametersTest, CopyConstructor) { + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + AesGcmParameters copy(*parameters); + EXPECT_THAT(copy.KeySizeInBytes(), Eq(16)); + EXPECT_THAT(copy.IvSizeInBytes(), Eq(16)); + EXPECT_THAT(copy.TagSizeInBytes(), Eq(16)); + EXPECT_THAT(copy.GetVariant(), Eq(AesGcmParameters::Variant::kTink)); + EXPECT_THAT(copy.HasIdRequirement(), IsTrue()); +} + +TEST(AesGcmParametersTest, CopyAssignment) { + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + AesGcmParameters copy = *parameters; + EXPECT_THAT(copy.KeySizeInBytes(), Eq(16)); + EXPECT_THAT(copy.IvSizeInBytes(), Eq(16)); + EXPECT_THAT(copy.TagSizeInBytes(), Eq(16)); + EXPECT_THAT(copy.GetVariant(), Eq(AesGcmParameters::Variant::kTink)); + EXPECT_THAT(copy.HasIdRequirement(), IsTrue()); +} + +using AesGcmParametersVariantTest = + TestWithParam<std::tuple<int, int, AesGcmParameters::Variant>>; + +INSTANTIATE_TEST_SUITE_P(AesGcmParametersVariantTestSuite, + AesGcmParametersVariantTest, + Combine(Values(16, 24, 32), Range(12, 16), + Values(AesGcmParameters::Variant::kTink, + AesGcmParameters::Variant::kCrunchy, + AesGcmParameters::Variant::kNoPrefix))); + +TEST_P(AesGcmParametersVariantTest, ParametersEquals) { + int key_size; + int iv_and_tag_size; + AesGcmParameters::Variant variant; + std::tie(key_size, iv_and_tag_size, variant) = GetParam(); + + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(key_size) + .SetIvSizeInBytes(iv_and_tag_size) + .SetTagSizeInBytes(iv_and_tag_size) + .SetVariant(variant) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<AesGcmParameters> other_parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(key_size) + .SetIvSizeInBytes(iv_and_tag_size) + .SetTagSizeInBytes(iv_and_tag_size) + .SetVariant(variant) + .Build(); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters == *other_parameters); + EXPECT_TRUE(*other_parameters == *parameters); + EXPECT_FALSE(*parameters != *other_parameters); + EXPECT_FALSE(*other_parameters != *parameters); +} + +TEST(AesGcmParametersTest, KeySizeNotEqual) { + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<AesGcmParameters> other_parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(24) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +TEST(AesGcmParametersTest, IvSizeNotEqual) { + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<AesGcmParameters> other_parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(12) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +TEST(AesGcmParametersTest, TagSizeNotEqual) { + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<AesGcmParameters> other_parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(14) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +TEST(AesGcmParametersTest, VariantNotEqual) { + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kTink) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<AesGcmParameters> other_parameters = + AesGcmParameters::Builder() + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(16) + .SetTagSizeInBytes(16) + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .Build(); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/aead/aes_gcm_proto_serialization.cc b/cc/aead/aes_gcm_proto_serialization.cc new file mode 100644 index 0000000..57272ff --- /dev/null +++ b/cc/aead/aes_gcm_proto_serialization.cc
@@ -0,0 +1,273 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/aead/aes_gcm_proto_serialization.h" + +#include <string> + +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/aead/aes_gcm_key.h" +#include "tink/aead/aes_gcm_parameters.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/aes_gcm.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::google::crypto::tink::AesGcmKeyFormat; +using ::google::crypto::tink::OutputPrefixType; + +using AesGcmProtoParametersParserImpl = + internal::ParametersParserImpl<internal::ProtoParametersSerialization, + AesGcmParameters>; +using AesGcmProtoParametersSerializerImpl = + internal::ParametersSerializerImpl<AesGcmParameters, + internal::ProtoParametersSerialization>; +using AesGcmProtoKeyParserImpl = + internal::KeyParserImpl<internal::ProtoKeySerialization, AesGcmKey>; +using AesGcmProtoKeySerializerImpl = + internal::KeySerializerImpl<AesGcmKey, internal::ProtoKeySerialization>; + +const absl::string_view kTypeUrl = + "type.googleapis.com/google.crypto.tink.AesGcmKey"; + +util::StatusOr<AesGcmParameters::Variant> ToVariant( + OutputPrefixType output_prefix_type) { + switch (output_prefix_type) { + case OutputPrefixType::LEGACY: + ABSL_FALLTHROUGH_INTENDED; // Parse LEGACY output prefix as CRUNCHY. + case OutputPrefixType::CRUNCHY: + return AesGcmParameters::Variant::kCrunchy; + case OutputPrefixType::RAW: + return AesGcmParameters::Variant::kNoPrefix; + case OutputPrefixType::TINK: + return AesGcmParameters::Variant::kTink; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine AesGcmParameters::Variant"); + } +} + +util::StatusOr<OutputPrefixType> ToOutputPrefixType( + AesGcmParameters::Variant variant) { + switch (variant) { + case AesGcmParameters::Variant::kCrunchy: + return OutputPrefixType::CRUNCHY; + case AesGcmParameters::Variant::kNoPrefix: + return OutputPrefixType::RAW; + case AesGcmParameters::Variant::kTink: + return OutputPrefixType::TINK; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine output prefix type"); + } +} + +// Legacy Tink AES-GCM key proto format assumes 12-byte random IVs and 16-byte +// tags. +util::Status ValidateParamsForProto(const AesGcmParameters& params) { + if (params.IvSizeInBytes() != 12) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Tink currently restricts AES-GCM IV size to 12 bytes."); + } + if (params.TagSizeInBytes() != 16) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Tink currently restricts AES-GCM tag size to 16 bytes."); + } + return util::OkStatus(); +} + +util::StatusOr<AesGcmParameters> ParseParameters( + const internal::ProtoParametersSerialization& serialization) { + if (serialization.GetKeyTemplate().type_url() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing AesGcmParameters."); + } + + AesGcmKeyFormat proto_key_format; + if (!proto_key_format.ParseFromString( + serialization.GetKeyTemplate().value())) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse AesGcmKeyFormat proto"); + } + if (proto_key_format.version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + util::StatusOr<AesGcmParameters::Variant> variant = + ToVariant(serialization.GetKeyTemplate().output_prefix_type()); + if (!variant.ok()) return variant.status(); + + // Legacy Tink AES-GCM key proto format assumes 12-byte random IVs and 16-byte + // tags. + return AesGcmParameters::Builder() + .SetVariant(*variant) + .SetKeySizeInBytes(proto_key_format.key_size()) + .SetIvSizeInBytes(12) + .SetTagSizeInBytes(16) + .Build(); +} + +util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters( + const AesGcmParameters& parameters) { + util::Status valid_params = ValidateParamsForProto(parameters); + if (!valid_params.ok()) return valid_params; + + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(parameters.GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + + AesGcmKeyFormat proto_key_format; + proto_key_format.set_key_size(parameters.KeySizeInBytes()); + + return internal::ProtoParametersSerialization::Create( + kTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString()); +} + +util::StatusOr<AesGcmKey> ParseKey( + const internal::ProtoKeySerialization& serialization, + absl::optional<SecretKeyAccessToken> token) { + if (serialization.TypeUrl() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing AesGcmKey."); + } + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + google::crypto::tink::AesGcmKey proto_key; + RestrictedData restricted_data = serialization.SerializedKeyProto(); + // OSS proto library complains if input is not converted to a string. + if (!proto_key.ParseFromString( + std::string(restricted_data.GetSecret(*token)))) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse AesGcmKey proto"); + } + if (proto_key.version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + util::StatusOr<AesGcmParameters::Variant> variant = + ToVariant(serialization.GetOutputPrefixType()); + if (!variant.ok()) return variant.status(); + + // Legacy AES-GCM key proto format assumes 12-byte random IVs and 16-byte + // tags. + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetVariant(*variant) + .SetKeySizeInBytes(proto_key.key_value().length()) + .SetIvSizeInBytes(12) + .SetTagSizeInBytes(16) + .Build(); + if (!parameters.ok()) return parameters.status(); + + return AesGcmKey::Create( + *parameters, RestrictedData(proto_key.key_value(), *token), + serialization.IdRequirement(), GetPartialKeyAccess()); +} + +util::StatusOr<internal::ProtoKeySerialization> SerializeKey( + const AesGcmKey& key, absl::optional<SecretKeyAccessToken> token) { + util::Status valid_params = ValidateParamsForProto(key.GetParameters()); + if (!valid_params.ok()) return valid_params; + + util::StatusOr<RestrictedData> restricted_input = + key.GetKeyBytes(GetPartialKeyAccess()); + if (!restricted_input.ok()) return restricted_input.status(); + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + + google::crypto::tink::AesGcmKey proto_key; + proto_key.set_version(0); + // OSS proto library complains if input is not converted to a string. + proto_key.set_key_value(std::string(restricted_input->GetSecret(*token))); + + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(key.GetParameters().GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + + RestrictedData restricted_output = + RestrictedData(proto_key.SerializeAsString(), *token); + return internal::ProtoKeySerialization::Create( + kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC, + *output_prefix_type, key.GetIdRequirement()); +} + +AesGcmProtoParametersParserImpl* AesGcmProtoParametersParser() { + static auto* parser = + new AesGcmProtoParametersParserImpl(kTypeUrl, ParseParameters); + return parser; +} + +AesGcmProtoParametersSerializerImpl* AesGcmProtoParametersSerializer() { + static auto* serializer = + new AesGcmProtoParametersSerializerImpl(kTypeUrl, SerializeParameters); + return serializer; +} + +AesGcmProtoKeyParserImpl* AesGcmProtoKeyParser() { + static auto* parser = new AesGcmProtoKeyParserImpl(kTypeUrl, ParseKey); + return parser; +} + +AesGcmProtoKeySerializerImpl* AesGcmProtoKeySerializer() { + static auto* serializer = new AesGcmProtoKeySerializerImpl(SerializeKey); + return serializer; +} + +} // namespace + +util::Status RegisterAesGcmProtoSerialization() { + util::Status status = + internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersParser(AesGcmProtoParametersParser()); + if (!status.ok()) return status; + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersSerializer(AesGcmProtoParametersSerializer()); + if (!status.ok()) return status; + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeyParser(AesGcmProtoKeyParser()); + if (!status.ok()) return status; + + return internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeySerializer(AesGcmProtoKeySerializer()); +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/aead/aes_gcm_proto_serialization.h b/cc/aead/aes_gcm_proto_serialization.h new file mode 100644 index 0000000..68a49f4 --- /dev/null +++ b/cc/aead/aes_gcm_proto_serialization.h
@@ -0,0 +1,31 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_AEAD_AES_GCM_PROTO_SERIALIZATION_H_ +#define TINK_AEAD_AES_GCM_PROTO_SERIALIZATION_H_ + +#include "tink/util/status.h" + +namespace crypto { +namespace tink { + +// Registers proto parsers and serializers for AES-GCM parameters and keys. +crypto::tink::util::Status RegisterAesGcmProtoSerialization(); + +} // namespace tink +} // namespace crypto + +#endif // TINK_AEAD_AES_GCM_PROTO_SERIALIZATION_H_
diff --git a/cc/aead/aes_gcm_proto_serialization_test.cc b/cc/aead/aes_gcm_proto_serialization_test.cc new file mode 100644 index 0000000..f19c820 --- /dev/null +++ b/cc/aead/aes_gcm_proto_serialization_test.cc
@@ -0,0 +1,515 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/aead/aes_gcm_proto_serialization.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/aead/aes_gcm_key.h" +#include "tink/aead/aes_gcm_parameters.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/subtle/random.h" +#include "tink/util/test_matchers.h" +#include "proto/aes_gcm.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::subtle::Random; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesGcmKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::NotNull; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + AesGcmParameters::Variant variant; + OutputPrefixType output_prefix_type; + int key_size; + int iv_size; + int tag_size; + absl::optional<int> id; + std::string output_prefix; +}; + +class AesGcmProtoSerializationTest : public TestWithParam<TestCase> { + protected: + void SetUp() override { + internal::MutableSerializationRegistry::GlobalInstance().Reset(); + } +}; + +INSTANTIATE_TEST_SUITE_P( + AesGcmProtoSerializationTestSuite, AesGcmProtoSerializationTest, + Values(TestCase{AesGcmParameters::Variant::kTink, OutputPrefixType::TINK, + /*key_size=*/16, /*iv_size=*/12, /*tag_size=*/16, + /*id=*/0x02030400, + /*output_prefix=*/std::string("\x01\x02\x03\x04\x00", 5)}, + TestCase{AesGcmParameters::Variant::kCrunchy, + OutputPrefixType::CRUNCHY, /*key_size=*/16, /*iv_size=*/12, + /*tag_size=*/16, /*id=*/0x01030005, + /*output_prefix=*/std::string("\x00\x01\x03\x00\x05", 5)}, + TestCase{AesGcmParameters::Variant::kNoPrefix, OutputPrefixType::RAW, + /*key_size=*/32, /*iv_size=*/12, /*tag_size=*/16, + /*id=*/absl::nullopt, /*output_prefix=*/""})); + +TEST_P(AesGcmProtoSerializationTest, ParseParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + AesGcmKeyFormat key_format_proto; + key_format_proto.set_version(0); + key_format_proto.set_key_size(test_case.key_size); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesGcmKey", + test_case.output_prefix_type, key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(params, IsOk()); + EXPECT_THAT((*params)->HasIdRequirement(), test_case.id.has_value()); + + const AesGcmParameters* gcm_params = + dynamic_cast<const AesGcmParameters*>(params->get()); + ASSERT_THAT(gcm_params, NotNull()); + EXPECT_THAT(gcm_params->GetVariant(), Eq(test_case.variant)); + EXPECT_THAT(gcm_params->KeySizeInBytes(), Eq(test_case.key_size)); + EXPECT_THAT(gcm_params->IvSizeInBytes(), Eq(test_case.iv_size)); + EXPECT_THAT(gcm_params->TagSizeInBytes(), Eq(test_case.tag_size)); +} + +TEST_F(AesGcmProtoSerializationTest, ParseParametersWithInvalidSerialization) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + AesGcmKeyFormat key_format_proto; + key_format_proto.set_version(0); + key_format_proto.set_key_size(16); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesGcmKey", + OutputPrefixType::RAW, "invalid_serialization"); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesGcmProtoSerializationTest, ParseParametersWithUnkownOutputPrefix) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + AesGcmKeyFormat key_format_proto; + key_format_proto.set_version(0); + key_format_proto.set_key_size(16); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesGcmKey", + OutputPrefixType::UNKNOWN_PREFIX, + key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesGcmProtoSerializationTest, ParseParametersWithInvalidVersion) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + AesGcmKeyFormat key_format_proto; + key_format_proto.set_version(1); + key_format_proto.set_key_size(16); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesGcmKey", + OutputPrefixType::RAW, + key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesGcmProtoSerializationTest, SerializeParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetVariant(test_case.variant) + .SetKeySizeInBytes(test_case.key_size) + .SetIvSizeInBytes(test_case.iv_size) + .SetTagSizeInBytes(test_case.tag_size) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>( + *parameters); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.AesGcmKey")); + + const internal::ProtoParametersSerialization* proto_serialization = + dynamic_cast<const internal::ProtoParametersSerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->GetKeyTemplate().type_url(), + Eq("type.googleapis.com/google.crypto.tink.AesGcmKey")); + EXPECT_THAT(proto_serialization->GetKeyTemplate().output_prefix_type(), + Eq(test_case.output_prefix_type)); + + AesGcmKeyFormat key_format; + ASSERT_THAT( + key_format.ParseFromString(proto_serialization->GetKeyTemplate().value()), + IsTrue()); + EXPECT_THAT(key_format.key_size(), Eq(test_case.key_size)); +} + +TEST_F(AesGcmProtoSerializationTest, SerializeParametersWithDisallowedIvSize) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(14) + .SetTagSizeInBytes(16) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>( + *parameters); + EXPECT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesGcmProtoSerializationTest, SerializeParametersWithDisallowedTagSize) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(12) + .SetTagSizeInBytes(14) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>( + *parameters); + EXPECT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesGcmProtoSerializationTest, ParseKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + google::crypto::tink::AesGcmKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesGcmKey", serialized_key, + KeyData::SYMMETRIC, test_case.output_prefix_type, test_case.id); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(test_case.id)); + EXPECT_THAT((*key)->GetParameters().HasIdRequirement(), + test_case.id.has_value()); + + util::StatusOr<AesGcmParameters> expected_parameters = + AesGcmParameters::Builder() + .SetVariant(test_case.variant) + .SetKeySizeInBytes(test_case.key_size) + .SetIvSizeInBytes(test_case.iv_size) + .SetTagSizeInBytes(test_case.tag_size) + .Build(); + ASSERT_THAT(expected_parameters, IsOk()); + + util::StatusOr<AesGcmKey> expected_key = AesGcmKey::Create( + *expected_parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(expected_key, IsOk()); + + EXPECT_THAT(**key, Eq(*expected_key)); +} + +TEST_F(AesGcmProtoSerializationTest, ParseLegacyKeyAsCrunchy) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(32); + google::crypto::tink::AesGcmKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesGcmKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::LEGACY, /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + + const AesGcmKey* aes_gcm_key = dynamic_cast<const AesGcmKey*>(key->get()); + ASSERT_THAT(aes_gcm_key, NotNull()); + EXPECT_THAT(aes_gcm_key->GetParameters().GetVariant(), + Eq(AesGcmParameters::Variant::kCrunchy)); +} + +TEST_F(AesGcmProtoSerializationTest, ParseKeyWithInvalidSerialization) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + RestrictedData serialized_key = + RestrictedData("invalid_serialization", InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesGcmKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesGcmProtoSerializationTest, ParseKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::AesGcmKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesGcmKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, /*token=*/absl::nullopt); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesGcmProtoSerializationTest, ParseKeyWithInvalidVersion) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::AesGcmKey key_proto; + key_proto.set_version(1); // Invalid version number. + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesGcmKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesGcmProtoSerializationTest, SerializeKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetVariant(test_case.variant) + .SetKeySizeInBytes(test_case.key_size) + .SetIvSizeInBytes(test_case.iv_size) + .SetTagSizeInBytes(test_case.tag_size) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + util::StatusOr<AesGcmKey> key = AesGcmKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.AesGcmKey")); + + const internal::ProtoKeySerialization* proto_serialization = + dynamic_cast<const internal::ProtoKeySerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->TypeUrl(), + Eq("type.googleapis.com/google.crypto.tink.AesGcmKey")); + EXPECT_THAT(proto_serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(proto_serialization->GetOutputPrefixType(), + Eq(test_case.output_prefix_type)); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(test_case.id)); + + google::crypto::tink::AesGcmKey proto_key; + // OSS proto library complains if input is not converted to a string. + ASSERT_THAT(proto_key.ParseFromString(std::string( + proto_serialization->SerializedKeyProto().GetSecret( + InsecureSecretKeyAccess::Get()))), + IsTrue()); + EXPECT_THAT(proto_key.key_value().size(), Eq(test_case.key_size)); +} + +TEST_F(AesGcmProtoSerializationTest, SerializeKeyWithDisallowedIvSize) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(14) + .SetTagSizeInBytes(16) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(32); + util::StatusOr<AesGcmKey> key = AesGcmKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesGcmProtoSerializationTest, SerializeKeyWithDisallowedTagSize) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .SetKeySizeInBytes(32) + .SetIvSizeInBytes(12) + .SetTagSizeInBytes(14) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(32); + util::StatusOr<AesGcmKey> key = AesGcmKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesGcmProtoSerializationTest, SerializeKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk()); + + util::StatusOr<AesGcmParameters> parameters = + AesGcmParameters::Builder() + .SetVariant(AesGcmParameters::Variant::kNoPrefix) + .SetKeySizeInBytes(16) + .SetIvSizeInBytes(12) + .SetTagSizeInBytes(16) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + util::StatusOr<AesGcmKey> key = AesGcmKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>(*key, absl::nullopt); + EXPECT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/aead/cord_aead.h b/cc/aead/cord_aead.h index 46a60ce..e57ba5f 100644 --- a/cc/aead/cord_aead.h +++ b/cc/aead/cord_aead.h
@@ -56,7 +56,7 @@ absl::Cord ciphertext, absl::Cord associated_data) const = 0; - virtual ~CordAead() {} + virtual ~CordAead() = default; }; } // namespace tink
diff --git a/cc/aead/cord_aead_wrapper.cc b/cc/aead/cord_aead_wrapper.cc index 0efb3a0..263b3fc 100644 --- a/cc/aead/cord_aead_wrapper.cc +++ b/cc/aead/cord_aead_wrapper.cc
@@ -56,7 +56,7 @@ crypto::tink::util::StatusOr<absl::Cord> Decrypt( absl::Cord ciphertext, absl::Cord associated_data) const override; - ~CordAeadSetWrapper() override {} + ~CordAeadSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<CordAead>> aead_set_;
diff --git a/cc/aead/failing_aead.cc b/cc/aead/failing_aead.cc index 81835c5..9f6c0f8 100644 --- a/cc/aead/failing_aead.cc +++ b/cc/aead/failing_aead.cc
@@ -15,8 +15,10 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/aead/failing_aead.h" +#include <memory> #include <string> #include <utility> + #include "absl/strings/string_view.h" namespace crypto {
diff --git a/cc/aead/failing_aead.h b/cc/aead/failing_aead.h index 70b80a9..88d8c81 100644 --- a/cc/aead/failing_aead.h +++ b/cc/aead/failing_aead.h
@@ -16,6 +16,7 @@ #ifndef TINK_AEAD_FAILING_AEAD_H_ #define TINK_AEAD_FAILING_AEAD_H_ +#include <memory> #include <string> #include "absl/strings/string_view.h"
diff --git a/cc/aead/internal/BUILD.bazel b/cc/aead/internal/BUILD.bazel index 0f7c72f..225bffc 100644 --- a/cc/aead/internal/BUILD.bazel +++ b/cc/aead/internal/BUILD.bazel
@@ -11,6 +11,7 @@ "//util:errors", "//util:statusor", "@boringssl//:crypto", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/status", ], ) @@ -61,13 +62,11 @@ "//internal:ssl_unique_ptr", "//subtle:random", "//subtle:subtle_util", - "//util:errors", "//util:secret_data", "//util:status", "//util:statusor", "@boringssl//:crypto", "@com_google_absl//absl/status", - "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:cord", ], ) @@ -154,7 +153,7 @@ name = "cord_aes_gcm_boringssl_test", size = "small", srcs = ["cord_aes_gcm_boringssl_test.cc"], - data = ["@wycheproof//testvectors:aes_gcm"], + data = ["//testvectors:aes_gcm"], deps = [ ":cord_aes_gcm_boringssl", "//subtle:aes_gcm_boringssl", @@ -199,7 +198,7 @@ cc_test( name = "zero_copy_aes_gcm_boringssl_test", srcs = ["zero_copy_aes_gcm_boringssl_test.cc"], - data = ["@wycheproof//testvectors:aes_gcm"], + data = ["//testvectors:aes_gcm"], deps = [ ":wycheproof_aead", ":zero_copy_aead", @@ -235,21 +234,20 @@ name = "ssl_aead_test", srcs = ["ssl_aead_test.cc"], data = [ - "@wycheproof//testvectors:aes_gcm", - "@wycheproof//testvectors:aes_gcm_siv", - "@wycheproof//testvectors:chacha20_poly1305", + "//testvectors:aes_gcm", + "//testvectors:aes_gcm_siv", + "//testvectors:chacha20_poly1305", ], deps = [ ":ssl_aead", ":wycheproof_aead", - "//config:tink_fips", + "//internal:fips_utils", "//internal:ssl_util", "//subtle:subtle_util", "//util:secret_data", "//util:statusor", "//util:test_matchers", "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", @@ -265,6 +263,7 @@ ":ssl_aead", "//config:tink_fips", "//internal:ssl_util", + "//internal:util", "//subtle:subtle_util", "//util:secret_data", "//util:statusor",
diff --git a/cc/aead/internal/BUILD.gn b/cc/aead/internal/BUILD.gn index ffabdb7..1202a08 100644 --- a/cc/aead/internal/BUILD.gn +++ b/cc/aead/internal/BUILD.gn
@@ -14,6 +14,7 @@ "aead_util.h", ] public_deps = [ + "//third_party/abseil-cpp/absl/container:flat_hash_set", "//third_party/abseil-cpp/absl/status:status", "//third_party/boringssl:crypto", "//third_party/tink/cc/util:errors", @@ -60,13 +61,11 @@ ":aead_util", "//third_party/abseil-cpp/absl/status:status", "//third_party/abseil-cpp/absl/strings:cord", - "//third_party/abseil-cpp/absl/strings:strings", "//third_party/boringssl:crypto", "//third_party/tink/cc/aead:cord_aead", "//third_party/tink/cc/internal:ssl_unique_ptr", "//third_party/tink/cc/subtle:random", "//third_party/tink/cc/subtle:subtle_util", - "//third_party/tink/cc/util:errors", "//third_party/tink/cc/util:secret_data", "//third_party/tink/cc/util:status", "//third_party/tink/cc/util:statusor",
diff --git a/cc/aead/internal/CMakeLists.txt b/cc/aead/internal/CMakeLists.txt index e7c1a07..217d2b4 100644 --- a/cc/aead/internal/CMakeLists.txt +++ b/cc/aead/internal/CMakeLists.txt
@@ -6,6 +6,7 @@ aead_util.cc aead_util.h DEPS + absl::flat_hash_set absl::status crypto tink::util::errors @@ -21,6 +22,7 @@ absl::strings tink::subtle::wycheproof_util tink::util::statusor + TESTONLY ) tink_cc_library( @@ -52,14 +54,12 @@ DEPS tink::aead::internal::aead_util absl::status - absl::strings absl::cord crypto tink::aead::cord_aead tink::internal::ssl_unique_ptr tink::subtle::random tink::subtle::subtle_util - tink::util::errors tink::util::secret_data tink::util::status tink::util::statusor @@ -74,6 +74,7 @@ gmock absl::strings tink::util::statusor + TESTONLY ) tink_cc_library( @@ -159,11 +160,10 @@ tink::aead::internal::wycheproof_aead gmock absl::flat_hash_set - absl::memory absl::status absl::strings absl::span - tink::config::tink_fips + tink::internal::fips_utils tink::internal::ssl_util tink::subtle::subtle_util tink::util::secret_data @@ -241,6 +241,7 @@ absl::span tink::config::tink_fips tink::internal::ssl_util + tink::internal::util tink::subtle::subtle_util tink::util::secret_data tink::util::statusor
diff --git a/cc/aead/internal/aead_from_zero_copy_test.cc b/cc/aead/internal/aead_from_zero_copy_test.cc index 2fe4486..9693661 100644 --- a/cc/aead/internal/aead_from_zero_copy_test.cc +++ b/cc/aead/internal/aead_from_zero_copy_test.cc
@@ -49,7 +49,7 @@ TEST(AeadFromZeroCopyTest, EncryptSucceeds) { std::unique_ptr<MockZeroCopyAead> mock_zero_copy_aead = - absl::WrapUnique(new MockZeroCopyAead()); + std::make_unique<MockZeroCopyAead>(); EXPECT_CALL(*mock_zero_copy_aead, MaxEncryptionSize(kPlaintext.size())) .WillOnce(Return(kCiphertext.size())); EXPECT_CALL(*mock_zero_copy_aead, Encrypt(kPlaintext, kAssociatedData, _)) @@ -66,7 +66,7 @@ TEST(AeadFromZeroCopyTest, EncryptFailsIfZeroCopyEncryptFails) { std::unique_ptr<MockZeroCopyAead> mock_zero_copy_aead = - absl::WrapUnique(new MockZeroCopyAead()); + std::make_unique<MockZeroCopyAead>(); EXPECT_CALL(*mock_zero_copy_aead, MaxEncryptionSize(kPlaintext.size())) .WillOnce(Return(kCiphertext.size())); EXPECT_CALL(*mock_zero_copy_aead, Encrypt(kPlaintext, kAssociatedData, _)) @@ -79,7 +79,7 @@ TEST(AeadFromZeroCopyTest, DecryptSucceeds) { std::unique_ptr<MockZeroCopyAead> mock_zero_copy_aead = - absl::WrapUnique(new MockZeroCopyAead()); + std::make_unique<MockZeroCopyAead>(); EXPECT_CALL(*mock_zero_copy_aead, MaxDecryptionSize(kCiphertext.size())) .WillOnce(Return(kPlaintext.size())); EXPECT_CALL(*mock_zero_copy_aead, Decrypt(kCiphertext, kAssociatedData, _)) @@ -96,7 +96,7 @@ TEST(AeadFromZeroCopyTest, EncryptFailsIfZeroCopyDecryptFails) { std::unique_ptr<MockZeroCopyAead> mock_zero_copy_aead = - absl::WrapUnique(new MockZeroCopyAead()); + std::make_unique<MockZeroCopyAead>(); EXPECT_CALL(*mock_zero_copy_aead, MaxDecryptionSize(kCiphertext.size())) .WillOnce(Return(kPlaintext.size())); EXPECT_CALL(*mock_zero_copy_aead, Decrypt(kCiphertext, kAssociatedData, _))
diff --git a/cc/aead/internal/aead_util.cc b/cc/aead/internal/aead_util.cc index bfe0719..22c0bce 100644 --- a/cc/aead/internal/aead_util.cc +++ b/cc/aead/internal/aead_util.cc
@@ -15,6 +15,8 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/aead/internal/aead_util.h" +#include <string> + #include "absl/status/status.h" #include "openssl/evp.h" #include "tink/util/errors.h" @@ -24,6 +26,18 @@ namespace tink { namespace internal { +bool IsSupportedKmsEnvelopeAeadDekKeyType(absl::string_view key_type) { + static const auto *kSupportedDekKeyTypes = + new absl::flat_hash_set<std::string>({ + "type.googleapis.com/google.crypto.tink.AesGcmKey", + "type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key", + "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey", + "type.googleapis.com/google.crypto.tink.AesEaxKey", + "type.googleapis.com/google.crypto.tink.AesGcmSivKey", + }); + return kSupportedDekKeyTypes->contains(key_type); +} + util::StatusOr<const EVP_CIPHER *> GetAesGcmCipherForKeySize( uint32_t key_size_in_bytes) { switch (key_size_in_bytes) {
diff --git a/cc/aead/internal/aead_util.h b/cc/aead/internal/aead_util.h index c8c84d2..094b22d 100644 --- a/cc/aead/internal/aead_util.h +++ b/cc/aead/internal/aead_util.h
@@ -16,6 +16,9 @@ #ifndef TINK_AEAD_INTERNAL_AEAD_UTIL_H_ #define TINK_AEAD_INTERNAL_AEAD_UTIL_H_ +#include <string> + +#include "absl/container/flat_hash_set.h" #include "openssl/evp.h" #include "tink/util/statusor.h" @@ -23,6 +26,8 @@ namespace tink { namespace internal { +bool IsSupportedKmsEnvelopeAeadDekKeyType(absl::string_view key_type); + // Returns a pointer to an AES-GCM EVP_CIPHER for the given key size. util::StatusOr<const EVP_CIPHER *> GetAesGcmCipherForKeySize( uint32_t key_size_in_bytes);
diff --git a/cc/aead/internal/aead_util_test.cc b/cc/aead/internal/aead_util_test.cc index ea3941a..83c717f 100644 --- a/cc/aead/internal/aead_util_test.cc +++ b/cc/aead/internal/aead_util_test.cc
@@ -27,6 +27,8 @@ using ::crypto::tink::test::IsOk; using ::crypto::tink::test::IsOkAndHolds; +using ::testing::IsFalse; +using ::testing::IsTrue; using ::testing::Not; TEST(AeadUtilTest, GetAesGcmCipherForKeySize) { @@ -42,6 +44,15 @@ } } +TEST(AeadUtilTest, SupportedKmsEnvelopeAeadDekKeyTypes) { + EXPECT_THAT(IsSupportedKmsEnvelopeAeadDekKeyType( + "type.googleapis.com/google.crypto.tink.AesGcmKey"), + IsTrue()); + EXPECT_THAT(IsSupportedKmsEnvelopeAeadDekKeyType( + "type.googleapis.com/google.crypto.tink.KmsEnvelopeAeadKey"), + IsFalse()); +} + #ifdef OPENSSL_IS_BORINGSSL TEST(AeadUtilTest, GetAesAeadForKeySize) {
diff --git a/cc/aead/internal/cord_aes_gcm_boringssl.cc b/cc/aead/internal/cord_aes_gcm_boringssl.cc index 321e9bf..4356bee 100644 --- a/cc/aead/internal/cord_aes_gcm_boringssl.cc +++ b/cc/aead/internal/cord_aes_gcm_boringssl.cc
@@ -17,22 +17,18 @@ #include "tink/aead/internal/cord_aes_gcm_boringssl.h" #include <cstdint> -#include <iterator> #include <memory> #include <string> #include <utility> -#include <vector> #include "absl/status/status.h" #include "absl/strings/cord.h" #include "openssl/evp.h" -#include "openssl/err.h" #include "tink/aead/cord_aead.h" #include "tink/aead/internal/aead_util.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/subtle/random.h" #include "tink/subtle/subtle_util.h" -#include "tink/util/errors.h" #include "tink/util/secret_data.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -47,8 +43,8 @@ // Set the IV `iv` for the given `context`. if `encryption` is true, set the // context for encryption, and for decryption otherwise. -util::Status SetIv(EVP_CIPHER_CTX* context, absl::string_view iv, - bool encryption) { +util::Status SetIvAndDirection(EVP_CIPHER_CTX* context, absl::string_view iv, + bool encryption) { const int encryption_flag = encryption ? 1 : 0; // Set the IV size. if (EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_GCM_SET_IVLEN, iv.size(), @@ -67,28 +63,100 @@ return util::OkStatus(); } +#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x30000000L +// Returns a new EVP_CIPHER_CTX for encryption (`encryption` == true) or +// decryption (`encryption` == false). It tries to skip part of the +// initialization copying `partial_context`. +util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> NewContextFromPartial( + EVP_CIPHER_CTX* partial_context, absl::string_view iv, bool encryption) { + internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new()); + if (context == nullptr) { + return util::Status(absl::StatusCode::kInternal, + "EVP_CIPHER_CTX_new failed"); + } + // Try making a copy of `partial_context` to skip some pre-computations. + // + // NOTE: With BoringSSL and OpenSSL 1.1.1 EVP_CIPHER_CTX_copy makes a copy + // of the `cipher_data` field of `context` as well, which contains the key + // material and IV (see [1] and [2]). + // + // [1]https://github.com/google/boringssl/blob/4c8bcf0da2951cacd8ed8eaa7fd2df4b22fca23b/crypto/fipsmodule/cipher/cipher.c#L116 + // [2]https://github.com/openssl/openssl/blob/830bf8e1e4749ad65c51b6a1d0d769ae689404ba/crypto/evp/evp_enc.c#L703 + if (EVP_CIPHER_CTX_copy(context.get(), partial_context) <= 0) { + return util::Status(absl::StatusCode::kInternal, + "EVP_CIPHER_CTX_copy failed"); + } + util::Status res = + SetIvAndDirection(context.get(), iv, /*encryption=*/encryption); + if (!res.ok()) { + return res; + } + return std::move(context); +} +#else +// Returns a new EVP_CIPHER_CTX for encryption (`encryption` == true) or +// decryption (`encryption` == false) with given `key` and `iv`. +// +// NOTE: Copying the context fails with OpenSSL 3.0, which doesn't provide a +// `dupctx` function for aead ciphers (see [1], [2]). +// +// [1]https://github.com/openssl/openssl/blob/eb52450f5151e8e78743ab05de21a344823316f5/crypto/evp/evp_enc.c#L1427 +// [2]https://github.com/openssl/openssl/blob/cac250755efd0c40cc6127a0e4baceb8d226c7e3/providers/implementations/include/prov/ciphercommon_aead.h#L30 +util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> NewContext( + const util::SecretData& key, absl::string_view iv, bool encryption) { + internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new()); + if (context == nullptr) { + return util::Status(absl::StatusCode::kInternal, + "EVP_CIPHER_CTX_new failed"); + } + util::StatusOr<const EVP_CIPHER*> cipher = + internal::GetAesGcmCipherForKeySize(key.size()); + if (!cipher.ok()) { + return cipher.status(); + } + if (EVP_CipherInit_ex(context.get(), *cipher, /*impl=*/nullptr, + reinterpret_cast<const uint8_t*>(key.data()), + /*iv=*/nullptr, /*enc=*/1) <= 0) { + return util::Status(absl::StatusCode::kInternal, + "Context initialization failed"); + } + util::Status res = + SetIvAndDirection(context.get(), iv, /*encryption=*/encryption); + if (!res.ok()) { + return res; + } + return std::move(context); +} +#endif + } // namespace -util::StatusOr<std::unique_ptr<CordAead> > CordAesGcmBoringSsl::New( - util::SecretData key_value) { +util::StatusOr<std::unique_ptr<CordAead>> CordAesGcmBoringSsl::New( + const util::SecretData& key_value) { util::StatusOr<const EVP_CIPHER*> cipher = internal::GetAesGcmCipherForKeySize(key_value.size()); if (!cipher.ok()) { return cipher.status(); } - internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new()); - // Initialize the cipher now to have some precomputations on the key. The - // direction (enc/dec) is not important since it will be overwritten later. - if (EVP_CipherInit_ex(context.get(), *cipher, /*engine=*/nullptr, + internal::SslUniquePtr<EVP_CIPHER_CTX> partial_context(EVP_CIPHER_CTX_new()); + // Initialize a partial context for the cipher to allow OpenSSL/BoringSSL + // making some precomputations on the key. Encrypt and Decrypt will try making + // a copy of this context to avoid doing the same initializations again and to + // guarantee thread safety. + // + // NOTE: It doesn't matter at this point if we set the direction to encryption + // or decryption, it will be overwritten later any time we call + // EVP_CipherInit_ex. + if (EVP_CipherInit_ex(partial_context.get(), *cipher, /*engine=*/nullptr, reinterpret_cast<const uint8_t*>(&key_value[0]), /*iv=*/nullptr, /*enc=*/1) <= 0) { return util::Status(absl::StatusCode::kInternal, "Context initialization failed"); } - std::unique_ptr<CordAead> aead = - absl::WrapUnique(new CordAesGcmBoringSsl(std::move(context))); + std::unique_ptr<CordAead> aead = absl::WrapUnique( + new CordAesGcmBoringSsl(std::move(partial_context), key_value)); return std::move(aead); } @@ -96,18 +164,21 @@ absl::Cord plaintext, absl::Cord associated_data) const { std::string iv = subtle::Random::GetRandomBytes(kIvSizeInBytes); - internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new()); - EVP_CIPHER_CTX_copy(context.get(), context_.get()); - - util::Status res = SetIv(context.get(), iv, /*encryption=*/true); - if (!res.ok()) { - return res; +#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x30000000L + util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context = + NewContextFromPartial(partial_context_.get(), iv, /*encryption=*/true); +#else + util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context = + NewContext(key_, iv, /*encryption=*/true); +#endif + if (!context.ok()) { + return context.status(); } int len = 0; // Process AAD. for (auto ad_chunk : associated_data.Chunks()) { - if (!EVP_EncryptUpdate(context.get(), /*out=*/nullptr, &len, + if (!EVP_EncryptUpdate(context->get(), /*out=*/nullptr, &len, reinterpret_cast<const uint8_t*>(ad_chunk.data()), ad_chunk.size())) { return util::Status(absl::StatusCode::kInternal, "Encryption failed"); @@ -124,7 +195,7 @@ for (auto plaintext_chunk : plaintext.Chunks()) { if (!EVP_EncryptUpdate( - context.get(), + context->get(), reinterpret_cast<uint8_t*>(&(buffer[ciphertext_buffer_offset])), &len, reinterpret_cast<const uint8_t*>(plaintext_chunk.data()), plaintext_chunk.size())) { @@ -132,13 +203,14 @@ } ciphertext_buffer_offset += plaintext_chunk.size(); } - if (!EVP_EncryptFinal_ex(context.get(), nullptr, &len)) { + if (!EVP_EncryptFinal_ex(context->get(), nullptr, &len)) { return util::Status(absl::StatusCode::kInternal, "Encryption failed"); } std::string tag; subtle::ResizeStringUninitialized(&tag, kTagSizeInBytes); - if (!EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_GCM_GET_TAG, kTagSizeInBytes, + if (!EVP_CIPHER_CTX_ctrl(context->get(), EVP_CTRL_GCM_GET_TAG, + kTagSizeInBytes, reinterpret_cast<uint8_t*>(&tag[0]))) { return util::Status(absl::StatusCode::kInternal, "Encryption failed"); } @@ -157,23 +229,26 @@ return util::Status(absl::StatusCode::kInternal, "Ciphertext too short"); } - // First bytes contain IV. + // First bytes contain the IV. std::string iv = std::string(ciphertext.Subcord(0, kIvSizeInBytes)); absl::Cord raw_ciphertext = ciphertext.Subcord( kIvSizeInBytes, ciphertext.size() - kIvSizeInBytes - kTagSizeInBytes); - internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new()); - EVP_CIPHER_CTX_copy(context.get(), context_.get()); - - util::Status res = SetIv(context.get(), iv, /*encryption=*/false); - if (!res.ok()) { - return res; +#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x30000000L + util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context = + NewContextFromPartial(partial_context_.get(), iv, /*encryption=*/false); +#else + util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context = + NewContext(key_, iv, /*encryption=*/false); +#endif + if (!context.ok()) { + return context.status(); } int len = 0; // Process associated data. for (auto ad_chunk : associated_data.Chunks()) { - if (!EVP_DecryptUpdate(context.get(), nullptr, &len, + if (!EVP_DecryptUpdate(context->get(), /*out=*/nullptr, &len, reinterpret_cast<const uint8_t*>(ad_chunk.data()), ad_chunk.size())) { return util::Status(absl::StatusCode::kInternal, "Decryption failed"); @@ -192,7 +267,7 @@ }); for (auto ct_chunk : raw_ciphertext.Chunks()) { - if (!EVP_DecryptUpdate(context.get(), + if (!EVP_DecryptUpdate(context->get(), reinterpret_cast<uint8_t*>( &plaintext_buffer[plaintext_buffer_offset]), &len, @@ -207,13 +282,13 @@ std::string tag = std::string( ciphertext.Subcord(ciphertext.size() - kTagSizeInBytes, kTagSizeInBytes)); - if (!EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_GCM_SET_TAG, kTagSizeInBytes, - &tag[0])) { + if (!EVP_CIPHER_CTX_ctrl(context->get(), EVP_CTRL_GCM_SET_TAG, + kTagSizeInBytes, &tag[0])) { return util::Status(absl::StatusCode::kInternal, "Could not set authentication tag"); } // Verify authentication tag. - if (!EVP_DecryptFinal_ex(context.get(), nullptr, &len)) { + if (!EVP_DecryptFinal_ex(context->get(), nullptr, &len)) { return util::Status(absl::StatusCode::kInternal, "Authentication failed"); } return result;
diff --git a/cc/aead/internal/cord_aes_gcm_boringssl.h b/cc/aead/internal/cord_aes_gcm_boringssl.h index 9dcd288..273b523 100644 --- a/cc/aead/internal/cord_aes_gcm_boringssl.h +++ b/cc/aead/internal/cord_aes_gcm_boringssl.h
@@ -20,12 +20,10 @@ #include <memory> #include <utility> -#include "absl/strings/string_view.h" #include "openssl/evp.h" #include "tink/aead/cord_aead.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/util/secret_data.h" -#include "tink/util/status.h" #include "tink/util/statusor.h" namespace crypto { @@ -35,7 +33,7 @@ class CordAesGcmBoringSsl : public CordAead { public: static crypto::tink::util::StatusOr<std::unique_ptr<CordAead>> New( - util::SecretData key_value); + const util::SecretData& key_value); crypto::tink::util::StatusOr<absl::Cord> Encrypt( absl::Cord plaintext, absl::Cord associated_data) const override; @@ -44,10 +42,15 @@ absl::Cord ciphertext, absl::Cord associated_data) const override; private: - explicit CordAesGcmBoringSsl(internal::SslUniquePtr<EVP_CIPHER_CTX> context) - : context_(std::move(context)) {} + explicit CordAesGcmBoringSsl( + internal::SslUniquePtr<EVP_CIPHER_CTX> partial_context, + const util::SecretData& key) + : partial_context_(std::move(partial_context)), key_(key) {} - internal::SslUniquePtr<EVP_CIPHER_CTX> context_; + // Partially-initialized EVP_CIPHER_CTX context that is copied for every + // Encrypt/Decrypt operation. + internal::SslUniquePtr<EVP_CIPHER_CTX> partial_context_; + util::SecretData key_; }; } // namespace internal
diff --git a/cc/aead/internal/ssl_aead.cc b/cc/aead/internal/ssl_aead.cc index 4b62a8a..c8b2629 100644 --- a/cc/aead/internal/ssl_aead.cc +++ b/cc/aead/internal/ssl_aead.cc
@@ -44,36 +44,10 @@ ABSL_CONST_INIT const int kXchacha20Poly1305TagSizeInBytes = 16; ABSL_CONST_INIT const int kAesGcmTagSizeInBytes = 16; +ABSL_CONST_INIT const int kAesGcmSivTagSizeInBytes = 16; namespace { -// Sets `iv` to the given `context`, as well as the "direction" -// (encrypt/decrypt) based on `encryption`. -util::Status SetIvAndDirection(EVP_CIPHER_CTX *context, absl::string_view iv, - bool encryption) { - if (context == nullptr) { - return util::Status(absl::StatusCode::kInternal, - "Context must not be null"); - } - const int encryption_flag = encryption ? 1 : 0; - // Set the size for IV first, then set the IV bytes. - if (EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_AEAD_SET_IVLEN, iv.size(), - /*ptr=*/nullptr) <= 0) { - return util::Status(absl::StatusCode::kInternal, "Setting IV size failed"); - } - if (EVP_CipherInit_ex(context, /*cipher=*/nullptr, /*impl=*/nullptr, - /*key=*/nullptr, - reinterpret_cast<const uint8_t *>(iv.data()), - /*enc=*/encryption_flag) <= 0) { - return util::Status( - absl::StatusCode::kInternal, - absl::StrCat("Failed to initialize the context with IV of size ", - iv.size(), " and for ", - encryption ? "encryption" : "decryption")); - } - return util::OkStatus(); -} - // Encrypts/Decrypts `data` and writes the result into `out`. The direction // (encrypt/decrypt) is given by `context`. `out` is assumed to be large enough // to hold the encrypted/decrypted content. @@ -108,9 +82,9 @@ class OpenSslOneShotAeadImpl : public SslOneShotAead { public: - OpenSslOneShotAeadImpl(internal::SslUniquePtr<EVP_CIPHER_CTX> context, - size_t tag_size) - : context_(std::move(context)), tag_size_(tag_size) {} + explicit OpenSslOneShotAeadImpl(const util::SecretData &key, + const EVP_CIPHER *cipher, size_t tag_size) + : key_(key), cipher_(cipher), tag_size_(tag_size) {} util::StatusOr<int64_t> Encrypt(absl::string_view plaintext, absl::string_view associated_data, @@ -140,25 +114,15 @@ associated_data.size())); } - // For thread safety we copy the context and only then set the IV. This - // allows to allocate an AesGcmBoringSsl cipher, and initialize the context - // to force precomputation on the key, and only then set a different IV - // for each call to `Encrypt`. - internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new()); - // This makes a copy of the `cipher_data` field of the context too, which - // contains the key material and IV (see - // https://github.com/google/boringssl/blob/master/crypto/fipsmodule/cipher/cipher.c#L116). - EVP_CIPHER_CTX_copy(context.get(), context_.get()); - - util::Status res = - SetIvAndDirection(context.get(), iv, /*encryption=*/true); - if (!res.ok()) { - return res; + util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context = + GetContext(iv, /*encryption=*/true); + if (!context.ok()) { + return context.status(); } // Set the associated data. int len = 0; - if (EVP_EncryptUpdate(context.get(), /*out=*/nullptr, &len, + if (EVP_EncryptUpdate(context->get(), /*out=*/nullptr, &len, reinterpret_cast<const uint8_t *>(ad.data()), ad.size()) <= 0) { return util::Status(absl::StatusCode::kInternal, @@ -166,18 +130,18 @@ } util::StatusOr<int64_t> raw_ciphertext_bytes = - UpdateCipher(context.get(), plaintext_data, out); + UpdateCipher(context->get(), plaintext_data, out); if (!raw_ciphertext_bytes.ok()) { return raw_ciphertext_bytes.status(); } - if (EVP_EncryptFinal_ex(context.get(), /*out=*/nullptr, &len) <= 0) { + if (EVP_EncryptFinal_ex(context->get(), /*out=*/nullptr, &len) <= 0) { return util::Status(absl::StatusCode::kInternal, "Finalization failed"); } // Write the tag after the ciphertext. absl::Span<char> tag = out.subspan(*raw_ciphertext_bytes, tag_size_); - if (EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_AEAD_GET_TAG, tag_size_, + if (EVP_CIPHER_CTX_ctrl(context->get(), EVP_CTRL_AEAD_GET_TAG, tag_size_, reinterpret_cast<uint8_t *>(tag.data())) <= 0) { return util::Status(absl::StatusCode::kInternal, "Failed to get the tag"); } @@ -218,18 +182,15 @@ associated_data.size())); } - internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new()); - EVP_CIPHER_CTX_copy(context.get(), context_.get()); - - util::Status res = - SetIvAndDirection(context.get(), iv, /*encryption=*/false); - if (!res.ok()) { - return res; + util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context = + GetContext(iv, /*encryption=*/false); + if (!context.ok()) { + return context.status(); } int len = 0; // Add the associated data. - if (EVP_DecryptUpdate(context.get(), /*out=*/nullptr, &len, + if (EVP_DecryptUpdate(context->get(), /*out=*/nullptr, &len, reinterpret_cast<const uint8_t *>(ad.data()), ad.size()) <= 0) { return util::Status(absl::StatusCode::kInternal, @@ -246,7 +207,7 @@ auto tag = std::string(ciphertext.substr(raw_ciphertext_size, tag_size_)); // Set the tag. - if (EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_AEAD_SET_TAG, tag_size_, + if (EVP_CIPHER_CTX_ctrl(context->get(), EVP_CTRL_AEAD_SET_TAG, tag_size_, reinterpret_cast<uint8_t *>(&tag[0])) <= 0) { return util::Status(absl::StatusCode::kInternal, "Could not set authentication tag"); @@ -267,12 +228,12 @@ absl::MakeCleanup([out] { OPENSSL_cleanse(out.data(), out.size()); }); util::StatusOr<int64_t> written_bytes = - UpdateCipher(context.get(), raw_ciphertext, out_buffer); + UpdateCipher(context->get(), raw_ciphertext, out_buffer); if (!written_bytes.ok()) { return written_bytes.status(); } - if (!EVP_DecryptFinal_ex(context.get(), /*out=*/nullptr, &len)) { + if (!EVP_DecryptFinal_ex(context->get(), /*out=*/nullptr, &len)) { return util::Status(absl::StatusCode::kInternal, "Authentication failed"); } @@ -281,20 +242,70 @@ return *written_bytes; } + int64_t CiphertextSize(int64_t plaintext_length) const override { + return plaintext_length + tag_size_; + } + + int64_t PlaintextSize(int64_t ciphertext_length) const override { + if (ciphertext_length < tag_size_) { + return 0; + } + return ciphertext_length - tag_size_; + } + private: - const internal::SslUniquePtr<EVP_CIPHER_CTX> context_; + // Returns a new EVP_CIPHER_CTX for encryption (`ecryption` == true) or + // decryption (`encryption` == false). + util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> GetContext( + absl::string_view iv, bool encryption) const { + internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new()); + if (context == nullptr) { + return util::Status(absl::StatusCode::kInternal, + "EVP_CIPHER_CTX_new failed"); + } + const int encryption_flag = encryption ? 1 : 0; + if (EVP_CipherInit_ex(context.get(), cipher_, /*impl=*/nullptr, + /*key=*/nullptr, /*iv=*/nullptr, + encryption_flag) <= 0) { + return util::Status( + absl::StatusCode::kInternal, + absl::StrCat("Failed initializializing context for ", + encryption ? "encryption" : "decryption")); + } + // Set the size for IV first, then set the IV bytes. + if (EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_AEAD_SET_IVLEN, iv.size(), + /*ptr=*/nullptr) <= 0) { + return util::Status( + absl::StatusCode::kInternal, + absl::StrCat("Failed stting size of the IV to ", iv.size())); + } + if (EVP_CipherInit_ex(context.get(), /*cipher=*/nullptr, /*impl=*/nullptr, + reinterpret_cast<const uint8_t *>(key_.data()), + reinterpret_cast<const uint8_t *>(iv.data()), + encryption_flag) <= 0) { + return util::Status( + absl::StatusCode::kInternal, + absl::StrCat("Failed to set key of size ", key_.size(), + "and IV of size ", iv.size())); + } + + return std::move(context); + } + + const util::SecretData key_; + const EVP_CIPHER *cipher_; const size_t tag_size_; }; #ifdef OPENSSL_IS_BORINGSSL -// Implementation of the one-shot AEAD cypter. This is purposely internal to an -// anonymous namespace to disallow direct use of this class other than through -// the Create* functions below. +// Implementation of the one-shot AEAD cypter. This is purposely internal to +// an anonymous namespace to disallow direct use of this class other than +// through the Create* functions below. class BoringSslOneShotAeadImpl : public SslOneShotAead { public: - BoringSslOneShotAeadImpl(internal::SslUniquePtr<EVP_AEAD_CTX> context, - size_t tag_size) + explicit BoringSslOneShotAeadImpl( + internal::SslUniquePtr<EVP_AEAD_CTX> context, size_t tag_size) : context_(std::move(context)), tag_size_(tag_size) {} util::StatusOr<int64_t> Encrypt(absl::string_view plaintext, @@ -388,70 +399,24 @@ return out_len; } + int64_t CiphertextSize(int64_t plaintext_length) const override { + return plaintext_length + tag_size_; + } + + int64_t PlaintextSize(int64_t ciphertext_length) const override { + if (ciphertext_length < tag_size_) { + return 0; + } + return ciphertext_length - tag_size_; + } + + private: const internal::SslUniquePtr<EVP_AEAD_CTX> context_; const size_t tag_size_; }; #endif -#ifdef OPENSSL_IS_BORINGSSL -// Always use the BoringSSL APIs when available. -using SslOneShotAeadImpl = BoringSslOneShotAeadImpl; -#else -using SslOneShotAeadImpl = OpenSslOneShotAeadImpl; -#endif - -// One shot implementing AES-GCM. -class SslAesGcmOneShotAead : public SslOneShotAeadImpl { - public: -#ifdef OPENSSL_IS_BORINGSSL - explicit SslAesGcmOneShotAead(internal::SslUniquePtr<EVP_AEAD_CTX> context) - : BoringSslOneShotAeadImpl(std::move(context), kAesGcmTagSizeInBytes) {} -#else - explicit SslAesGcmOneShotAead(internal::SslUniquePtr<EVP_CIPHER_CTX> context) - : SslOneShotAeadImpl(std::move(context), kAesGcmTagSizeInBytes) {} -#endif - - int64_t CiphertextSize(int64_t plaintext_length) const override { - return plaintext_length + kAesGcmTagSizeInBytes; - } - - int64_t PlaintextSize(int64_t ciphertext_length) const override { - if (ciphertext_length < kAesGcmTagSizeInBytes) { - return 0; - } - return ciphertext_length - kAesGcmTagSizeInBytes; - } -}; - -// One shot implementing AES-GCM-SIV. -using SslAesGcmSivOneShotAead = SslAesGcmOneShotAead; - -// One shot implementing XCHACHA-POLY-1305. -class SslXchacha20Poly1305OneShotAead : public SslOneShotAeadImpl { - public: -#ifdef OPENSSL_IS_BORINGSSL - explicit SslXchacha20Poly1305OneShotAead( - internal::SslUniquePtr<EVP_AEAD_CTX> context) - : BoringSslOneShotAeadImpl(std::move(context), kAesGcmTagSizeInBytes) {} -#else - explicit SslXchacha20Poly1305OneShotAead( - internal::SslUniquePtr<EVP_CIPHER_CTX> context) - : SslOneShotAeadImpl(std::move(context), kAesGcmTagSizeInBytes) {} -#endif - - int64_t CiphertextSize(int64_t plaintext_length) const override { - return plaintext_length + kXchacha20Poly1305TagSizeInBytes; - } - - int64_t PlaintextSize(int64_t ciphertext_length) const override { - if (ciphertext_length < kXchacha20Poly1305TagSizeInBytes) { - return 0; - } - return ciphertext_length - kXchacha20Poly1305TagSizeInBytes; - } -}; - } // namespace util::StatusOr<std::unique_ptr<SslOneShotAead>> CreateAesGcmOneShotCrypter( @@ -470,6 +435,8 @@ absl::StatusCode::kInternal, absl::StrCat("EVP_AEAD_CTX_new failed: ", internal::GetSslErrors())); } + return {absl::make_unique<BoringSslOneShotAeadImpl>(std::move(context), + kAesGcmTagSizeInBytes)}; #else util::StatusOr<const EVP_CIPHER *> aead_cipher = GetAesGcmCipherForKeySize(key.size()); @@ -477,25 +444,9 @@ return aead_cipher.status(); } - internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new()); - - if (context == nullptr) { - return util::Status(absl::StatusCode::kInternal, - "EVP_CIPHER_CTX_new failed"); - } - - // Initialize the context for the cipher having OpenSSL to make some - // precomputations on the key. It doesn't matter at this point if we set - // encryption or decryption, it will be overwritten later on anyways any - // time we call EVP_CipherInit_ex. - if (EVP_CipherInit_ex(context.get(), *aead_cipher, /*impl=*/nullptr, - reinterpret_cast<const uint8_t *>(&key[0]), - /*iv=*/nullptr, /*enc=*/1) <= 0) { - return util::Status(absl::StatusCode::kInternal, - "Context initialization failed"); - } + return absl::make_unique<OpenSslOneShotAeadImpl>(key, *aead_cipher, + kAesGcmTagSizeInBytes); #endif - return {absl::make_unique<SslAesGcmOneShotAead>(std::move(context))}; } util::StatusOr<std::unique_ptr<SslOneShotAead>> CreateAesGcmSivOneShotCrypter( @@ -513,7 +464,8 @@ absl::StrCat("EVP_AEAD_CTX_new initialization Failed: ", internal::GetSslErrors())); } - return {absl::make_unique<SslAesGcmSivOneShotAead>(std::move(context))}; + return {absl::make_unique<BoringSslOneShotAeadImpl>( + std::move(context), kAesGcmSivTagSizeInBytes)}; #else return util::Status(absl::StatusCode::kUnimplemented, "AES-GCM-SIV is unimplemented for OpenSSL"); @@ -538,8 +490,8 @@ absl::StrCat("EVP_AEAD_CTX_new initialization Failed: ", internal::GetSslErrors())); } - return { - absl::make_unique<SslXchacha20Poly1305OneShotAead>(std::move(context))}; + return {absl::make_unique<BoringSslOneShotAeadImpl>( + std::move(context), kXchacha20Poly1305TagSizeInBytes)}; #else return util::Status(absl::StatusCode::kUnimplemented, "Xchacha20-Poly1305 is unimplemented for OpenSSL");
diff --git a/cc/aead/internal/ssl_aead.h b/cc/aead/internal/ssl_aead.h index 6ff877a..3fdc158 100644 --- a/cc/aead/internal/ssl_aead.h +++ b/cc/aead/internal/ssl_aead.h
@@ -28,9 +28,10 @@ namespace tink { namespace internal { +// Tag sizes. ABSL_CONST_INIT extern const int kXchacha20Poly1305TagSizeInBytes; -// Tag size for both AES-GCM and AES-GCM-SIV. ABSL_CONST_INIT extern const int kAesGcmTagSizeInBytes; +ABSL_CONST_INIT extern const int kAesGcmSivTagSizeInBytes; // Interface for one-shot AEAD crypters. class SslOneShotAead {
diff --git a/cc/aead/internal/ssl_aead_large_inputs_test.cc b/cc/aead/internal/ssl_aead_large_inputs_test.cc index 1ca2400..9427b78 100644 --- a/cc/aead/internal/ssl_aead_large_inputs_test.cc +++ b/cc/aead/internal/ssl_aead_large_inputs_test.cc
@@ -33,6 +33,7 @@ #include "tink/aead/internal/ssl_aead.h" #include "tink/config/tink_fips.h" #include "tink/internal/ssl_util.h" +#include "tink/internal/util.h" #include "tink/subtle/subtle_util.h" #include "tink/util/secret_data.h" #include "tink/util/statusor.h" @@ -87,6 +88,9 @@ // Encrypt/decrypt with an input larger than a MAX int. TEST_P(SslOneShotAeadLargeInputsTest, EncryptDecryptLargeInput) { + if (IsWindows()) { + GTEST_SKIP() << "Skipping on Windows: this currently times out"; + } const int64_t buff_size = static_cast<int64_t>(std::numeric_limits<int>::max()) + 1024; std::string large_input(buff_size, '0');
diff --git a/cc/aead/internal/ssl_aead_test.cc b/cc/aead/internal/ssl_aead_test.cc index 375e9cd..23fd35b 100644 --- a/cc/aead/internal/ssl_aead_test.cc +++ b/cc/aead/internal/ssl_aead_test.cc
@@ -34,7 +34,7 @@ #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "tink/aead/internal/wycheproof_aead.h" -#include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/internal/ssl_util.h" #include "tink/subtle/subtle_util.h" #include "tink/util/secret_data.h" @@ -524,7 +524,7 @@ } TEST(SslOneShotAeadTest, AesGcmTestFipsOnly) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (IsFipsModeEnabled() && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is " "unavailable."; } @@ -539,7 +539,7 @@ } TEST(SslOneShotAeadTest, AesGcmTestTestFipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!IsFipsModeEnabled() || IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; }
diff --git a/cc/aead/internal/zero_copy_aead_wrapper.cc b/cc/aead/internal/zero_copy_aead_wrapper.cc index 3ed95cc..b1e8114 100644 --- a/cc/aead/internal/zero_copy_aead_wrapper.cc +++ b/cc/aead/internal/zero_copy_aead_wrapper.cc
@@ -60,7 +60,7 @@ absl::string_view ciphertext, absl::string_view associated_data) const override; - ~ZeroCopyAeadSetWrapper() override {} + ~ZeroCopyAeadSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<ZeroCopyAead>> aead_set_;
diff --git a/cc/aead/internal/zero_copy_aead_wrapper.h b/cc/aead/internal/zero_copy_aead_wrapper.h index 1964a6d..beca0aa 100644 --- a/cc/aead/internal/zero_copy_aead_wrapper.h +++ b/cc/aead/internal/zero_copy_aead_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_AEAD_INTERNAL_ZERO_COPY_AEAD_WRAPPER_H_ #define TINK_AEAD_INTERNAL_ZERO_COPY_AEAD_WRAPPER_H_ +#include <memory> + #include "tink/aead.h" #include "tink/aead/internal/zero_copy_aead.h" #include "tink/primitive_set.h" @@ -46,4 +48,4 @@ } // namespace tink } // namespace crypto -#endif // TINK_AEAD_AEAD_WRAPPER_H_ +#endif // TINK_AEAD_INTERNAL_ZERO_COPY_AEAD_WRAPPER_H_
diff --git a/cc/aead/internal/zero_copy_aead_wrapper_test.cc b/cc/aead/internal/zero_copy_aead_wrapper_test.cc index 489b7de..b4e2218 100644 --- a/cc/aead/internal/zero_copy_aead_wrapper_test.cc +++ b/cc/aead/internal/zero_copy_aead_wrapper_test.cc
@@ -16,6 +16,7 @@ #include "tink/aead/internal/zero_copy_aead_wrapper.h" +#include <cstring> #include <memory> #include <string> #include <utility>
diff --git a/cc/aead/kms_envelope_aead.cc b/cc/aead/kms_envelope_aead.cc index 887c253..8f36b0d 100644 --- a/cc/aead/kms_envelope_aead.cc +++ b/cc/aead/kms_envelope_aead.cc
@@ -27,6 +27,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "tink/aead.h" +#include "tink/aead/internal/aead_util.h" #include "tink/registry.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -60,6 +61,11 @@ util::StatusOr<std::unique_ptr<Aead>> KmsEnvelopeAead::New( const google::crypto::tink::KeyTemplate& dek_template, std::unique_ptr<Aead> remote_aead) { + if (!internal::IsSupportedKmsEnvelopeAeadDekKeyType( + dek_template.type_url())) { + return util::Status(absl::StatusCode::kInvalidArgument, + "unsupported key type"); + } if (remote_aead == nullptr) { return util::Status(absl::StatusCode::kInvalidArgument, "remote_aead must be non-null");
diff --git a/cc/aead/kms_envelope_aead.h b/cc/aead/kms_envelope_aead.h index 1fd9d89..acfe066 100644 --- a/cc/aead/kms_envelope_aead.h +++ b/cc/aead/kms_envelope_aead.h
@@ -58,7 +58,7 @@ absl::string_view ciphertext, absl::string_view associated_data) const override; - ~KmsEnvelopeAead() override {} + ~KmsEnvelopeAead() override = default; private: KmsEnvelopeAead(const google::crypto::tink::KeyTemplate& dek_template,
diff --git a/cc/aead/kms_envelope_aead_key_manager.h b/cc/aead/kms_envelope_aead_key_manager.h index 04b9e31..69c99d1 100644 --- a/cc/aead/kms_envelope_aead_key_manager.h +++ b/cc/aead/kms_envelope_aead_key_manager.h
@@ -25,6 +25,7 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "tink/aead.h" +#include "tink/aead/internal/aead_util.h" #include "tink/core/key_type_manager.h" #include "tink/core/template_util.h" #include "tink/internal/fips_utils.h" @@ -75,6 +76,11 @@ return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, "Missing kek_uri."); } + if (!internal::IsSupportedKmsEnvelopeAeadDekKeyType( + format.dek_template().type_url())) { + return util::Status(absl::StatusCode::kInvalidArgument, + "unsupported dek key type"); + } return util::OkStatus(); }
diff --git a/cc/aead/kms_envelope_aead_key_manager_test.cc b/cc/aead/kms_envelope_aead_key_manager_test.cc index 7b32aa1..4cee9c1 100644 --- a/cc/aead/kms_envelope_aead_key_manager_test.cc +++ b/cc/aead/kms_envelope_aead_key_manager_test.cc
@@ -26,13 +26,16 @@ #include "absl/memory/memory.h" #include "absl/status/status.h" #include "tink/aead.h" +#include "tink/aead/aead_config.h" #include "tink/aead/aead_key_templates.h" #include "tink/aead/aes_eax_key_manager.h" #include "tink/aead/kms_envelope_aead.h" #include "tink/kms_client.h" #include "tink/kms_clients.h" +#include "tink/mac/mac_key_templates.h" #include "tink/registry.h" #include "tink/subtle/aead_test_util.h" +#include "tink/util/fake_kms_client.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" @@ -46,8 +49,9 @@ using ::crypto::tink::test::DummyAead; using ::crypto::tink::test::DummyKmsClient; using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; using ::crypto::tink::test::StatusIs; -using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::KeyTemplate; using ::google::crypto::tink::KmsEnvelopeAeadKey; using ::google::crypto::tink::KmsEnvelopeAeadKeyFormat; using ::testing::Eq; @@ -118,7 +122,15 @@ TEST(KmsEnvelopeAeadKeyManagerTest, ValidateKeyFormatNoTemplate) { KmsEnvelopeAeadKeyFormat key_format; - *key_format.mutable_dek_template() = AeadKeyTemplates::Aes128Eax(); + key_format.set_kek_uri("Some uri"); + EXPECT_THAT(KmsEnvelopeAeadKeyManager().ValidateKeyFormat(key_format), + Not(IsOk())); +} + +TEST(KmsEnvelopeAeadKeyManagerTest, ValidateKeyFormatInvalidDekTemplate) { + KmsEnvelopeAeadKeyFormat key_format; + key_format.set_kek_uri("Some uri"); + *key_format.mutable_dek_template() = MacKeyTemplates::HmacSha256(); EXPECT_THAT(KmsEnvelopeAeadKeyManager().ValidateKeyFormat(key_format), Not(IsOk())); } @@ -229,6 +241,54 @@ IsOk()); } +class KmsEnvelopeAeadKeyManagerDekTemplatesTest + : public testing::TestWithParam<KeyTemplate> { + void SetUp() override { ASSERT_THAT(AeadConfig::Register(), IsOk()); } +}; + +TEST_P(KmsEnvelopeAeadKeyManagerDekTemplatesTest, EncryptDecryp) { + util::StatusOr<std::string> kek_uri_result = + test::FakeKmsClient::CreateFakeKeyUri(); + ASSERT_THAT(kek_uri_result, IsOk()); + std::string kek_uri = kek_uri_result.value(); + util::Status register_fake_kms_client_status = + test::FakeKmsClient::RegisterNewClient(kek_uri, /*credentials_path=*/""); + ASSERT_THAT(register_fake_kms_client_status, IsOk()); + + KeyTemplate dek_template = GetParam(); + KeyTemplate env_template = + AeadKeyTemplates::KmsEnvelopeAead(kek_uri, dek_template); + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + KeysetHandle::GenerateNew(env_template); + ASSERT_THAT(handle, IsOk()); + util::StatusOr<std::unique_ptr<Aead>> envelope_aead = + (*handle)->GetPrimitive<Aead>(); + ASSERT_THAT(envelope_aead, IsOk()); + + std::string plaintext = "plaintext"; + std::string associated_data = "associated_data"; + util::StatusOr<std::string> ciphertext = + (*envelope_aead)->Encrypt(plaintext, associated_data); + ASSERT_THAT(ciphertext, IsOk()); + util::StatusOr<std::string> decrypted = + (*envelope_aead)->Decrypt(ciphertext.value(), associated_data); + EXPECT_THAT(decrypted, IsOkAndHolds(plaintext)); + + std::string invalid_associated_data = "invalid_associated_data"; + util::StatusOr<std::string> decrypted_with_invalid_associated_data = + (*envelope_aead)->Decrypt(ciphertext.value(), invalid_associated_data); + EXPECT_THAT(decrypted_with_invalid_associated_data.status(), Not(IsOk())); +} + +INSTANTIATE_TEST_SUITE_P( + KmsEnvelopeAeadKeyManagerDekTemplatesTest, + KmsEnvelopeAeadKeyManagerDekTemplatesTest, + testing::Values(AeadKeyTemplates::Aes128Gcm(), + AeadKeyTemplates::Aes256Gcm(), + AeadKeyTemplates::Aes128CtrHmacSha256(), + AeadKeyTemplates::Aes128Eax(), + AeadKeyTemplates::Aes128GcmNoPrefix())); + } // namespace } // namespace tink } // namespace crypto
diff --git a/cc/aead/kms_envelope_aead_test.cc b/cc/aead/kms_envelope_aead_test.cc index 673a1b5..ef8395d 100644 --- a/cc/aead/kms_envelope_aead_test.cc +++ b/cc/aead/kms_envelope_aead_test.cc
@@ -21,15 +21,18 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/endian.h" #include "absl/memory/memory.h" #include "absl/status/status.h" +#include "absl/strings/string_view.h" #include "tink/aead.h" #include "tink/aead/aead_config.h" #include "tink/aead/aead_key_templates.h" +#include "tink/keyset_handle.h" #include "tink/mac/mac_key_templates.h" #include "tink/registry.h" #include "tink/util/status.h" @@ -37,147 +40,249 @@ #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" #include "proto/aes_gcm.pb.h" +#include "tink/internal/ssl_util.h" namespace crypto { namespace tink { namespace { -using crypto::tink::test::DummyAead; -using crypto::tink::test::IsOk; -using crypto::tink::test::StatusIs; -using testing::HasSubstr; +using ::crypto::tink::Aead; +using ::crypto::tink::test::DummyAead; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyTemplate; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Not; +using ::testing::SizeIs; +using ::testing::Test; +constexpr int kEncryptedDekPrefixSize = 4; +constexpr absl::string_view kRemoteAeadName = "kms-backed-aead"; -TEST(KmsEnvelopeAeadTest, BasicEncryptDecrypt) { - EXPECT_THAT(AeadConfig::Register(), IsOk()); +class KmsEnvelopeAeadTest : public Test { + protected: + void TearDown() override { Registry::Reset(); } +}; - auto dek_template = AeadKeyTemplates::Aes128Eax(); - std::string remote_aead_name = "kms-backed-aead"; - auto remote_aead = absl::make_unique<DummyAead>(remote_aead_name); +TEST_F(KmsEnvelopeAeadTest, EncryptDecryptSucceed) { + ASSERT_THAT(AeadConfig::Register(), IsOk()); - auto aead_result = KmsEnvelopeAead::New(dek_template, std::move(remote_aead)); - EXPECT_THAT(aead_result, IsOk()); - auto aead = std::move(aead_result.value()); + // Use an AES-128-GCM primitive as the remote one. + util::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(keyset_handle, IsOk()); + KeyTemplate dek_template = AeadKeyTemplates::Aes128Eax(); + util::StatusOr<std::unique_ptr<Aead>> remote_aead = + (*keyset_handle)->GetPrimitive<Aead>(); + + util::StatusOr<std::unique_ptr<Aead>> envelope_aead = + KmsEnvelopeAead::New(dek_template, *std::move(remote_aead)); + ASSERT_THAT(envelope_aead, IsOk()); + std::string message = "Some data to encrypt."; - std::string aad = "Some data to authenticate."; - auto encrypt_result = aead->Encrypt(message, aad); - EXPECT_THAT(encrypt_result, IsOk()); - auto decrypt_result = aead->Decrypt(encrypt_result.value(), aad); - EXPECT_THAT(decrypt_result, IsOk()); - EXPECT_EQ(decrypt_result.value(), message); + std::string aad = "Some associated data."; + util::StatusOr<std::string> encrypt_result = + (*envelope_aead)->Encrypt(message, aad); + ASSERT_THAT(encrypt_result, IsOk()); + util::StatusOr<std::string> decrypt_result = + (*envelope_aead)->Decrypt(encrypt_result.value(), aad); + EXPECT_THAT(decrypt_result, IsOkAndHolds(message)); } -TEST(KmsEnvelopeAeadTest, NullAead) { - auto dek_template = AeadKeyTemplates::Aes128Eax(); - auto aead_result = KmsEnvelopeAead::New(dek_template, nullptr); - EXPECT_THAT(aead_result.status(), StatusIs(absl::StatusCode::kInvalidArgument, - HasSubstr("non-null"))); -} - -TEST(KmsEnvelopeAeadTest, MissingDekKeyManager) { - Registry::Reset(); - auto dek_template = AeadKeyTemplates::Aes128Eax(); - std::string remote_aead_name = "kms-backed-aead"; - auto remote_aead = absl::make_unique<DummyAead>(remote_aead_name); - auto aead_result = KmsEnvelopeAead::New(dek_template, std::move(remote_aead)); - EXPECT_THAT(aead_result.status(), - StatusIs(absl::StatusCode::kNotFound, HasSubstr("AesEaxKey"))); -} - -TEST(KmsEnvelopeAeadTest, WrongDekPrimitive) { - EXPECT_THAT(AeadConfig::Register(), IsOk()); - auto dek_template = MacKeyTemplates::HmacSha256(); - std::string remote_aead_name = "kms-backed-aead"; - auto remote_aead = absl::make_unique<DummyAead>(remote_aead_name); - auto aead_result = KmsEnvelopeAead::New(dek_template, std::move(remote_aead)); - EXPECT_THAT(aead_result.status(), - StatusIs(absl::StatusCode::kInvalidArgument, - HasSubstr("not among supported primitives"))); -} - -TEST(KmsEnvelopeAeadTest, DecryptionErrors) { - EXPECT_THAT(AeadConfig::Register(), IsOk()); - - auto dek_template = AeadKeyTemplates::Aes128Gcm(); - std::string remote_aead_name = "kms-backed-aead"; - auto remote_aead = absl::make_unique<DummyAead>(remote_aead_name); - - auto aead_result = KmsEnvelopeAead::New(dek_template, std::move(remote_aead)); - EXPECT_THAT(aead_result, IsOk()); - auto aead = std::move(aead_result.value()); - std::string message = "Some data to encrypt."; - std::string aad = "Some data to authenticate."; - auto encrypt_result = aead->Encrypt(message, aad); - EXPECT_THAT(encrypt_result, IsOk()); - auto ct = encrypt_result.value(); - - // Empty ciphertext. - auto decrypt_result = aead->Decrypt("", aad); +TEST_F(KmsEnvelopeAeadTest, NewFailsIfReamoteAeadIsNull) { + KeyTemplate dek_template = AeadKeyTemplates::Aes128Eax(); EXPECT_THAT( - decrypt_result.status(), + KmsEnvelopeAead::New(dek_template, /*remote_aead=*/nullptr).status(), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("non-null"))); +} + +TEST_F(KmsEnvelopeAeadTest, NewFailsIfDekKeyManagerIsNotRegistered) { + KeyTemplate dek_template = AeadKeyTemplates::Aes128Eax(); + auto remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName); + EXPECT_THAT( + KmsEnvelopeAead::New(dek_template, std::move(remote_aead)).status(), + StatusIs(absl::StatusCode::kNotFound, HasSubstr("AesEaxKey"))); +} + +TEST_F(KmsEnvelopeAeadTest, NewFailsIfUsingDekTemplateOfUnsupportedKeyType) { + ASSERT_THAT(AeadConfig::Register(), IsOk()); + KeyTemplate dek_template = MacKeyTemplates::HmacSha256(); + auto remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName); + EXPECT_THAT( + KmsEnvelopeAead::New(dek_template, std::move(remote_aead)).status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("unsupported key type"))); +} + +TEST_F(KmsEnvelopeAeadTest, DecryptFailsWithInvalidCiphertextOrAad) { + ASSERT_THAT(AeadConfig::Register(), IsOk()); + + KeyTemplate dek_template = AeadKeyTemplates::Aes128Gcm(); + auto remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName); + util::StatusOr<std::unique_ptr<Aead>> aead = + KmsEnvelopeAead::New(dek_template, std::move(remote_aead)); + ASSERT_THAT(aead, IsOk()); + + std::string message = "Some data to encrypt."; + std::string aad = "Some associated data."; + util::StatusOr<std::string> encrypt_result = (*aead)->Encrypt(message, aad); + ASSERT_THAT(encrypt_result, IsOk()); + auto ciphertext = absl::string_view(*encrypt_result); + + // Ciphertext has size zero or smaller than 4 bytes. + EXPECT_THAT( + (*aead)->Decrypt(/*ciphertext=*/"", aad).status(), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("too short"))); + EXPECT_THAT( + (*aead)->Decrypt(/*ciphertext=*/"sh", aad).status(), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("too short"))); - // Short ciphertext. - decrypt_result = aead->Decrypt("sh", aad); + // Ciphertext is smaller than the size of the key. + const int dek_encrypted_key_size = absl::big_endian::Load32( + reinterpret_cast<const uint8_t*>(ciphertext.data())); + // We leave only key size and key truncated by one. EXPECT_THAT( - decrypt_result.status(), - StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("too short"))); - - // Truncated ciphertext. - decrypt_result = aead->Decrypt(ct.substr(2), aad); - EXPECT_THAT( - decrypt_result.status(), + (*aead) + ->Decrypt(ciphertext.substr(0, 4 + dek_encrypted_key_size - 1), aad) + .status(), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("invalid"))); - // Corrupted ciphertext. - auto ct_copy = ct; - ct_copy[4] = 'a'; // corrupt serialized DEK. - decrypt_result = aead->Decrypt(ct_copy, aad); + std::string corrupted_ciphertext = *encrypt_result; + // Corrupt the serialized DEK. + corrupted_ciphertext[4] = 'a'; EXPECT_THAT( - decrypt_result.status(), + (*aead)->Decrypt(corrupted_ciphertext, aad).status(), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("invalid"))); // Wrong associated data. - decrypt_result = aead->Decrypt(ct, "wrong aad"); - EXPECT_THAT(decrypt_result.status(), + EXPECT_THAT((*aead)->Decrypt(ciphertext, "wrong aad").status(), StatusIs(absl::StatusCode::kInternal, HasSubstr("Authentication failed"))); } -TEST(KmsEnvelopeAeadTest, KeyFormat) { - EXPECT_THAT(AeadConfig::Register(), IsOk()); +TEST_F(KmsEnvelopeAeadTest, DekMaintainsCorrectKeyFormat) { + ASSERT_THAT(AeadConfig::Register(), IsOk()); - auto dek_template = AeadKeyTemplates::Aes128Gcm(); + KeyTemplate dek_template = AeadKeyTemplates::Aes128Gcm(); + auto kms_remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName); + util::StatusOr<std::unique_ptr<Aead>> aead = + KmsEnvelopeAead::New(dek_template, std::move(kms_remote_aead)); + ASSERT_THAT(aead, IsOk()); - // Construct a remote AEAD which uses same key template for this test. - std::string remote_aead_name = "kms-backed-aead"; - auto remote_aead = absl::make_unique<DummyAead>(remote_aead_name); - - // Create envelope AEAD and encrypt some data. - auto aead_result = KmsEnvelopeAead::New(dek_template, std::move(remote_aead)); - EXPECT_THAT(aead_result, IsOk()); - - auto aead = std::move(aead_result.value()); std::string message = "Some data to encrypt."; - std::string aad = "Some data to authenticate."; - auto encrypt_result = aead->Encrypt(message, aad); - EXPECT_THAT(encrypt_result, IsOk()); - auto ct = encrypt_result.value(); + std::string aad = "Some associated data."; + util::StatusOr<std::string> ciphertext = (*aead)->Encrypt(message, aad); + ASSERT_THAT(ciphertext, IsOk()); - // Recover DEK from ciphertext - auto enc_dek_size = - absl::big_endian::Load32(reinterpret_cast<const uint8_t*>(ct.data())); - - remote_aead = absl::make_unique<DummyAead>(remote_aead_name); - auto dek_decrypt_result = - remote_aead->Decrypt(ct.substr(4, enc_dek_size), ""); + // Recover DEK from ciphertext (see + // https://developers.google.com/tink/wire-format#envelope_encryption). + auto enc_dek_size = absl::big_endian::Load32( + reinterpret_cast<const uint8_t*>(ciphertext->data())); + DummyAead remote_aead = DummyAead(kRemoteAeadName); + absl::string_view encrypted_dek = + absl::string_view(*ciphertext) + .substr(kEncryptedDekPrefixSize, enc_dek_size); + util::StatusOr<std::string> dek_proto_bytes = + remote_aead.Decrypt(encrypted_dek, + /*associated_data=*/""); + ASSERT_THAT(dek_proto_bytes, IsOk()); // Check if we can deserialize a GCM key proto from the decrypted DEK. google::crypto::tink::AesGcmKey key; - EXPECT_THAT(key.ParseFromString(dek_decrypt_result.value()), true); - EXPECT_THAT(key.key_value().size(), testing::Eq(16)); + EXPECT_TRUE(key.ParseFromString(dek_proto_bytes.value())); + EXPECT_THAT(key.key_value(), SizeIs(16)); } +TEST_F(KmsEnvelopeAeadTest, MultipleEncryptionsProduceDifferentDeks) { + ASSERT_THAT(AeadConfig::Register(), IsOk()); + + KeyTemplate dek_template = AeadKeyTemplates::Aes128Gcm(); + auto kms_remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName); + util::StatusOr<std::unique_ptr<Aead>> aead = + KmsEnvelopeAead::New(dek_template, std::move(kms_remote_aead)); + ASSERT_THAT(aead, IsOk()); + + std::string message = "Some data to encrypt."; + std::string aad = "Some associated data."; + + constexpr int kNumIterations = 2; + std::vector<google::crypto::tink::AesGcmKey> ciphertexts; + ciphertexts.reserve(kNumIterations); + for (int i = 0; i < kNumIterations; i++) { + util::StatusOr<std::string> ciphertext = (*aead)->Encrypt(message, aad); + ASSERT_THAT(ciphertext, IsOk()); + + auto enc_dek_size = absl::big_endian::Load32( + reinterpret_cast<const uint8_t*>(ciphertext->data())); + DummyAead remote_aead = DummyAead(kRemoteAeadName); + util::StatusOr<std::string> dek_proto_bytes = remote_aead.Decrypt( + ciphertext->substr(kEncryptedDekPrefixSize, enc_dek_size), + /*associated_data=*/""); + ASSERT_THAT(dek_proto_bytes, IsOk()); + + google::crypto::tink::AesGcmKey key; + ASSERT_TRUE(key.ParseFromString(dek_proto_bytes.value())); + ASSERT_THAT(key.key_value(), SizeIs(16)); + ciphertexts.push_back(key); + } + + for (int i = 0; i < ciphertexts.size() - 1; i++) { + for (int j = i + 1; j < ciphertexts.size(); j++) { + EXPECT_THAT(ciphertexts[i].SerializeAsString(), + Not(Eq(ciphertexts[j].SerializeAsString()))); + } + } +} + +class KmsEnvelopeAeadDekTemplatesTest + : public testing::TestWithParam<KeyTemplate> { + void SetUp() override { ASSERT_THAT(AeadConfig::Register(), IsOk()); } +}; + +TEST_P(KmsEnvelopeAeadDekTemplatesTest, EncryptDecrypt) { + // Use an AES-128-GCM primitive as the remote AEAD. + util::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(keyset_handle, IsOk()); + util::StatusOr<std::unique_ptr<Aead>> remote_aead = + (*keyset_handle)->GetPrimitive<Aead>(); + + KeyTemplate dek_template = GetParam(); + util::StatusOr<std::unique_ptr<Aead>> envelope_aead = + KmsEnvelopeAead::New(dek_template, *std::move(remote_aead)); + ASSERT_THAT(envelope_aead, IsOk()); + + std::string plaintext = "plaintext"; + std::string associated_data = "associated_data"; + util::StatusOr<std::string> ciphertext = + (*envelope_aead)->Encrypt(plaintext, associated_data); + ASSERT_THAT(ciphertext, IsOk()); + util::StatusOr<std::string> decrypted = + (*envelope_aead)->Decrypt(ciphertext.value(), associated_data); + EXPECT_THAT(decrypted, IsOkAndHolds(plaintext)); +} + +std::vector<KeyTemplate> GetTestTemplates() { + std::vector<KeyTemplate> templates = { + AeadKeyTemplates::Aes128Gcm(), + AeadKeyTemplates::Aes256Gcm(), + AeadKeyTemplates::Aes128CtrHmacSha256(), + AeadKeyTemplates::Aes128Eax(), + AeadKeyTemplates::Aes128GcmNoPrefix() + }; + if (internal::IsBoringSsl()) { + templates.push_back(AeadKeyTemplates::XChaCha20Poly1305()); + templates.push_back(AeadKeyTemplates::Aes256GcmSiv()); + } + return templates; +} + +INSTANTIATE_TEST_SUITE_P( + KmsEnvelopeAeadDekTemplatesTest, KmsEnvelopeAeadDekTemplatesTest, + testing::ValuesIn(GetTestTemplates())); + } // namespace } // namespace tink } // namespace crypto
diff --git a/cc/binary_keyset_reader.h b/cc/binary_keyset_reader.h index 01d4220..7b718c1 100644 --- a/cc/binary_keyset_reader.h +++ b/cc/binary_keyset_reader.h
@@ -18,6 +18,7 @@ #define TINK_BINARY_KEYSET_READER_H_ #include <istream> +#include <memory> #include <string> #include "absl/strings/string_view.h"
diff --git a/cc/binary_keyset_writer.h b/cc/binary_keyset_writer.h index 802ba41..5e5a0dc 100644 --- a/cc/binary_keyset_writer.h +++ b/cc/binary_keyset_writer.h
@@ -17,6 +17,7 @@ #ifndef TINK_BINARY_KEYSET_WRITER_H_ #define TINK_BINARY_KEYSET_WRITER_H_ +#include <memory> #include <ostream> #include <utility> @@ -38,7 +39,7 @@ std::unique_ptr<std::ostream> destination_stream); crypto::tink::util::Status - Write(const google::crypto::tink::Keyset& keyset) override;; + Write(const google::crypto::tink::Keyset& keyset) override; crypto::tink::util::Status Write(const google::crypto::tink::EncryptedKeyset& encrypted_keyset) override;
diff --git a/cc/catalogue.h b/cc/catalogue.h deleted file mode 100644 index 0452c4c..0000000 --- a/cc/catalogue.h +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef TINK_CATALOGUE_H_ -#define TINK_CATALOGUE_H_ - -#include <string> - -#include "absl/base/macros.h" -#include "tink/key_manager.h" -#include "tink/util/statusor.h" - -namespace crypto { -namespace tink { - -// This class is deprecated. We don't support catalogues anymore. -template <class P> -class ABSL_DEPRECATED("Catalogues are not supported anymore.") Catalogue { - public: - // Returns a key manager for the given 'type_url', 'primitive_name', - // and version at least 'min_version' (if any found). - // Caller owns the returned manager. - virtual crypto::tink::util::StatusOr<std::unique_ptr<KeyManager<P>>> - GetKeyManager(const std::string& type_url, const std::string& primitive_name, - uint32_t min_version) const = 0; - - virtual ~Catalogue() {} -}; - -} // namespace tink -} // namespace crypto - -#endif // TINK_CATALOGUE_H_
diff --git a/cc/config.h b/cc/config.h deleted file mode 100644 index 099a448..0000000 --- a/cc/config.h +++ /dev/null
@@ -1,116 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef TINK_CONFIG_H_ -#define TINK_CONFIG_H_ - -#include <string> - -#include "absl/status/status.h" -#include "absl/strings/ascii.h" -#include "tink/aead/aead_config.h" -#include "tink/catalogue.h" -#include "tink/daead/deterministic_aead_config.h" -#include "tink/hybrid/hybrid_config.h" -#include "tink/key_manager.h" -#include "tink/mac/mac_config.h" -#include "tink/registry.h" -#include "tink/signature/signature_config.h" -#include "tink/streamingaead/streaming_aead_config.h" -#include "tink/util/errors.h" -#include "tink/util/status.h" -#include "proto/config.pb.h" - -namespace crypto { -namespace tink { - -// Static methods for handling of Tink configurations. -// -// Configurations, i.e., collections of key types and their corresponding key -// managers supported by a specific run-time environment enable control -// of Tink setup via JSON-formatted config files that determine which key types -// are supported, and provide a mechanism for deprecation of obsolete/outdated -// cryptographic schemes (see tink/proto/config.proto for more info). -// -// Example usage: -// -// RegistryConfig registry_config = ...; -// auto status = Config::Register(registry_config); -// -class Config { - public: - // Returns a KeyTypeEntry for Tink key types with the specified parameters. - static std::unique_ptr<google::crypto::tink::KeyTypeEntry> - GetTinkKeyTypeEntry(const std::string& catalogue_name, - const std::string& primitive_name, - const std::string& key_proto_name, - int key_manager_version, bool new_key_allowed); - - // Registers a key manager according to the specification in 'entry'. - template <class P> - static crypto::tink::util::Status Register( - const google::crypto::tink::KeyTypeEntry& entry); - - // Registers key managers and primitive wrappers according to the - // specification in 'config'. - static crypto::tink::util::Status Register( - const google::crypto::tink::RegistryConfig& config); - - private: - static crypto::tink::util::Status Validate( - const google::crypto::tink::KeyTypeEntry& entry); -}; - -/////////////////////////////////////////////////////////////////////////////// -// Implementation details of templated methods. - -// static -template <class P> -crypto::tink::util::Status Config::Register( - const google::crypto::tink::KeyTypeEntry& entry) { - util::Status status; - std::string primitive_name = absl::AsciiStrToLower(entry.primitive_name()); - - if (primitive_name == "mac") { - status = MacConfig::Register(); - } else if (primitive_name == "aead") { - status = AeadConfig::Register(); - } else if (primitive_name == "deterministicaead") { - status = DeterministicAeadConfig::Register(); - } else if (primitive_name == "hybridencrypt" || - primitive_name == "hybriddecrypt") { - status = HybridConfig::Register(); - } else if (primitive_name == "publickeysign" || - primitive_name == "publickeyverify") { - status = SignatureConfig::Register(); - } else if (primitive_name == "streamingaead") { - status = StreamingAeadConfig::Register(); - } else { - status = util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("Non-standard primitive '", entry.primitive_name(), - "', call Registry::RegisterKeyManager " - "and Registry::" - "RegisterPrimitiveWrapper directly.")); - } - if (!status.ok()) return status; - return util::OkStatus(); -} - -} // namespace tink -} // namespace crypto - -#endif // TINK_CONFIG_H_
diff --git a/cc/config/BUILD.bazel b/cc/config/BUILD.bazel index 8ec12df..8d77a3e 100644 --- a/cc/config/BUILD.bazel +++ b/cc/config/BUILD.bazel
@@ -11,7 +11,6 @@ include_prefix = "tink/config", visibility = ["//visibility:public"], deps = [ - "//:config", "//:key_manager", "//:registry", "//daead:deterministic_aead_config", @@ -38,28 +37,6 @@ build_setting_default = False, ) -bool_flag( - name = "tink_use_absl_status", - build_setting_default = False, -) - -config_setting( - name = "absl_status_enabled", - flag_values = {"//config:tink_use_absl_status": "True"}, - visibility = ["//visibility:public"], -) - -bool_flag( - name = "tink_use_absl_statusor", - build_setting_default = False, -) - -config_setting( - name = "absl_statusor_enabled", - flag_values = {"//config:tink_use_absl_statusor": "True"}, - visibility = ["//visibility:public"], -) - cc_library( name = "tink_fips", srcs = ["tink_fips.cc"], @@ -75,6 +52,60 @@ ], ) +cc_library( + name = "fips_140_2", + srcs = ["fips_140_2.cc"], + hdrs = ["fips_140_2.h"], + include_prefix = "tink/config", + visibility = ["//visibility:public"], + deps = [ + "//:configuration", + "//aead:aead_wrapper", + "//aead:aes_ctr_hmac_aead_key_manager", + "//aead:aes_gcm_key_manager", + "//internal:configuration_impl", + "//internal:fips_utils", + "//mac:hmac_key_manager", + "//mac:mac_wrapper", + "//mac/internal:chunked_mac_wrapper", + "//prf:hmac_prf_key_manager", + "//prf:prf_set_wrapper", + "//signature:ecdsa_sign_key_manager", + "//signature:ecdsa_verify_key_manager", + "//signature:public_key_sign_wrapper", + "//signature:public_key_verify_wrapper", + "//signature:rsa_ssa_pkcs1_sign_key_manager", + "//signature:rsa_ssa_pkcs1_verify_key_manager", + "//signature:rsa_ssa_pss_sign_key_manager", + "//signature:rsa_ssa_pss_verify_key_manager", + "@com_google_absl//absl/log:check", + ], +) + +cc_library( + name = "key_gen_fips_140_2", + srcs = ["key_gen_fips_140_2.cc"], + hdrs = ["key_gen_fips_140_2.h"], + include_prefix = "tink/config", + visibility = ["//visibility:public"], + deps = [ + "//:key_gen_configuration", + "//aead:aes_ctr_hmac_aead_key_manager", + "//aead:aes_gcm_key_manager", + "//internal:fips_utils", + "//internal:key_gen_configuration_impl", + "//mac:hmac_key_manager", + "//prf:hmac_prf_key_manager", + "//signature:ecdsa_sign_key_manager", + "//signature:ecdsa_verify_key_manager", + "//signature:rsa_ssa_pkcs1_sign_key_manager", + "//signature:rsa_ssa_pkcs1_verify_key_manager", + "//signature:rsa_ssa_pss_sign_key_manager", + "//signature:rsa_ssa_pss_verify_key_manager", + "@com_google_absl//absl/log:check", + ], +) + # tests cc_test( @@ -84,7 +115,6 @@ deps = [ ":tink_config", "//:aead", - "//:config", "//:deterministic_aead", "//:hybrid_decrypt", "//:hybrid_encrypt", @@ -127,3 +157,50 @@ "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "fips_140_2_test", + srcs = ["fips_140_2_test.cc"], + deps = [ + ":fips_140_2", + "//aead:aead_key_templates", + "//aead:aes_ctr_hmac_aead_key_manager", + "//aead:aes_gcm_key_manager", + "//internal:configuration_impl", + "//internal:fips_utils", + "//internal:key_type_info_store", + "//mac:aes_cmac_key_manager", + "//mac:hmac_key_manager", + "//prf:hmac_prf_key_manager", + "//proto:tink_cc_proto", + "//signature:ecdsa_verify_key_manager", + "//signature:rsa_ssa_pkcs1_verify_key_manager", + "//signature:rsa_ssa_pss_verify_key_manager", + "//util:test_keyset_handle", + "//util:test_matchers", + "//util:test_util", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "key_gen_fips_140_2_test", + srcs = ["key_gen_fips_140_2_test.cc"], + deps = [ + ":key_gen_fips_140_2", + "//aead:aead_key_templates", + "//aead:aes_ctr_hmac_aead_key_manager", + "//aead:aes_gcm_key_manager", + "//internal:fips_utils", + "//internal:key_gen_configuration_impl", + "//mac:aes_cmac_key_manager", + "//mac:hmac_key_manager", + "//prf:hmac_prf_key_manager", + "//proto:tink_cc_proto", + "//signature:ecdsa_verify_key_manager", + "//signature:rsa_ssa_pkcs1_verify_key_manager", + "//signature:rsa_ssa_pss_verify_key_manager", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/cc/config/CMakeLists.txt b/cc/config/CMakeLists.txt index d479a98..010958c 100644 --- a/cc/config/CMakeLists.txt +++ b/cc/config/CMakeLists.txt
@@ -1,5 +1,7 @@ tink_module(config) +add_subdirectory(internal) + tink_cc_library( NAME tink_config SRCS @@ -7,7 +9,6 @@ tink_config.h DEPS absl::core_headers - tink::core::config tink::core::key_manager tink::core::registry tink::daead::deterministic_aead_config @@ -41,6 +42,56 @@ tink::util::status ) +tink_cc_library( + NAME fips_140_2 + SRCS + fips_140_2.cc + fips_140_2.h + DEPS + absl::check + tink::core::configuration + tink::aead::aead_wrapper + tink::aead::aes_ctr_hmac_aead_key_manager + tink::aead::aes_gcm_key_manager + tink::internal::configuration_impl + tink::internal::fips_utils + tink::mac::hmac_key_manager + tink::mac::mac_wrapper + tink::mac::internal::chunked_mac_wrapper + tink::prf::hmac_prf_key_manager + tink::prf::prf_set_wrapper + tink::signature::ecdsa_verify_key_manager + tink::signature::public_key_sign_wrapper + tink::signature::public_key_verify_wrapper + tink::signature::rsa_ssa_pkcs1_sign_key_manager + tink::signature::rsa_ssa_pkcs1_verify_key_manager + tink::signature::rsa_ssa_pss_sign_key_manager + tink::signature::rsa_ssa_pss_verify_key_manager + tink::signature::ecdsa_sign_key_manager +) + +tink_cc_library( + NAME key_gen_fips_140_2 + SRCS + key_gen_fips_140_2.cc + key_gen_fips_140_2.h + DEPS + absl::check + tink::core::key_gen_configuration + tink::aead::aes_ctr_hmac_aead_key_manager + tink::aead::aes_gcm_key_manager + tink::internal::fips_utils + tink::internal::key_gen_configuration_impl + tink::mac::hmac_key_manager + tink::prf::hmac_prf_key_manager + tink::signature::ecdsa_verify_key_manager + tink::signature::rsa_ssa_pkcs1_sign_key_manager + tink::signature::rsa_ssa_pkcs1_verify_key_manager + tink::signature::rsa_ssa_pss_sign_key_manager + tink::signature::rsa_ssa_pss_verify_key_manager + tink::signature::ecdsa_sign_key_manager +) + # tests tink_cc_test( @@ -53,7 +104,6 @@ absl::status tink::core::cc tink::core::aead - tink::core::config tink::core::deterministic_aead tink::core::hybrid_decrypt tink::core::hybrid_encrypt @@ -89,3 +139,50 @@ tink::util::status tink::util::test_matchers ) + +tink_cc_test( + NAME fips_140_2_test + SRCS + fips_140_2_test.cc + DEPS + tink::config::fips_140_2 + gmock + tink::aead::aead_key_templates + tink::aead::aes_ctr_hmac_aead_key_manager + tink::aead::aes_gcm_key_manager + tink::internal::configuration_impl + tink::internal::fips_utils + tink::internal::key_type_info_store + tink::mac::aes_cmac_key_manager + tink::mac::hmac_key_manager + tink::prf::hmac_prf_key_manager + tink::signature::ecdsa_verify_key_manager + tink::signature::rsa_ssa_pkcs1_verify_key_manager + tink::signature::rsa_ssa_pss_verify_key_manager + tink::util::test_keyset_handle + tink::util::test_matchers + tink::util::test_util + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME key_gen_fips_140_2_test + SRCS + key_gen_fips_140_2_test.cc + DEPS + tink::config::key_gen_fips_140_2 + gmock + tink::aead::aead_key_templates + tink::aead::aes_ctr_hmac_aead_key_manager + tink::aead::aes_gcm_key_manager + tink::internal::fips_utils + tink::internal::key_gen_configuration_impl + tink::mac::aes_cmac_key_manager + tink::mac::hmac_key_manager + tink::prf::hmac_prf_key_manager + tink::signature::ecdsa_verify_key_manager + tink::signature::rsa_ssa_pkcs1_verify_key_manager + tink::signature::rsa_ssa_pss_verify_key_manager + tink::util::test_matchers + tink::proto::tink_cc_proto +)
diff --git a/cc/config/fips_140_2.cc b/cc/config/fips_140_2.cc new file mode 100644 index 0000000..0eafff5 --- /dev/null +++ b/cc/config/fips_140_2.cc
@@ -0,0 +1,134 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/config/fips_140_2.h" + +#include "absl/log/check.h" +#include "tink/aead/aead_wrapper.h" +#include "tink/aead/aes_ctr_hmac_aead_key_manager.h" +#include "tink/aead/aes_gcm_key_manager.h" +#include "tink/configuration.h" +#include "tink/internal/configuration_impl.h" +#include "tink/internal/fips_utils.h" +#include "tink/mac/hmac_key_manager.h" +#include "tink/mac/internal/chunked_mac_wrapper.h" +#include "tink/mac/mac_wrapper.h" +#include "tink/prf/hmac_prf_key_manager.h" +#include "tink/prf/prf_set_wrapper.h" +#include "tink/signature/ecdsa_verify_key_manager.h" +#include "tink/signature/public_key_sign_wrapper.h" +#include "tink/signature/public_key_verify_wrapper.h" +#include "tink/signature/rsa_ssa_pkcs1_sign_key_manager.h" +#include "tink/signature/rsa_ssa_pkcs1_verify_key_manager.h" +#include "tink/signature/rsa_ssa_pss_sign_key_manager.h" +#include "tink/signature/rsa_ssa_pss_verify_key_manager.h" +#include "tink/signature/ecdsa_sign_key_manager.h" + +namespace crypto { +namespace tink { +namespace { + +util::Status AddMac(Configuration& config) { + util::Status status = internal::ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<MacWrapper>(), config); + if (!status.ok()) { + return status; + } + status = internal::ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<internal::ChunkedMacWrapper>(), config); + if (!status.ok()) { + return status; + } + + return internal::ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<HmacKeyManager>(), config); +} + +util::Status AddAead(Configuration& config) { + util::Status status = internal::ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<AeadWrapper>(), config); + if (!status.ok()) { + return status; + } + + status = internal::ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<AesCtrHmacAeadKeyManager>(), config); + if (!status.ok()) { + return status; + } + return internal::ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), config); +} + +util::Status AddPrf(Configuration& config) { + util::Status status = internal::ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<PrfSetWrapper>(), config); + if (!status.ok()) { + return status; + } + + return internal::ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<HmacPrfKeyManager>(), config); +} + +util::Status AddSignature(Configuration& config) { + util::Status status = internal::ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<PublicKeySignWrapper>(), config); + if (!status.ok()) { + return status; + } + status = internal::ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<PublicKeyVerifyWrapper>(), config); + if (!status.ok()) { + return status; + } + + status = internal::ConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), config); + if (!status.ok()) { + return status; + } + status = internal::ConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<RsaSsaPssSignKeyManager>(), + absl::make_unique<RsaSsaPssVerifyKeyManager>(), config); + if (!status.ok()) { + return status; + } + return internal::ConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<RsaSsaPkcs1SignKeyManager>(), + absl::make_unique<RsaSsaPkcs1VerifyKeyManager>(), config); +} + +} // namespace + +const Configuration& ConfigFips140_2() { + static const Configuration* instance = [] { + internal::SetFipsRestricted(); + + static Configuration* config = new Configuration(); + CHECK_OK(AddMac(*config)); + CHECK_OK(AddAead(*config)); + CHECK_OK(AddPrf(*config)); + CHECK_OK(AddSignature(*config)); + + return config; + }(); + return *instance; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/config/fips_140_2.h b/cc/config/fips_140_2.h new file mode 100644 index 0000000..24fa9b1 --- /dev/null +++ b/cc/config/fips_140_2.h
@@ -0,0 +1,33 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_CONFIG_FIPS_140_2_H_ +#define TINK_CONFIG_FIPS_140_2_H_ + +#include "tink/configuration.h" + +namespace crypto { +namespace tink { + +// Configuration used to generate primitives using FIPS 140-2-compliant key +// types. Importing this Configuration restricts Tink to FIPS globally and +// requires BoringSSL to be built with the BoringCrypto module. +const Configuration& ConfigFips140_2(); + +} // namespace tink +} // namespace crypto + +#endif // TINK_CONFIG_FIPS_140_2_H_
diff --git a/cc/config/fips_140_2_test.cc b/cc/config/fips_140_2_test.cc new file mode 100644 index 0000000..7f134c9 --- /dev/null +++ b/cc/config/fips_140_2_test.cc
@@ -0,0 +1,142 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/config/fips_140_2.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/aead/aead_key_templates.h" +#include "tink/aead/aes_ctr_hmac_aead_key_manager.h" +#include "tink/aead/aes_gcm_key_manager.h" +#include "tink/internal/configuration_impl.h" +#include "tink/internal/fips_utils.h" +#include "tink/internal/key_type_info_store.h" +#include "tink/mac/aes_cmac_key_manager.h" +#include "tink/mac/hmac_key_manager.h" +#include "tink/prf/hmac_prf_key_manager.h" +#include "tink/signature/ecdsa_verify_key_manager.h" +#include "tink/signature/rsa_ssa_pkcs1_verify_key_manager.h" +#include "tink/signature/rsa_ssa_pss_verify_key_manager.h" +#include "tink/util/test_keyset_handle.h" +#include "tink/util/test_matchers.h" +#include "tink/util/test_util.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; + +class Fips1402Test : public ::testing::Test { + protected: + void TearDown() override { internal::UnSetFipsRestricted(); } +}; + +TEST_F(Fips1402Test, ConfigFips1402) { + if (!internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in FIPS mode"; + } + + util::StatusOr<const internal::KeyTypeInfoStore*> store = + internal::ConfigurationImpl::GetKeyTypeInfoStore(ConfigFips140_2()); + ASSERT_THAT(store, IsOk()); + + EXPECT_THAT((*store)->Get(HmacKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(AesCtrHmacAeadKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(AesGcmKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(HmacPrfKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(EcdsaVerifyKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(RsaSsaPssVerifyKeyManager().get_key_type()), + IsOk()); + EXPECT_THAT((*store)->Get(RsaSsaPkcs1VerifyKeyManager().get_key_type()), + IsOk()); +} + +TEST_F(Fips1402Test, ConfigFips1402FailsInNonFipsMode) { + if (internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in non-FIPS mode"; + } + + EXPECT_DEATH_IF_SUPPORTED( + ConfigFips140_2(), "BoringSSL not built with the BoringCrypto module."); +} + +TEST_F(Fips1402Test, NonFipsTypeNotPresent) { + if (!internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in FIPS mode"; + } + + util::StatusOr<const internal::KeyTypeInfoStore*> store = + internal::ConfigurationImpl::GetKeyTypeInfoStore(ConfigFips140_2()); + ASSERT_THAT(store, IsOk()); + EXPECT_THAT((*store)->Get(AesCmacKeyManager().get_key_type()).status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST_F(Fips1402Test, NewKeyDataAndGetPrimitive) { + if (!internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in FIPS mode"; + } + + // TODO(b/265705174): Replace with KeysetHandle::GenerateNew once that takes a + // config parameter. + util::StatusOr<const internal::KeyTypeInfoStore*> store = + internal::ConfigurationImpl::GetKeyTypeInfoStore(ConfigFips140_2()); + ASSERT_THAT(store, IsOk()); + KeyTemplate templ = AeadKeyTemplates::Aes128Gcm(); + util::StatusOr<internal::KeyTypeInfoStore::Info*> info = + (*store)->Get(templ.type_url()); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<std::unique_ptr<KeyData>> key_data = + (*info)->key_factory().NewKeyData(templ.value()); + ASSERT_THAT(key_data, IsOk()); + + Keyset keyset; + uint32_t key_id = 0; + test::AddKeyData(**key_data, key_id, OutputPrefixType::TINK, + KeyStatusType::ENABLED, &keyset); + keyset.set_primary_key_id(key_id); + + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + util::StatusOr<std::unique_ptr<Aead>> aead = + handle->GetPrimitive<Aead>(ConfigFips140_2()); + EXPECT_THAT(aead, IsOk()); + + std::string plaintext = "plaintext"; + std::string ad = "ad"; + util::StatusOr<std::string> ciphertext = (*aead)->Encrypt(plaintext, ad); + ASSERT_THAT(ciphertext, IsOk()); + + util::StatusOr<std::string> decrypted = (*aead)->Decrypt(*ciphertext, ad); + EXPECT_THAT(decrypted, IsOkAndHolds(plaintext)); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/config/internal/BUILD.bazel b/cc/config/internal/BUILD.bazel new file mode 100644 index 0000000..6d94c3a --- /dev/null +++ b/cc/config/internal/BUILD.bazel
@@ -0,0 +1,29 @@ +package(default_visibility = ["//:__subpackages__"]) + +licenses(["notice"]) + +cc_library( + name = "global_registry", + srcs = ["global_registry.cc"], + hdrs = ["global_registry.h"], + include_prefix = "tink/config/internal", + deps = [ + "//:key_gen_configuration", + "//internal:key_gen_configuration_impl", + "@com_google_absl//absl/log:check", + ], +) + +cc_test( + name = "global_registry_test", + srcs = ["global_registry_test.cc"], + deps = [ + ":global_registry", + "//:keyset_handle", + "//proto:aes_gcm_cc_proto", + "//proto:tink_cc_proto", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/cc/config/internal/CMakeLists.txt b/cc/config/internal/CMakeLists.txt new file mode 100644 index 0000000..3e19a05 --- /dev/null +++ b/cc/config/internal/CMakeLists.txt
@@ -0,0 +1,26 @@ +tink_module(config::internal) + +tink_cc_library( + NAME global_registry + SRCS + global_registry.cc + global_registry.h + DEPS + absl::check + tink::core::key_gen_configuration + tink::internal::key_gen_configuration_impl +) + +tink_cc_test( + NAME global_registry_test + SRCS + global_registry_test.cc + DEPS + tink::config::internal::global_registry + gmock + absl::status + tink::core::keyset_handle + tink::util::test_matchers + tink::proto::aes_gcm_cc_proto + tink::proto::tink_cc_proto +)
diff --git a/cc/config/internal/global_registry.cc b/cc/config/internal/global_registry.cc new file mode 100644 index 0000000..e1e6907 --- /dev/null +++ b/cc/config/internal/global_registry.cc
@@ -0,0 +1,38 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/config/internal/global_registry.h" + +#include "absl/log/check.h" +#include "tink/internal/key_gen_configuration_impl.h" +#include "tink/key_gen_configuration.h" + +namespace crypto { +namespace tink { +namespace internal { + +const KeyGenConfiguration& KeyGenConfigGlobalRegistry() { + static const KeyGenConfiguration* instance = [] { + static KeyGenConfiguration* config = new KeyGenConfiguration(); + CHECK_OK(KeyGenConfigurationImpl::SetGlobalRegistryMode(*config)); + return config; + }(); + return *instance; +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/config/internal/global_registry.h b/cc/config/internal/global_registry.h new file mode 100644 index 0000000..e1c8996 --- /dev/null +++ b/cc/config/internal/global_registry.h
@@ -0,0 +1,34 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_CONFIG_INTERNAL_GLOBAL_REGISTRY_H_ +#define TINK_CONFIG_INTERNAL_GLOBAL_REGISTRY_H_ + +#include "tink/key_gen_configuration.h" + +namespace crypto { +namespace tink { +namespace internal { + +// TODO(b/265705174): Move to public API after API review. +// Used to generate keys using the global crypto::tink::Registry. +const crypto::tink::KeyGenConfiguration& KeyGenConfigGlobalRegistry(); + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_CONFIG_INTERNAL_GLOBAL_REGISTRY_H_
diff --git a/cc/config/internal/global_registry_test.cc b/cc/config/internal/global_registry_test.cc new file mode 100644 index 0000000..b5196f3 --- /dev/null +++ b/cc/config/internal/global_registry_test.cc
@@ -0,0 +1,122 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/config/internal/global_registry.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/keyset_handle.h" +#include "tink/util/test_matchers.h" +#include "proto/aes_gcm.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesGcmKey; +using ::google::crypto::tink::AesGcmKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; + +class FakePrimitive { + public: + explicit FakePrimitive(std::string s) : s_(s) {} + std::string get() { return s_; } + + private: + std::string s_; +}; + +class FakeKeyTypeManager + : public KeyTypeManager<AesGcmKey, AesGcmKeyFormat, List<FakePrimitive>> { + public: + class FakePrimitiveFactory : public PrimitiveFactory<FakePrimitive> { + public: + util::StatusOr<std::unique_ptr<FakePrimitive>> Create( + const AesGcmKey& key) const override { + return absl::make_unique<FakePrimitive>(key.key_value()); + } + }; + + FakeKeyTypeManager() + : KeyTypeManager(absl::make_unique<FakePrimitiveFactory>()) {} + + KeyData::KeyMaterialType key_material_type() const override { + return KeyData::SYMMETRIC; + } + + uint32_t get_version() const override { return 0; } + + const std::string& get_key_type() const override { return key_type_; } + + util::Status ValidateKey(const AesGcmKey& key) const override { + return util::OkStatus(); + } + + util::Status ValidateKeyFormat( + const AesGcmKeyFormat& key_format) const override { + return util::OkStatus(); + } + + util::StatusOr<AesGcmKey> CreateKey( + const AesGcmKeyFormat& key_format) const override { + return AesGcmKey(); + } + + util::StatusOr<AesGcmKey> DeriveKey( + const AesGcmKeyFormat& key_format, + InputStream* input_stream) const override { + return AesGcmKey(); + } + + private: + const std::string key_type_ = + "type.googleapis.com/google.crypto.tink.AesGcmKey"; +}; + +TEST(GlobalRegistryTest, KeyGenConfigGlobalRegistry) { + Registry::Reset(); + + KeyTemplate templ; + templ.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey"); + templ.set_output_prefix_type(OutputPrefixType::TINK); + + EXPECT_THAT( + KeysetHandle::GenerateNew(templ, KeyGenConfigGlobalRegistry()).status(), + StatusIs(absl::StatusCode::kNotFound)); + + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<FakeKeyTypeManager>(), + /*new_key_allowed=*/true), + IsOk()); + EXPECT_THAT( + KeysetHandle::GenerateNew(templ, KeyGenConfigGlobalRegistry()).status(), + IsOk()); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/config/key_gen_fips_140_2.cc b/cc/config/key_gen_fips_140_2.cc new file mode 100644 index 0000000..1c260be --- /dev/null +++ b/cc/config/key_gen_fips_140_2.cc
@@ -0,0 +1,95 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/config/key_gen_fips_140_2.h" + +#include "absl/log/check.h" +#include "tink/aead/aes_ctr_hmac_aead_key_manager.h" +#include "tink/aead/aes_gcm_key_manager.h" +#include "tink/internal/fips_utils.h" +#include "tink/internal/key_gen_configuration_impl.h" +#include "tink/key_gen_configuration.h" +#include "tink/mac/hmac_key_manager.h" +#include "tink/prf/hmac_prf_key_manager.h" +#include "tink/signature/ecdsa_verify_key_manager.h" +#include "tink/signature/rsa_ssa_pkcs1_sign_key_manager.h" +#include "tink/signature/rsa_ssa_pkcs1_verify_key_manager.h" +#include "tink/signature/rsa_ssa_pss_sign_key_manager.h" +#include "tink/signature/rsa_ssa_pss_verify_key_manager.h" +#include "tink/signature/ecdsa_sign_key_manager.h" + +namespace crypto { +namespace tink { +namespace { + +util::Status AddMac(KeyGenConfiguration& config) { + return internal::KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<HmacKeyManager>(), config); +} + +util::Status AddAead(KeyGenConfiguration& config) { + util::Status status = internal::KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<AesCtrHmacAeadKeyManager>(), config); + if (!status.ok()) { + return status; + } + return internal::KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), config); +} + +util::Status AddPrf(KeyGenConfiguration& config) { + return internal::KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<HmacPrfKeyManager>(), config); +} + +util::Status AddSignature(KeyGenConfiguration& config) { + util::Status status = + internal::KeyGenConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), config); + if (!status.ok()) { + return status; + } + status = internal::KeyGenConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<RsaSsaPssSignKeyManager>(), + absl::make_unique<RsaSsaPssVerifyKeyManager>(), config); + if (!status.ok()) { + return status; + } + return internal::KeyGenConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<RsaSsaPkcs1SignKeyManager>(), + absl::make_unique<RsaSsaPkcs1VerifyKeyManager>(), config); +} + +} // namespace + +const KeyGenConfiguration& KeyGenConfigFips140_2() { + static const KeyGenConfiguration* instance = [] { + internal::SetFipsRestricted(); + + static KeyGenConfiguration* config = new KeyGenConfiguration(); + CHECK_OK(AddMac(*config)); + CHECK_OK(AddAead(*config)); + CHECK_OK(AddPrf(*config)); + CHECK_OK(AddSignature(*config)); + + return config; + }(); + return *instance; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/config/key_gen_fips_140_2.h b/cc/config/key_gen_fips_140_2.h new file mode 100644 index 0000000..a22e7ed --- /dev/null +++ b/cc/config/key_gen_fips_140_2.h
@@ -0,0 +1,33 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_CONFIG_KEY_GEN_FIPS_140_2_H_ +#define TINK_CONFIG_KEY_GEN_FIPS_140_2_H_ + +#include "tink/key_gen_configuration.h" + +namespace crypto { +namespace tink { + +// KeyGenConfiguration used to generate keys using FIPS 140-2-compliant key +// types. Importing this KeyGenConfiguration restricts Tink to FIPS globally and +// requires BoringSSL to be built with the BoringCrypto module. +const KeyGenConfiguration& KeyGenConfigFips140_2(); + +} // namespace tink +} // namespace crypto + +#endif // TINK_CONFIG_KEY_GEN_FIPS_140_2_H_
diff --git a/cc/config/key_gen_fips_140_2_test.cc b/cc/config/key_gen_fips_140_2_test.cc new file mode 100644 index 0000000..3b9994c --- /dev/null +++ b/cc/config/key_gen_fips_140_2_test.cc
@@ -0,0 +1,118 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/config/key_gen_fips_140_2.h" + +#include <memory> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/aead/aead_key_templates.h" +#include "tink/aead/aes_ctr_hmac_aead_key_manager.h" +#include "tink/aead/aes_gcm_key_manager.h" +#include "tink/internal/fips_utils.h" +#include "tink/internal/key_gen_configuration_impl.h" +#include "tink/mac/aes_cmac_key_manager.h" +#include "tink/mac/hmac_key_manager.h" +#include "tink/prf/hmac_prf_key_manager.h" +#include "tink/signature/ecdsa_verify_key_manager.h" +#include "tink/signature/rsa_ssa_pkcs1_verify_key_manager.h" +#include "tink/signature/rsa_ssa_pss_verify_key_manager.h" +#include "tink/util/test_matchers.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::KeyTemplate; + +class KeyGenFips1402Test : public testing::Test { + protected: + void TearDown() override { internal::UnSetFipsRestricted(); } +}; + +TEST_F(KeyGenFips1402Test, KeyGenConfigFips1402) { + if (!internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in FIPS mode"; + } + + util::StatusOr<const internal::KeyTypeInfoStore*> store = + internal::KeyGenConfigurationImpl::GetKeyTypeInfoStore( + KeyGenConfigFips140_2()); + ASSERT_THAT(store, IsOk()); + + EXPECT_THAT((*store)->Get(HmacKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(AesCtrHmacAeadKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(AesGcmKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(HmacPrfKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(EcdsaVerifyKeyManager().get_key_type()), IsOk()); + EXPECT_THAT((*store)->Get(RsaSsaPssVerifyKeyManager().get_key_type()), + IsOk()); + EXPECT_THAT((*store)->Get(RsaSsaPkcs1VerifyKeyManager().get_key_type()), + IsOk()); +} + +TEST_F(KeyGenFips1402Test, KeyGenConfigFips1402FailsInNonFipsMode) { + if (internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in non-FIPS mode"; + } + + EXPECT_DEATH_IF_SUPPORTED( + KeyGenConfigFips140_2(), + "BoringSSL not built with the BoringCrypto module."); +} + +TEST_F(KeyGenFips1402Test, NonFipsTypeNotPresent) { + if (!internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in FIPS mode"; + } + + util::StatusOr<const internal::KeyTypeInfoStore*> store = + internal::KeyGenConfigurationImpl::GetKeyTypeInfoStore( + KeyGenConfigFips140_2()); + ASSERT_THAT(store, IsOk()); + EXPECT_THAT((*store)->Get(AesCmacKeyManager().get_key_type()).status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST_F(KeyGenFips1402Test, NewKeyData) { + if (!internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in FIPS mode"; + } + + // TODO(b/265705174): Replace with KeysetHandle::GenerateNew once that takes a + // config parameter. + util::StatusOr<const internal::KeyTypeInfoStore*> store = + internal::KeyGenConfigurationImpl::GetKeyTypeInfoStore( + KeyGenConfigFips140_2()); + ASSERT_THAT(store, IsOk()); + KeyTemplate templ = AeadKeyTemplates::Aes128Gcm(); + util::StatusOr<internal::KeyTypeInfoStore::Info*> info = + (*store)->Get(templ.type_url()); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<std::unique_ptr<KeyData>> key_data = + (*info)->key_factory().NewKeyData(templ.value()); + EXPECT_THAT(key_data, IsOk()); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/config/tink_config.cc b/cc/config/tink_config.cc index d909ab8..5210bfa 100644 --- a/cc/config/tink_config.cc +++ b/cc/config/tink_config.cc
@@ -16,7 +16,6 @@ #include "tink/config/tink_config.h" -#include "tink/config.h" #include "tink/daead/deterministic_aead_config.h" #include "tink/hybrid/hybrid_config.h" #include "tink/key_manager.h"
diff --git a/cc/config/tink_config_test.cc b/cc/config/tink_config_test.cc index 3cd046d..e1cd175 100644 --- a/cc/config/tink_config_test.cc +++ b/cc/config/tink_config_test.cc
@@ -20,7 +20,6 @@ #include "absl/status/status.h" #include "tink/aead.h" #include "tink/aead/aes_gcm_key_manager.h" -#include "tink/config.h" #include "tink/deterministic_aead.h" #include "tink/hybrid_decrypt.h" #include "tink/hybrid_encrypt.h"
diff --git a/cc/config/tink_fips_test.cc b/cc/config/tink_fips_test.cc index 61dc06d..ec15ca7 100644 --- a/cc/config/tink_fips_test.cc +++ b/cc/config/tink_fips_test.cc
@@ -65,7 +65,7 @@ } TEST(TinkFipsTest, CompatibilityChecksWithBoringCrypto) { - if (!FIPS_mode()) { + if (!internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test only run if BoringCrypto module is available."; } @@ -88,7 +88,7 @@ } TEST(TinkFipsTest, CompatibilityChecksWithoutBoringCrypto) { - if (FIPS_mode()) { + if (internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test only run if BoringCrypto module is not available."; }
diff --git a/cc/configuration.h b/cc/configuration.h new file mode 100644 index 0000000..a087737 --- /dev/null +++ b/cc/configuration.h
@@ -0,0 +1,55 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_CONFIGURATION_H_ +#define TINK_CONFIGURATION_H_ + +#include "tink/internal/key_type_info_store.h" +#include "tink/internal/keyset_wrapper_store.h" + +namespace crypto { +namespace tink { + +namespace internal { +class ConfigurationImpl; +} + +// Configuration used to generate primitives using stored primitive wrappers and +// key type managers. +class Configuration { + public: + Configuration() = default; + + // Not copyable or movable. + Configuration(const Configuration&) = delete; + Configuration& operator=(const Configuration&) = delete; + + private: + friend class internal::ConfigurationImpl; + + // When true, Configuration is in global registry mode. For `some_fn(config)` + // with a `config` parameter, this indicates to `some_fn` to use + // crypto::tink::Registry directly. + bool global_registry_mode_ = false; + + crypto::tink::internal::KeyTypeInfoStore key_type_info_store_; + crypto::tink::internal::KeysetWrapperStore keyset_wrapper_store_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_CONFIGURATION_H_
diff --git a/cc/core/cleartext_keyset_handle.cc b/cc/core/cleartext_keyset_handle.cc index 989f95e..9e000ff 100644 --- a/cc/core/cleartext_keyset_handle.cc +++ b/cc/core/cleartext_keyset_handle.cc
@@ -20,6 +20,7 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" @@ -41,14 +42,23 @@ std::unique_ptr<KeysetReader> reader, const absl::flat_hash_map<std::string, std::string>& monitoring_annotations) { - auto keyset_result = reader->Read(); + util::StatusOr<std::unique_ptr<Keyset>> keyset_result = reader->Read(); if (!keyset_result.ok()) { return ToStatusF(absl::StatusCode::kInvalidArgument, "Error reading keyset data: %s", keyset_result.status().message()); } + util::StatusOr<std::vector<std::shared_ptr<const KeysetHandle::Entry>>> + entries = KeysetHandle::GetEntriesFromKeyset(**keyset_result); + if (!entries.ok()) { + return entries.status(); + } + if (entries->size() != (*keyset_result)->key_size()) { + return util::Status(absl::StatusCode::kInternal, + "Error converting keyset proto into key entries."); + } std::unique_ptr<KeysetHandle> handle(new KeysetHandle( - std::move(keyset_result.value()), monitoring_annotations)); + std::move(keyset_result.value()), *entries, monitoring_annotations)); return std::move(handle); }
diff --git a/cc/core/cleartext_keyset_handle_test.cc b/cc/core/cleartext_keyset_handle_test.cc index d172ccf..0ff416c 100644 --- a/cc/core/cleartext_keyset_handle_test.cc +++ b/cc/core/cleartext_keyset_handle_test.cc
@@ -29,7 +29,6 @@ #include "tink/util/test_util.h" #include "proto/tink.pb.h" -using crypto::tink::TestKeysetHandle; using crypto::tink::test::AddRawKey; using crypto::tink::test::AddTinkKey; @@ -49,9 +48,9 @@ TEST_F(CleartextKeysetHandleTest, testRead) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); { // Reader that reads a valid keyset. @@ -76,9 +75,9 @@ TEST_F(CleartextKeysetHandleTest, testWrite) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42);
diff --git a/cc/core/config.cc b/cc/core/config.cc deleted file mode 100644 index fe5cae2..0000000 --- a/cc/core/config.cc +++ /dev/null
@@ -1,87 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "tink/config.h" - -#include <memory> -#include <string> - -#include "absl/status/status.h" -#include "absl/strings/ascii.h" -#include "absl/strings/str_cat.h" -#include "tink/util/errors.h" -#include "tink/util/status.h" -#include "tink/util/statusor.h" -#include "proto/config.pb.h" - -using google::crypto::tink::KeyTypeEntry; - -namespace crypto { -namespace tink { - -// static -std::unique_ptr<google::crypto::tink::KeyTypeEntry> Config::GetTinkKeyTypeEntry( - const std::string& catalogue_name, const std::string& primitive_name, - const std::string& key_proto_name, int key_manager_version, - bool new_key_allowed) { - std::string prefix = "type.googleapis.com/google.crypto.tink."; - std::unique_ptr<KeyTypeEntry> entry(new KeyTypeEntry()); - entry->set_catalogue_name(catalogue_name); - entry->set_primitive_name(primitive_name); - entry->set_type_url(prefix.append(key_proto_name)); - entry->set_key_manager_version(key_manager_version); - entry->set_new_key_allowed(new_key_allowed); - return entry; -} - -// static -crypto::tink::util::Status Config::Validate(const KeyTypeEntry& entry) { - if (entry.type_url().empty()) { - return util::Status(absl::StatusCode::kInvalidArgument, - "Missing type_url."); - } - if (entry.primitive_name().empty()) { - return util::Status(absl::StatusCode::kInvalidArgument, - "Missing primitive_name."); - } - if (entry.catalogue_name().empty()) { - return util::Status(absl::StatusCode::kInvalidArgument, - "Missing catalogue_name."); - } - return util::OkStatus(); -} - -// static -util::Status Config::Register( - const google::crypto::tink::RegistryConfig& config) { - util::Status status; - status = MacConfig::Register(); - if (!status.ok()) return status; - status = AeadConfig::Register(); - if (!status.ok()) return status; - status = DeterministicAeadConfig::Register(); - if (!status.ok()) return status; - status = HybridConfig::Register(); - if (!status.ok()) return status; - status = SignatureConfig::Register(); - if (!status.ok()) return status; - status = StreamingAeadConfig::Register(); - if (!status.ok()) return status; - return util::OkStatus(); -} - -} // namespace tink -} // namespace crypto
diff --git a/cc/core/config_test.cc b/cc/core/config_test.cc deleted file mode 100644 index 2633475..0000000 --- a/cc/core/config_test.cc +++ /dev/null
@@ -1,56 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#include "tink/config.h" - -#include "gtest/gtest.h" -#include "tink/mac.h" -#include "proto/config.pb.h" - -using google::crypto::tink::KeyTypeEntry; - -namespace crypto { -namespace tink { -namespace { - -class ConfigTest : public ::testing::Test { -}; - -TEST_F(ConfigTest, testValidation) { - KeyTypeEntry entry; - - auto status = Config::Register<Mac>(entry); - EXPECT_FALSE(status.ok()); - EXPECT_EQ(absl::StatusCode::kInvalidArgument, status.code()); - - entry.set_type_url("some key type"); - entry.set_catalogue_name("some catalogue"); - status = Config::Register<Mac>(entry); - EXPECT_FALSE(status.ok()); - EXPECT_EQ(absl::StatusCode::kInvalidArgument, status.code()); - - entry.set_primitive_name("some primitive"); - status = Config::Register<Mac>(entry); - EXPECT_FALSE(status.ok()); - EXPECT_EQ(absl::StatusCode::kInvalidArgument, status.code()); -} - - -// TODO(przydatek): add more tests. - -} // namespace -} // namespace tink -} // namespace crypto
diff --git a/cc/core/json_keyset_reader_test.cc b/cc/core/json_keyset_reader_test.cc index dfd5048..5a3702a 100644 --- a/cc/core/json_keyset_reader_test.cc +++ b/cc/core/json_keyset_reader_test.cc
@@ -27,7 +27,6 @@ #include "gtest/gtest.h" #include "absl/strings/escaping.h" #include "absl/strings/substitute.h" -#include "tink/util/protobuf_helper.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" #include "proto/aes_eax.pb.h" @@ -312,11 +311,11 @@ EXPECT_THAT(keyset->primary_key_id(), Eq(4294967275)); } -TEST_F(JsonKeysetReaderTest, ReadNegativeKeyId) { +TEST_F(JsonKeysetReaderTest, RejectsNegativeKeyIds) { std::string json_serialization = absl::Substitute(R"( { - "primaryKeyId": -21, + "primaryKeyId": 711, "key":[ { "keyData":{ @@ -349,6 +348,44 @@ EXPECT_THAT(read_result, Not(IsOk())); } +TEST_F(JsonKeysetReaderTest, RejectsKeyIdLargerThanUint32) { + // 4294967296 = 2^32, which is too large for uint32. + std::string json_serialization = + absl::Substitute(R"( + { + "primaryKeyId": 711, + "key":[ + { + "keyData":{ + "typeUrl":"type.googleapis.com/google.crypto.tink.AesGcmKey", + "keyMaterialType":"SYMMETRIC", + "value": "$0" + }, + "outputPrefixType":"TINK", + "keyId": 4294967296, + "status":"ENABLED" + }, + { + "keyData":{ + "typeUrl":"type.googleapis.com/google.crypto.tink.AesEaxKey", + "keyMaterialType":"SYMMETRIC", + "value":"$1" + }, + "outputPrefixType":"RAW", + "keyId":711, + "status":"ENABLED" + } + ] + })", + absl::Base64Escape(gcm_key_.SerializeAsString()), + absl::Base64Escape(eax_key_.SerializeAsString())); + auto reader_result = JsonKeysetReader::New(json_serialization); + ASSERT_THAT(reader_result, IsOk()); + auto reader = std::move(reader_result.value()); + auto read_result = reader->Read(); + EXPECT_THAT(read_result, Not(IsOk())); +} + } // namespace } // namespace tink } // namespace crypto
diff --git a/cc/core/key_manager_impl.h b/cc/core/key_manager_impl.h index ae0de69..c47c951 100644 --- a/cc/core/key_manager_impl.h +++ b/cc/core/key_manager_impl.h
@@ -17,6 +17,7 @@ #define TINK_CORE_KEY_MANAGER_IMPL_H_ #include <functional> +#include <memory> #include <string> #include <utility>
diff --git a/cc/core/key_type_manager.h b/cc/core/key_type_manager.h index a2a8c6f..0ca2836 100644 --- a/cc/core/key_type_manager.h +++ b/cc/core/key_type_manager.h
@@ -42,7 +42,7 @@ template <typename KeyProto, typename KeyFormatProto> class InternalKeyFactory { public: - virtual ~InternalKeyFactory() {} + virtual ~InternalKeyFactory() = default; // Validates a key format proto. KeyFormatProtos // on which this function returns a non-ok status will not be passed to @@ -69,7 +69,7 @@ template <typename KeyProto> class InternalKeyFactory<KeyProto, void> { public: - virtual ~InternalKeyFactory() {} + virtual ~InternalKeyFactory() = default; }; } // namespace internal @@ -112,7 +112,7 @@ template <typename Primitive> class PrimitiveFactory { public: - virtual ~PrimitiveFactory() {} + virtual ~PrimitiveFactory() = default; virtual crypto::tink::util::StatusOr<std::unique_ptr<Primitive>> Create( const KeyProto& key) const = 0; };
diff --git a/cc/core/key_type_manager_test.cc b/cc/core/key_type_manager_test.cc index 921aea5..f5f8dfb 100644 --- a/cc/core/key_type_manager_test.cc +++ b/cc/core/key_type_manager_test.cc
@@ -38,7 +38,6 @@ namespace { -using ::crypto::tink::test::StatusIs; using ::google::crypto::tink::AesGcmKey; using ::google::crypto::tink::AesGcmKeyFormat; using ::testing::Eq;
diff --git a/cc/core/keyset_handle.cc b/cc/core/keyset_handle.cc index 3b75cfb..eded473 100644 --- a/cc/core/keyset_handle.cc +++ b/cc/core/keyset_handle.cc
@@ -12,19 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. // -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + #include "tink/keyset_handle.h" +#include <cstdint> #include <memory> #include <string> #include <utility> +#include <vector> #include "absl/container/flat_hash_map.h" +#include "absl/log/check.h" #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "tink/aead.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/key_gen_configuration_impl.h" #include "tink/internal/key_info.h" +#include "tink/internal/key_status_util.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/util.h" +#include "tink/key_gen_configuration.h" +#include "tink/key_status.h" #include "tink/keyset_reader.h" #include "tink/keyset_writer.h" #include "tink/registry.h" @@ -36,7 +49,9 @@ using google::crypto::tink::KeyData; using google::crypto::tink::Keyset; using google::crypto::tink::KeysetInfo; +using google::crypto::tink::KeyStatusType; using google::crypto::tink::KeyTemplate; +using google::crypto::tink::OutputPrefixType; namespace crypto { namespace tink { @@ -83,8 +98,145 @@ return util::OkStatus(); } +util::StatusOr<internal::ProtoKeySerialization> ToProtoKeySerialization( + Keyset::Key key) { + absl::optional<int> id_requirement = absl::nullopt; + if (key.output_prefix_type() != OutputPrefixType::RAW) { + id_requirement = key.key_id(); + } + + return internal::ProtoKeySerialization::Create( + key.key_data().type_url(), + RestrictedData(key.key_data().value(), InsecureSecretKeyAccess::Get()), + key.key_data().key_material_type(), key.output_prefix_type(), + id_requirement); +} + } // anonymous namespace +util::Status KeysetHandle::ValidateAt(int index) const { + const Keyset::Key& proto_key = get_keyset().key(index); + OutputPrefixType output_prefix_type = proto_key.output_prefix_type(); + absl::optional<int> id_requirement = absl::nullopt; + if (output_prefix_type != OutputPrefixType::RAW) { + id_requirement = proto_key.key_id(); + } + + if (!internal::IsPrintableAscii(proto_key.key_data().type_url())) { + return util::Status(absl::StatusCode::kFailedPrecondition, + "Non-printable ASCII character in type URL."); + } + + util::StatusOr<KeyStatus> key_status = + internal::FromKeyStatusType(proto_key.status()); + if (!key_status.ok()) return key_status.status(); + + return util::OkStatus(); +} + +util::Status KeysetHandle::Validate() const { + int num_primary = 0; + const Keyset& keyset = get_keyset(); + + for (int i = 0; i < size(); ++i) { + util::Status status = ValidateAt(i); + if (!status.ok()) return status; + + Keyset::Key proto_key = keyset.key(i); + if (proto_key.key_id() == keyset.primary_key_id()) { + ++num_primary; + if (proto_key.status() != KeyStatusType::ENABLED) { + return util::Status(absl::StatusCode::kFailedPrecondition, + "Keyset has primary that is not enabled"); + } + } + } + + if (num_primary < 1) { + return util::Status(absl::StatusCode::kFailedPrecondition, + "Keyset has no primary"); + } + if (num_primary > 1) { + return util::Status(absl::StatusCode::kFailedPrecondition, + "Keyset has more than one primary"); + } + + return util::OkStatus(); +} + +KeysetHandle::Entry KeysetHandle::GetPrimary() const { + util::Status validation = Validate(); + CHECK_OK(validation); + + const Keyset& keyset = get_keyset(); + for (int i = 0; i < keyset.key_size(); ++i) { + if (keyset.key(i).key_id() == keyset.primary_key_id()) { + return (*this)[i]; + } + } + + // Since keyset handle was validated, it should have a valid primary key. + internal::LogFatal("Keyset handle should have a valid primary key."); +} + +KeysetHandle::Entry KeysetHandle::operator[](int index) const { + CHECK(index >= 0 && index < size()) + << "Invalid index " << index << " for keyset of size " << size(); + + if (!entries_.empty() && entries_.size() > index) { + return *entries_[index]; + } + // Since `entries_` has not been populated, the entry must be created on + // demand from the key proto entry at `index` in `keyset_`. This special + // case will no longer be necessary after `keyset_` has been removed from the + // `KeysetHandle` class. + // + // TODO(b/277792846): Remove after transition to rely solely on + // `KeysetHandle::Entry`. + return CreateEntryAt(index); +} + +KeysetHandle::Entry KeysetHandle::CreateEntryAt(int index) const { + CHECK(index >= 0 && index < size()) + << "Invalid index " << index << " for keyset of size " << size(); + + util::Status validation = ValidateAt(index); + CHECK_OK(validation); + + Keyset keyset = get_keyset(); + util::StatusOr<Entry> entry = + CreateEntry(keyset.key(index), keyset.primary_key_id()); + // Status should be OK since this keyset handle has been validated. + CHECK_OK(entry.status()); + return *entry; +} + +util::StatusOr<KeysetHandle::Entry> KeysetHandle::CreateEntry( + const Keyset::Key& proto_key, uint32_t primary_key_id) { + util::StatusOr<internal::ProtoKeySerialization> serialization = + ToProtoKeySerialization(proto_key); + if (!serialization.ok()) { + return serialization.status(); + } + + util::StatusOr<std::shared_ptr<const Key>> key = + internal::MutableSerializationRegistry::GlobalInstance() + .ParseKeyWithLegacyFallback(*serialization, + InsecureSecretKeyAccess::Get()); + if (!key.ok()) { + return key.status(); + } + + util::StatusOr<KeyStatus> key_status = + internal::FromKeyStatusType(proto_key.status()); + if (!key_status.ok()) { + return key_status.status(); + } + + return Entry(*std::move(key), *key_status, proto_key.key_id(), + proto_key.key_id() == primary_key_id); +} + util::StatusOr<std::unique_ptr<KeysetHandle>> KeysetHandle::Read( std::unique_ptr<KeysetReader> reader, const Aead& master_key_aead, const absl::flat_hash_map<std::string, std::string>& @@ -114,8 +266,17 @@ "Error decrypting encrypted keyset: %s", keyset_result.status().message()); } - return absl::WrapUnique( - new KeysetHandle(*std::move(keyset_result), monitoring_annotations)); + util::StatusOr<std::vector<std::shared_ptr<const Entry>>> entries = + GetEntriesFromKeyset(**keyset_result); + if (!entries.ok()) { + return entries.status(); + } + if (entries->size() != (*keyset_result)->key_size()) { + return util::Status(absl::StatusCode::kInternal, + "Error converting keyset proto into key entries."); + } + return absl::WrapUnique(new KeysetHandle(*std::move(keyset_result), *entries, + monitoring_annotations)); } util::StatusOr<std::unique_ptr<KeysetHandle>> KeysetHandle::ReadNoSecret( @@ -131,8 +292,17 @@ if (!validation.ok()) { return validation; } + util::StatusOr<std::vector<std::shared_ptr<const Entry>>> entries = + GetEntriesFromKeyset(keyset); + if (!entries.ok()) { + return entries.status(); + } + if (entries->size() != keyset.key_size()) { + return util::Status(absl::StatusCode::kInternal, + "Error converting keyset proto into key entries."); + } return absl::WrapUnique( - new KeysetHandle(std::move(keyset), monitoring_annotations)); + new KeysetHandle(std::move(keyset), *entries, monitoring_annotations)); } util::Status KeysetHandle::Write(KeysetWriter* writer, @@ -169,17 +339,30 @@ } util::StatusOr<std::unique_ptr<KeysetHandle>> KeysetHandle::GenerateNew( - const KeyTemplate& key_template, + const KeyTemplate& key_template, const KeyGenConfiguration& config, const absl::flat_hash_map<std::string, std::string>& monitoring_annotations) { - Keyset keyset; + auto handle = + absl::WrapUnique(new KeysetHandle(Keyset(), monitoring_annotations)); util::StatusOr<uint32_t> const result = - AddToKeyset(key_template, /*as_primary=*/true, &keyset); + handle->AddKey(key_template, /*as_primary=*/true, config); if (!result.ok()) { return result.status(); } - return absl::WrapUnique( - new KeysetHandle(std::move(keyset), monitoring_annotations)); + return std::move(handle); +} + +util::StatusOr<std::unique_ptr<KeysetHandle>> KeysetHandle::GenerateNew( + const KeyTemplate& key_template, + const absl::flat_hash_map<std::string, std::string>& + monitoring_annotations) { + KeyGenConfiguration config; + util::Status status = + internal::KeyGenConfigurationImpl::SetGlobalRegistryMode(config); + if (!status.ok()) { + return status; + } + return GenerateNew(key_template, config, monitoring_annotations); } util::StatusOr<std::unique_ptr<Keyset::Key>> ExtractPublicKey( @@ -206,44 +389,98 @@ public_keyset->add_key()->Swap(public_key_result.value().get()); } public_keyset->set_primary_key_id(get_keyset().primary_key_id()); + util::StatusOr<std::vector<std::shared_ptr<const Entry>>> entries = + GetEntriesFromKeyset(*public_keyset); + if (!entries.ok()) { + return entries.status(); + } + if (entries->size() != public_keyset->key_size()) { + return util::Status(absl::StatusCode::kInternal, + "Error converting keyset proto into key entries."); + } std::unique_ptr<KeysetHandle> handle( - new KeysetHandle(std::move(public_keyset))); + new KeysetHandle(std::move(public_keyset), *entries)); return std::move(handle); } crypto::tink::util::StatusOr<uint32_t> KeysetHandle::AddToKeyset( const google::crypto::tink::KeyTemplate& key_template, bool as_primary, - Keyset* keyset) { + const KeyGenConfiguration& config, Keyset* keyset) { if (key_template.output_prefix_type() == google::crypto::tink::OutputPrefixType::UNKNOWN_PREFIX) { return util::Status(absl::StatusCode::kInvalidArgument, "key template has unknown prefix"); } - auto key_data_result = Registry::NewKeyData(key_template); - if (!key_data_result.ok()) return key_data_result.status(); - auto key_data = std::move(key_data_result.value()); + + // Generate new key data. + util::StatusOr<std::unique_ptr<KeyData>> key_data; + if (internal::KeyGenConfigurationImpl::GetGlobalRegistryMode(config)) { + key_data = Registry::NewKeyData(key_template); + } else { + util::StatusOr<const internal::KeyTypeInfoStore*> key_type_info_store = + internal::KeyGenConfigurationImpl::GetKeyTypeInfoStore(config); + if (!key_type_info_store.ok()) { + return key_type_info_store.status(); + } + util::StatusOr<const internal::KeyTypeInfoStore::Info*> key_type_info = + (*key_type_info_store)->Get(key_template.type_url()); + if (!key_type_info.ok()) { + return key_type_info.status(); + } + key_data = (*key_type_info)->key_factory().NewKeyData(key_template.value()); + } + if (!key_data.ok()) { + return key_data.status(); + } + + // Add and fill in new key in `keyset`. Keyset::Key* key = keyset->add_key(); - uint32_t key_id = GenerateUnusedKeyId(*keyset); - *(key->mutable_key_data()) = *key_data; - key->set_status(google::crypto::tink::KeyStatusType::ENABLED); - key->set_key_id(key_id); + *(key->mutable_key_data()) = *std::move(key_data).value(); + key->set_status(KeyStatusType::ENABLED); key->set_output_prefix_type(key_template.output_prefix_type()); + + uint32_t key_id = GenerateUnusedKeyId(*keyset); + key->set_key_id(key_id); if (as_primary) { keyset->set_primary_key_id(key_id); } return key_id; } +crypto::tink::util::StatusOr<uint32_t> KeysetHandle::AddKey( + const google::crypto::tink::KeyTemplate& key_template, bool as_primary, + const KeyGenConfiguration& config) { + util::StatusOr<uint32_t> id = + AddToKeyset(key_template, as_primary, config, &keyset_); + if (!id.ok()) { + return id.status(); + } + util::StatusOr<const Entry> entry = CreateEntry( + keyset_.key(keyset_.key_size() - 1), keyset_.primary_key_id()); + if (!entry.ok()) { + return entry.status(); + } + entries_.push_back(std::make_shared<const Entry>(*entry)); + return *id; +} + KeysetInfo KeysetHandle::GetKeysetInfo() const { return KeysetInfoFromKeyset(get_keyset()); } -KeysetHandle::KeysetHandle(Keyset keyset) : keyset_(std::move(keyset)) {} - -KeysetHandle::KeysetHandle(std::unique_ptr<Keyset> keyset) - : keyset_(std::move(*keyset)) {} - -const Keyset& KeysetHandle::get_keyset() const { return keyset_; } +util::StatusOr<std::vector<std::shared_ptr<const KeysetHandle::Entry>>> +KeysetHandle::GetEntriesFromKeyset(const Keyset& keyset) { + std::vector<std::shared_ptr<const Entry>> entries; + for (const Keyset::Key& key : keyset.key()) { + util::StatusOr<const Entry> entry = + CreateEntry(key, keyset.primary_key_id()); + if (!entry.ok()) { + return entry.status(); + } + entries.push_back(std::make_shared<const Entry>(*entry)); + } + return entries; +} } // namespace tink } // namespace crypto
diff --git a/cc/core/keyset_handle_builder.cc b/cc/core/keyset_handle_builder.cc new file mode 100644 index 0000000..223321b --- /dev/null +++ b/cc/core/keyset_handle_builder.cc
@@ -0,0 +1,198 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyset_handle_builder.h" + +#include <iostream> +#include <memory> +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include "absl/log/check.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/key_status.h" +#include "tink/keyset_handle.h" +#include "tink/subtle/random.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::google::crypto::tink::Keyset; + +void SetBuilderEntryAttributes(KeyStatus status, bool is_primary, + absl::optional<int> id, + KeysetHandleBuilder::Entry* entry) { + entry->SetStatus(status); + if (is_primary) { + entry->SetPrimary(); + } else { + entry->UnsetPrimary(); + } + if (id.has_value()) { + entry->SetFixedId(*id); + } else { + entry->SetRandomId(); + } +} + +} // namespace + +KeysetHandleBuilder::KeysetHandleBuilder(const KeysetHandle& handle) { + for (int i = 0; i < handle.size(); ++i) { + KeysetHandle::Entry entry = handle[i]; + KeysetHandleBuilder::Entry builder_entry = + KeysetHandleBuilder::Entry::CreateFromKey( + std::move(entry.key_), entry.GetStatus(), entry.IsPrimary()); + AddEntry(std::move(builder_entry)); + } +} + +KeysetHandleBuilder::Entry KeysetHandleBuilder::Entry::CreateFromKey( + std::shared_ptr<const Key> key, KeyStatus status, bool is_primary) { + absl::optional<int> id_requirement = key->GetIdRequirement(); + auto imported_entry = absl::make_unique<internal::KeyEntry>(std::move(key)); + KeysetHandleBuilder::Entry entry(std::move(imported_entry)); + SetBuilderEntryAttributes(status, is_primary, id_requirement, &entry); + return entry; +} + +KeysetHandleBuilder::Entry KeysetHandleBuilder::Entry::CreateFromParams( + std::shared_ptr<const Parameters> parameters, KeyStatus status, + bool is_primary, absl::optional<int> id) { + auto generated_entry = + absl::make_unique<internal::ParametersEntry>(std::move(parameters)); + KeysetHandleBuilder::Entry entry(std::move(generated_entry)); + SetBuilderEntryAttributes(status, is_primary, id, &entry); + return entry; +} + +util::StatusOr<int> KeysetHandleBuilder::NextIdFromKeyIdStrategy( + internal::KeyIdStrategy strategy, const std::set<int>& ids_so_far) { + if (strategy.strategy == internal::KeyIdStrategyEnum::kFixedId) { + if (!strategy.id_requirement.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Missing fixed id with fixed id strategy."); + } + return *strategy.id_requirement; + } + if (strategy.strategy == internal::KeyIdStrategyEnum::kRandomId) { + int id = 0; + while (id == 0 || ids_so_far.find(id) != ids_so_far.end()) { + id = subtle::Random::GetRandomUInt32(); + } + return id; + } + return util::Status(absl::StatusCode::kInvalidArgument, + "Invalid key id strategy."); +} + +void KeysetHandleBuilder::ClearPrimary() { + for (KeysetHandleBuilder::Entry& entry : entries_) { + entry.UnsetPrimary(); + } +} + +KeysetHandleBuilder& KeysetHandleBuilder::AddEntry( + KeysetHandleBuilder::Entry entry) { + CHECK(!entry.added_to_builder_) + << "Keyset handle builder entry already added to a builder."; + entry.added_to_builder_ = true; + if (entry.IsPrimary()) { + ClearPrimary(); + } + entries_.push_back(std::move(entry)); + return *this; +} + +KeysetHandleBuilder& KeysetHandleBuilder::RemoveEntry(int index) { + CHECK(index >= 0 && index < entries_.size()) + << "Keyset handle builder entry removal index out of range."; + entries_.erase(entries_.begin() + index); + return *this; +} + +util::Status KeysetHandleBuilder::CheckIdAssignments() { + // We only want random id entries after fixed id entries. Otherwise, we might + // randomly pick an id that is later specified as a fixed id. + for (int i = 0; i < entries_.size() - 1; ++i) { + if (entries_[i].HasRandomId() && !entries_[i + 1].HasRandomId()) { + return util::Status(absl::StatusCode::kFailedPrecondition, + "Entries with random ids may only be followed " + "by other entries with random ids."); + } + } + return util::OkStatus(); +} + +util::StatusOr<KeysetHandle> KeysetHandleBuilder::Build() { + if (build_called_) { + return util::Status( + absl::StatusCode::kFailedPrecondition, + "KeysetHandleBuilder::Build may only be called once"); + } + build_called_ = true; + Keyset keyset; + absl::optional<int> primary_id = absl::nullopt; + + util::Status assigned_ids_status = CheckIdAssignments(); + if (!assigned_ids_status.ok()) return assigned_ids_status; + + std::set<int> ids_so_far; + for (KeysetHandleBuilder::Entry& entry : entries_) { + util::StatusOr<int> id = + NextIdFromKeyIdStrategy(entry.GetKeyIdStrategy(), ids_so_far); + if (!id.ok()) return id.status(); + + if (ids_so_far.find(*id) != ids_so_far.end()) { + return util::Status( + absl::StatusCode::kAlreadyExists, + absl::StrFormat("Next id %d is already used in the keyset.", *id)); + } + ids_so_far.insert(*id); + + util::StatusOr<Keyset::Key> key = entry.CreateKeysetKey(*id); + if (!key.ok()) return key.status(); + + *keyset.add_key() = *key; + if (entry.IsPrimary()) { + if (primary_id.has_value()) { + return util::Status( + absl::StatusCode::kInternal, + "Primary is already set in this keyset (should never happen since " + "primary is cleared when a new primary is added)."); + } + primary_id = *id; + } + } + + if (!primary_id.has_value()) { + return util::Status(absl::StatusCode::kFailedPrecondition, + "No primary set in this keyset."); + } + keyset.set_primary_key_id(*primary_id); + util::StatusOr<std::vector<std::shared_ptr<const KeysetHandle::Entry>>> + entries = KeysetHandle::GetEntriesFromKeyset(keyset); + return KeysetHandle(keyset, *std::move(entries)); +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/core/keyset_handle_builder_test.cc b/cc/core/keyset_handle_builder_test.cc new file mode 100644 index 0000000..3d29593 --- /dev/null +++ b/cc/core/keyset_handle_builder_test.cc
@@ -0,0 +1,838 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyset_handle_builder.h" + +#include <memory> +#include <ostream> +#include <set> +#include <sstream> +#include <string> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "tink/config/tink_config.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/legacy_proto_key.h" +#include "tink/internal/legacy_proto_parameters.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/key_status.h" +#include "tink/mac/aes_cmac_key.h" +#include "tink/mac/aes_cmac_parameters.h" +#include "tink/mac/mac_key_templates.h" +#include "tink/partial_key_access.h" +#include "tink/subtle/random.h" +#include "tink/util/status.h" +#include "tink/util/test_matchers.h" +#include "tink/util/test_util.h" +#include "proto/aes_cmac.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::AddTinkKey; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesCmacParams; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsFalse; +using ::testing::IsTrue; +using ::testing::SizeIs; +using ::testing::Test; + +class KeysetHandleBuilderTest : public Test { + protected: + void SetUp() override { + util::Status status = TinkConfig::Register(); + ASSERT_TRUE(status.ok()) << status; + } +}; + +using KeysetHandleBuilderDeathTest = KeysetHandleBuilderTest; + +util::StatusOr<internal::LegacyProtoParameters> CreateLegacyProtoParameters( + KeyTemplate key_template) { + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create(key_template); + if (!serialization.ok()) return serialization.status(); + + return internal::LegacyProtoParameters(*serialization); +} + +TEST_F(KeysetHandleBuilderTest, BuildWithSingleKey) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/123); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); + EXPECT_THAT(*handle, SizeIs(1)); + + EXPECT_THAT((*handle)[0].GetStatus(), Eq(KeyStatus::kEnabled)); + EXPECT_THAT((*handle)[0].GetId(), Eq(123)); + EXPECT_THAT((*handle)[0].IsPrimary(), IsTrue()); + EXPECT_THAT((*handle)[0].GetKey()->GetParameters().HasIdRequirement(), + IsTrue()); +} + +TEST_F(KeysetHandleBuilderTest, BuildWithMultipleKeys) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry0 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kDestroyed, + /*is_primary=*/false, + /*id=*/123); + + KeysetHandleBuilder::Entry entry1 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/456); + + KeysetHandleBuilder::Entry entry2 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kDisabled, + /*is_primary=*/false, /*id=*/789); + + util::StatusOr<KeysetHandle> handle = KeysetHandleBuilder() + .AddEntry(std::move(entry0)) + .AddEntry(std::move(entry1)) + .AddEntry(std::move(entry2)) + .Build(); + ASSERT_THAT(handle.status(), IsOk()); + EXPECT_THAT(*handle, SizeIs(3)); + + EXPECT_THAT((*handle)[0].GetStatus(), Eq(KeyStatus::kDestroyed)); + EXPECT_THAT((*handle)[0].GetId(), Eq(123)); + EXPECT_THAT((*handle)[0].IsPrimary(), IsFalse()); + EXPECT_THAT((*handle)[0].GetKey()->GetParameters().HasIdRequirement(), + IsTrue()); + + EXPECT_THAT((*handle)[1].GetStatus(), Eq(KeyStatus::kEnabled)); + EXPECT_THAT((*handle)[1].GetId(), Eq(456)); + EXPECT_THAT((*handle)[1].IsPrimary(), IsTrue()); + EXPECT_THAT((*handle)[1].GetKey()->GetParameters().HasIdRequirement(), + IsTrue()); + + EXPECT_THAT((*handle)[2].GetStatus(), Eq(KeyStatus::kDisabled)); + EXPECT_THAT((*handle)[2].GetId(), Eq(789)); + EXPECT_THAT((*handle)[2].IsPrimary(), IsFalse()); + EXPECT_THAT((*handle)[2].GetKey()->GetParameters().HasIdRequirement(), + IsTrue()); +} + +TEST_F(KeysetHandleBuilderTest, BuildCopy) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry0 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kDestroyed, + /*is_primary=*/false, + /*id=*/123); + + KeysetHandleBuilder::Entry entry1 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/456); + + KeysetHandleBuilder::Entry entry2 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kDisabled, + /*is_primary=*/false, /*id=*/789); + + util::StatusOr<KeysetHandle> handle = KeysetHandleBuilder() + .AddEntry(std::move(entry0)) + .AddEntry(std::move(entry1)) + .AddEntry(std::move(entry2)) + .Build(); + ASSERT_THAT(handle.status(), IsOk()); + + util::StatusOr<KeysetHandle> copy = KeysetHandleBuilder(*handle).Build(); + ASSERT_THAT(copy.status(), IsOk()); + EXPECT_THAT(copy->size(), Eq(3)); + + EXPECT_THAT((*copy)[0].GetStatus(), Eq(KeyStatus::kDestroyed)); + EXPECT_THAT((*copy)[0].GetId(), Eq(123)); + EXPECT_THAT((*copy)[0].IsPrimary(), IsFalse()); + EXPECT_THAT((*copy)[0].GetKey()->GetParameters().HasIdRequirement(), + IsTrue()); + + EXPECT_THAT((*copy)[1].GetStatus(), Eq(KeyStatus::kEnabled)); + EXPECT_THAT((*copy)[1].GetId(), Eq(456)); + EXPECT_THAT((*copy)[1].IsPrimary(), IsTrue()); + EXPECT_THAT((*copy)[1].GetKey()->GetParameters().HasIdRequirement(), + IsTrue()); + + EXPECT_THAT((*copy)[2].GetStatus(), Eq(KeyStatus::kDisabled)); + EXPECT_THAT((*copy)[2].GetId(), Eq(789)); + EXPECT_THAT((*copy)[2].IsPrimary(), IsFalse()); + EXPECT_THAT((*copy)[2].GetKey()->GetParameters().HasIdRequirement(), + IsTrue()); +} + +TEST_F(KeysetHandleBuilderTest, IsPrimary) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams(*parameters, + KeyStatus::kEnabled, + /*is_primary=*/false, + /*id=*/123); + EXPECT_THAT(entry.IsPrimary(), IsFalse()); + + entry.SetPrimary(); + EXPECT_THAT(entry.IsPrimary(), IsTrue()); +} + +TEST_F(KeysetHandleBuilderTest, SetAndGetStatus) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/false, + /*id=*/123); + + entry.SetStatus(KeyStatus::kDisabled); + EXPECT_THAT(entry.GetStatus(), Eq(KeyStatus::kDisabled)); + entry.SetStatus(KeyStatus::kEnabled); + EXPECT_THAT(entry.GetStatus(), Eq(KeyStatus::kEnabled)); + entry.SetStatus(KeyStatus::kDestroyed); + EXPECT_THAT(entry.GetStatus(), Eq(KeyStatus::kDestroyed)); +} + +TEST_F(KeysetHandleBuilderTest, BuildWithRandomId) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry primary = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true); + + KeysetHandleBuilder builder; + builder.AddEntry(std::move(primary)); + + int num_non_primary_entries = 1 << 16; + for (int i = 0; i < num_non_primary_entries; ++i) { + KeysetHandleBuilder::Entry non_primary = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/false); + builder.AddEntry(std::move(non_primary)); + } + + util::StatusOr<KeysetHandle> handle = builder.Build(); + ASSERT_THAT(handle.status(), IsOk()); + + std::set<int> ids; + for (int i = 0; i < handle->size(); ++i) { + ids.insert((*handle)[i].GetId()); + } + EXPECT_THAT(ids, SizeIs(num_non_primary_entries + 1)); +} + +TEST_F(KeysetHandleBuilderTest, BuildWithRandomIdAfterFixedId) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry fixed = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, /*id=*/123); + + KeysetHandleBuilder::Entry random = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/false); + + util::StatusOr<KeysetHandle> handle = KeysetHandleBuilder() + .AddEntry(std::move(fixed)) + .AddEntry(std::move(random)) + .Build(); + ASSERT_THAT(handle.status(), IsOk()); + + EXPECT_THAT(*handle, SizeIs(2)); + EXPECT_THAT((*handle)[0].GetId(), Eq(123)); +} + +TEST_F(KeysetHandleBuilderTest, BuildWithFixedIdAfterRandomIdFails) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry random = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/false); + + KeysetHandleBuilder::Entry fixed = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, /*id=*/123); + + util::StatusOr<KeysetHandle> handle = KeysetHandleBuilder() + .AddEntry(std::move(random)) + .AddEntry(std::move(fixed)) + .Build(); + ASSERT_THAT(handle.status(), StatusIs(absl::StatusCode::kFailedPrecondition)); +} + +TEST_F(KeysetHandleBuilderDeathTest, AddEntryToAnotherBuilderCrashes) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, /*id=*/123); + + KeysetHandleBuilder builder0; + builder0.AddEntry(std::move(entry)); + KeysetHandleBuilder builder1; + EXPECT_DEATH_IF_SUPPORTED( + builder1.AddEntry(std::move(builder0[0])), + "Keyset handle builder entry already added to a builder."); +} + +TEST_F(KeysetHandleBuilderDeathTest, ReAddEntryToSameBuilderCrashes) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, /*id=*/123); + + KeysetHandleBuilder builder; + builder.AddEntry(std::move(entry)); + EXPECT_DEATH_IF_SUPPORTED( + builder.AddEntry(std::move(builder[0])), + "Keyset handle builder entry already added to a builder."); +} + +TEST_F(KeysetHandleBuilderDeathTest, + AddDereferencedEntryToAnotherBuilderCrashes) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, /*id=*/123); + + KeysetHandleBuilder builder0; + builder0.AddEntry(std::move(entry)); + KeysetHandleBuilder builder1; + EXPECT_DEATH_IF_SUPPORTED( + builder1.AddEntry(std::move(*&(builder0[0]))), + "Keyset handle builder entry already added to a builder."); +} + +TEST_F(KeysetHandleBuilderTest, RemoveEntry) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry0 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/false, /*id=*/123); + + KeysetHandleBuilder::Entry entry1 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, /*id=*/456); + + util::StatusOr<KeysetHandle> handle0 = KeysetHandleBuilder() + .AddEntry(std::move(entry0)) + .AddEntry(std::move(entry1)) + .Build(); + ASSERT_THAT(handle0.status(), IsOk()); + ASSERT_THAT(*handle0, SizeIs(2)); + + util::StatusOr<KeysetHandle> handle1 = + KeysetHandleBuilder(*handle0).RemoveEntry(0).Build(); + ASSERT_THAT(handle1.status(), IsOk()); + ASSERT_THAT(*handle1, SizeIs(1)); + + EXPECT_THAT((*handle1)[0].GetStatus(), Eq(KeyStatus::kEnabled)); + EXPECT_THAT((*handle1)[0].GetId(), Eq(456)); + EXPECT_THAT((*handle1)[0].IsPrimary(), IsTrue()); + EXPECT_THAT((*handle1)[0].GetKey()->GetParameters().HasIdRequirement(), + IsTrue()); +} + +TEST_F(KeysetHandleBuilderDeathTest, RemoveOutofRangeIndexEntryCrashes) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, /*id=*/123); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); + ASSERT_THAT(*handle, SizeIs(1)); + + EXPECT_DEATH_IF_SUPPORTED( + KeysetHandleBuilder(*handle).RemoveEntry(1), + "Keyset handle builder entry removal index out of range."); +} + +TEST_F(KeysetHandleBuilderTest, Size) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry0 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kDestroyed, + /*is_primary=*/false, + /*id=*/123); + + KeysetHandleBuilder::Entry entry1 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/456); + + KeysetHandleBuilder builder; + ASSERT_THAT(builder, SizeIs(0)); + builder.AddEntry(std::move(entry0)); + ASSERT_THAT(builder, SizeIs(1)); + builder.AddEntry(std::move(entry1)); + EXPECT_THAT(builder, SizeIs(2)); +} + +TEST_F(KeysetHandleBuilderTest, NoPrimaryFails) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry0 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/false, + /*id=*/123); + + KeysetHandleBuilder::Entry entry1 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/false, + /*id=*/456); + + util::StatusOr<KeysetHandle> handle = KeysetHandleBuilder() + .AddEntry(std::move(entry0)) + .AddEntry(std::move(entry1)) + .Build(); + ASSERT_THAT(handle.status(), StatusIs(absl::StatusCode::kFailedPrecondition)); +} + +TEST_F(KeysetHandleBuilderTest, RemovePrimaryFails) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry0 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/123); + + KeysetHandleBuilder::Entry entry1 = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/false, + /*id=*/456); + + util::StatusOr<KeysetHandle> handle = KeysetHandleBuilder() + .AddEntry(std::move(entry0)) + .AddEntry(std::move(entry1)) + .RemoveEntry(0) + .Build(); + ASSERT_THAT(handle.status(), StatusIs(absl::StatusCode::kFailedPrecondition)); +} + +TEST_F(KeysetHandleBuilderTest, AddPrimaryClearsOtherPrimary) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder builder; + builder.AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, + /*is_primary=*/true, + /*id=*/123)); + builder.AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, + /*is_primary=*/true, + /*id=*/456)); + + ASSERT_THAT(builder[0].IsPrimary(), IsFalse()); + ASSERT_THAT(builder[1].IsPrimary(), IsTrue()); +} + +TEST_F(KeysetHandleBuilderTest, NoIdStrategySucceeds) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle, IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, DuplicateId) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, + /*is_primary=*/true, + /*id=*/123)) + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, + /*is_primary=*/false, + /*id=*/123)) + .Build(); + ASSERT_THAT(handle.status(), StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST_F(KeysetHandleBuilderTest, CreateBuilderEntryFromParams) { + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromParams( + absl::make_unique<AesCmacParameters>(std::move(*params)), + KeyStatus::kEnabled, /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, CreateBuilderEntryFromLegacyKey) { + Keyset keyset; + Keyset::Key key; + AddTinkKey("first_key_type", 11, key, KeyStatusType::DISABLED, + KeyData::SYMMETRIC, &keyset); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + key.key_data().type_url(), + RestrictedData(key.SerializeAsString(), + InsecureSecretKeyAccess::Get()), + key.key_data().key_material_type(), key.output_prefix_type(), + key.key_id()); + + util::StatusOr<internal::LegacyProtoKey> proto_key = + internal::LegacyProtoKey::Create(*serialization, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(proto_key.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = KeysetHandleBuilder::Entry::CreateFromKey( + absl::make_unique<internal::LegacyProtoKey>(std::move(*proto_key)), + KeyStatus::kEnabled, /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, CreateBuilderEntryFromKey) { + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(32); + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *params, secret, /*id_requirement=*/123, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = KeysetHandleBuilder::Entry::CreateFromKey( + absl::make_unique<AesCmacKey>(std::move(*key)), KeyStatus::kEnabled, + /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, CreateBuilderEntryFromCopyableKey) { + Keyset keyset; + Keyset::Key key; + AddTinkKey("first_key_type", 11, key, KeyStatusType::DISABLED, + KeyData::SYMMETRIC, &keyset); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + key.key_data().type_url(), + RestrictedData(key.SerializeAsString(), + InsecureSecretKeyAccess::Get()), + key.key_data().key_material_type(), key.output_prefix_type(), + key.key_id()); + + util::StatusOr<internal::LegacyProtoKey> proto_key = + internal::LegacyProtoKey::Create(*serialization, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(proto_key.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableKey( + *proto_key, KeyStatus::kEnabled, /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, CreateBuilderEntryFromParameters) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromParams( + absl::make_unique<internal::LegacyProtoParameters>(*parameters), + KeyStatus::kEnabled, /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, CreateBuilderEntryFromCopyableParameters) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, UsePrimitiveFromLegacyProtoParams) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); + + util::StatusOr<std::unique_ptr<Mac>> mac = handle->GetPrimitive<Mac>(); + ASSERT_THAT(mac.status(), IsOk()); + util::StatusOr<std::string> tag = (*mac)->ComputeMac("some input"); + ASSERT_THAT(tag.status(), IsOk()); + util::Status verified = (*mac)->VerifyMac(*tag, "some input"); + EXPECT_THAT(verified, IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, UsePrimitiveFromParams) { + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromParams( + absl::make_unique<AesCmacParameters>(std::move(*params)), + KeyStatus::kEnabled, /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); + + util::StatusOr<std::unique_ptr<Mac>> mac = handle->GetPrimitive<Mac>(); + ASSERT_THAT(mac.status(), IsOk()); + util::StatusOr<std::string> tag = (*mac)->ComputeMac("some input"); + ASSERT_THAT(tag.status(), IsOk()); + util::Status verified = (*mac)->VerifyMac(*tag, "some input"); + EXPECT_THAT(verified, IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, UsePrimitiveFromLegacyProtoKey) { + AesCmacParams params; + params.set_tag_size(16); + google::crypto::tink::AesCmacKey key; + *key.mutable_params() = params; + key.set_version(0); + key.set_key_value(subtle::Random::GetRandomBytes(32)); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesCmacKey", + RestrictedData(key.SerializeAsString(), + InsecureSecretKeyAccess::Get()), + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<internal::LegacyProtoKey> proto_key = + internal::LegacyProtoKey::Create(*serialization, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(proto_key.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableKey( + *proto_key, KeyStatus::kEnabled, /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); + + util::StatusOr<std::unique_ptr<Mac>> mac = handle->GetPrimitive<Mac>(); + ASSERT_THAT(mac.status(), IsOk()); + util::StatusOr<std::string> tag = (*mac)->ComputeMac("some input"); + ASSERT_THAT(tag.status(), IsOk()); + util::Status verified = (*mac)->VerifyMac(*tag, "some input"); + EXPECT_THAT(verified, IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, UsePrimitiveFromKey) { + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(32); + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *params, secret, /*id_requirement=*/123, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = KeysetHandleBuilder::Entry::CreateFromKey( + absl::make_unique<AesCmacKey>(std::move(*key)), KeyStatus::kEnabled, + /*is_primary=*/true); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder().AddEntry(std::move(entry)).Build(); + ASSERT_THAT(handle.status(), IsOk()); + + util::StatusOr<std::unique_ptr<Mac>> mac = handle->GetPrimitive<Mac>(); + ASSERT_THAT(mac.status(), IsOk()); + util::StatusOr<std::string> tag = (*mac)->ComputeMac("some input"); + ASSERT_THAT(tag.status(), IsOk()); + util::Status verified = (*mac)->VerifyMac(*tag, "some input"); + EXPECT_THAT(verified, IsOk()); +} + +TEST_F(KeysetHandleBuilderTest, BuildTwiceFails) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(MacKeyTemplates::AesCmac()); + ASSERT_THAT(parameters.status(), IsOk()); + + KeysetHandleBuilder::Entry entry = + KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/123); + + KeysetHandleBuilder builder; + builder.AddEntry(std::move(entry)); + + EXPECT_THAT(builder.Build(), IsOk()); + EXPECT_THAT(builder.Build().status(), + StatusIs(absl::StatusCode::kFailedPrecondition)); +} + +TEST_F(KeysetHandleBuilderTest, UsePrimitivesFromSplitKeyset) { + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *params, KeyStatus::kEnabled, /*is_primary=*/false)) + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *params, KeyStatus::kEnabled, /*is_primary=*/true)) + .Build(); + ASSERT_THAT(handle, IsOkAndHolds(SizeIs(2))); + + util::StatusOr<KeysetHandle> handle0 = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromKey( + (*handle)[0].GetKey(), KeyStatus::kEnabled, + /*is_primary=*/true)) + .Build(); + ASSERT_THAT(handle0, IsOkAndHolds(SizeIs(1))); + ASSERT_THAT((*handle)[0].GetId(), Eq((*handle0)[0].GetId())); + + util::StatusOr<KeysetHandle> handle1 = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromKey( + (*handle)[1].GetKey(), KeyStatus::kEnabled, + /*is_primary=*/true)) + .Build(); + ASSERT_THAT(handle1, IsOkAndHolds(SizeIs(1))); + ASSERT_THAT((*handle)[1].GetId(), Eq((*handle1)[0].GetId())); + + util::StatusOr<std::unique_ptr<Mac>> mac0 = handle0->GetPrimitive<Mac>(); + ASSERT_THAT(mac0.status(), IsOk()); + util::StatusOr<std::string> tag0 = (*mac0)->ComputeMac("some input"); + ASSERT_THAT(tag0.status(), IsOk()); + + util::StatusOr<std::unique_ptr<Mac>> mac1 = handle1->GetPrimitive<Mac>(); + ASSERT_THAT(mac1.status(), IsOk()); + util::StatusOr<std::string> tag1 = (*mac1)->ComputeMac("some other input"); + ASSERT_THAT(tag1.status(), IsOk()); + + // Use original keyset to verify tags computed from new keysets. + util::StatusOr<std::unique_ptr<Mac>> mac = handle->GetPrimitive<Mac>(); + ASSERT_THAT(mac.status(), IsOk()); + EXPECT_THAT((*mac)->VerifyMac(*tag0, "some input"), IsOk()); + EXPECT_THAT((*mac)->VerifyMac(*tag1, "some other input"), IsOk()); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/core/keyset_handle_test.cc b/cc/core/keyset_handle_test.cc index ae81673..539dc2c 100644 --- a/cc/core/keyset_handle_test.cc +++ b/cc/core/keyset_handle_test.cc
@@ -33,105 +33,123 @@ #include "tink/binary_keyset_reader.h" #include "tink/binary_keyset_writer.h" #include "tink/cleartext_keyset_handle.h" +#include "tink/config/fips_140_2.h" +#include "tink/config/internal/global_registry.h" +#include "tink/config/key_gen_fips_140_2.h" #include "tink/config/tink_config.h" #include "tink/core/key_manager_impl.h" +#include "tink/internal/fips_utils.h" +#include "tink/internal/key_gen_configuration_impl.h" #include "tink/json_keyset_reader.h" #include "tink/json_keyset_writer.h" +#include "tink/key_gen_configuration.h" +#include "tink/key_status.h" #include "tink/primitive_set.h" #include "tink/primitive_wrapper.h" #include "tink/signature/ecdsa_sign_key_manager.h" #include "tink/signature/signature_key_templates.h" -#include "tink/util/protobuf_helper.h" #include "tink/util/status.h" #include "tink/util/test_keyset_handle.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" +#include "proto/aes_gcm_siv.pb.h" #include "proto/tink.pb.h" namespace crypto { namespace tink { -using crypto::tink::TestKeysetHandle; -using crypto::tink::test::AddKeyData; -using crypto::tink::test::AddLegacyKey; -using crypto::tink::test::AddRawKey; -using crypto::tink::test::AddTinkKey; -using crypto::tink::test::DummyAead; -using crypto::tink::test::IsOk; -using crypto::tink::test::StatusIs; -using google::crypto::tink::EcdsaKeyFormat; -using google::crypto::tink::EncryptedKeyset; -using google::crypto::tink::KeyData; -using google::crypto::tink::Keyset; -using google::crypto::tink::KeyStatusType; -using google::crypto::tink::KeyTemplate; -using google::crypto::tink::OutputPrefixType; +using ::crypto::tink::TestKeysetHandle; +using ::crypto::tink::test::AddKeyData; +using ::crypto::tink::test::AddLegacyKey; +using ::crypto::tink::test::AddRawKey; +using ::crypto::tink::test::AddTinkKey; +using ::crypto::tink::test::DummyAead; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesGcmKey; +using ::google::crypto::tink::AesGcmKeyFormat; +using ::google::crypto::tink::AesGcmSivKey; +using ::google::crypto::tink::EcdsaKeyFormat; +using ::google::crypto::tink::EncryptedKeyset; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; using ::testing::_; +using ::testing::Eq; +using ::testing::IsFalse; +using ::testing::IsTrue; using ::testing::Not; +using ::testing::SizeIs; namespace { class KeysetHandleTest : public ::testing::Test { protected: void SetUp() override { + Registry::Reset(); auto status = TinkConfig::Register(); ASSERT_TRUE(status.ok()) << status; + + internal::UnSetFipsRestricted(); } }; -// Dummy key factory that is required to create a key manager. -class DummyAeadKeyFactory : public KeyFactory { +using KeysetHandleDeathTest = KeysetHandleTest; + +// Fake AEAD key type manager for testing. +class FakeAeadKeyManager + : public KeyTypeManager<AesGcmKey, AesGcmKeyFormat, List<Aead>> { public: - explicit DummyAeadKeyFactory(absl::string_view key_type) - : key_type_(key_type) {} + class AeadFactory : public PrimitiveFactory<Aead> { + public: + explicit AeadFactory(absl::string_view key_type) : key_type_(key_type) {} - util::StatusOr<std::unique_ptr<portable_proto::MessageLite>> NewKey( - const portable_proto::MessageLite& key_format) const override { - return util::Status(absl::StatusCode::kUnimplemented, "Unimplemented"); - } + util::StatusOr<std::unique_ptr<Aead>> Create( + const AesGcmKey& key) const override { + return {absl::make_unique<DummyAead>(key_type_)}; + } - util::StatusOr<std::unique_ptr<portable_proto::MessageLite>> NewKey( - absl::string_view serialized_key_format) const override { - return util::Status(absl::StatusCode::kUnimplemented, "Unimplemented"); - } + private: + const std::string key_type_; + }; - util::StatusOr<std::unique_ptr<KeyData>> NewKeyData( - absl::string_view serialized_key_format) const override { - auto key_data = absl::make_unique<KeyData>(); - key_data->set_type_url(key_type_); - std::string serialized_key_format_str(serialized_key_format); - key_data->set_value(serialized_key_format_str); - return std::move(key_data); - } - - private: - const std::string key_type_; -}; - -// Fake Aead key manager for testing. -class FakeAeadKeyManager : public KeyManager<Aead> { - public: explicit FakeAeadKeyManager(absl::string_view key_type) - : key_type_(key_type), key_factory_(key_type) {} + : KeyTypeManager(absl::make_unique<AeadFactory>(key_type)), + key_type_(key_type) {} - util::StatusOr<std::unique_ptr<Aead>> GetPrimitive( - const KeyData& key) const override { - return {absl::make_unique<DummyAead>(key_type_)}; - } - - util::StatusOr<std::unique_ptr<Aead>> GetPrimitive( - const portable_proto::MessageLite& key) const override { - return util::Status(absl::StatusCode::kUnknown, - "DummyAeadKeyFactory cannot construct an aead"); + google::crypto::tink::KeyData::KeyMaterialType key_material_type() + const override { + return google::crypto::tink::KeyData::SYMMETRIC; } uint32_t get_version() const override { return 0; } + const std::string& get_key_type() const override { return key_type_; } - const KeyFactory& get_key_factory() const override { return key_factory_; } + + crypto::tink::util::Status ValidateKey(const AesGcmKey& key) const override { + return util::OkStatus(); + } + + crypto::tink::util::Status ValidateKeyFormat( + const AesGcmKeyFormat& key_format) const override { + return util::OkStatus(); + } + + crypto::tink::util::StatusOr<AesGcmKey> CreateKey( + const AesGcmKeyFormat& key_format) const override { + return AesGcmKey(); + } + + crypto::tink::util::StatusOr<AesGcmKey> DeriveKey( + const AesGcmKeyFormat& key_format, + InputStream* input_stream) const override { + return AesGcmKey(); + } private: const std::string key_type_; - const DummyAeadKeyFactory key_factory_; }; class MockAeadPrimitiveWrapper : public PrimitiveWrapper<Aead, Aead> { @@ -145,9 +163,9 @@ Keyset GetTestKeyset() { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); return keyset; @@ -157,9 +175,9 @@ Keyset GetPublicTestKeyset() { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PUBLIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::REMOTE, &keyset); keyset.set_primary_key_id(42); return keyset; @@ -168,9 +186,9 @@ TEST_F(KeysetHandleTest, ReadEncryptedKeysetBinary) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); @@ -265,12 +283,12 @@ Registry::Reset(); ASSERT_THAT(Registry::RegisterPrimitiveWrapper(std::move(primitive_wrapper)), IsOk()); - ASSERT_THAT(Registry::RegisterKeyManager( - absl::make_unique<FakeAeadKeyManager>("some key type"), + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<FakeAeadKeyManager>("some_key_type"), /*new_key_allowed=*/true), IsOk()); - ASSERT_THAT(Registry::RegisterKeyManager( - absl::make_unique<FakeAeadKeyManager>("some other key type"), + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<FakeAeadKeyManager>("some_other_key_type"), /*new_key_allowed=*/true), IsOk()); @@ -283,9 +301,9 @@ TEST_F(KeysetHandleTest, ReadEncryptedKeysetJson) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); @@ -366,9 +384,9 @@ // Prepare a valid keyset handle Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); auto reader = @@ -406,9 +424,9 @@ TEST_F(KeysetHandleTest, ReadEncryptedKeysetWithAssociatedDataGoodKeyset) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); @@ -458,12 +476,12 @@ Registry::Reset(); ASSERT_THAT(Registry::RegisterPrimitiveWrapper(std::move(primitive_wrapper)), IsOk()); - ASSERT_THAT(Registry::RegisterKeyManager( - absl::make_unique<FakeAeadKeyManager>("some key type"), + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<FakeAeadKeyManager>("some_key_type"), /*new_key_allowed=*/true), IsOk()); - ASSERT_THAT(Registry::RegisterKeyManager( - absl::make_unique<FakeAeadKeyManager>("some other key type"), + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<FakeAeadKeyManager>("some_other_key_type"), /*new_key_allowed=*/true), IsOk()); @@ -476,9 +494,9 @@ TEST_F(KeysetHandleTest, ReadEncryptedKeysetWithAssociatedDataWrongAad) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); DummyAead aead("dummy aead 42"); @@ -497,9 +515,9 @@ TEST_F(KeysetHandleTest, ReadEncryptedKeysetWithAssociatedDataEmptyAad) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); DummyAead aead("dummy aead 42"); @@ -518,9 +536,9 @@ // Prepare a valid keyset handle Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); auto reader = @@ -556,55 +574,94 @@ EXPECT_EQ(absl::StatusCode::kInvalidArgument, status.code()); } -TEST_F(KeysetHandleTest, GenerateNewKeysetHandle) { - const google::crypto::tink::KeyTemplate* key_templates[] = { +TEST_F(KeysetHandleTest, GenerateNew) { + const google::crypto::tink::KeyTemplate* templates[] = { &AeadKeyTemplates::Aes128Gcm(), &AeadKeyTemplates::Aes256Gcm(), &AeadKeyTemplates::Aes128CtrHmacSha256(), &AeadKeyTemplates::Aes256CtrHmacSha256(), }; - for (auto templ : key_templates) { - auto handle_result = KeysetHandle::GenerateNew(*templ); - EXPECT_TRUE(handle_result.ok()) - << "Failed for template:\n " << templ->SerializeAsString() - << "\n with status: "<< handle_result.status(); + KeyGenConfiguration config; + ASSERT_THAT(internal::KeyGenConfigurationImpl::SetGlobalRegistryMode(config), + IsOk()); + for (auto templ : templates) { + EXPECT_THAT(KeysetHandle::GenerateNew(*templ).status(), IsOk()); + EXPECT_THAT(KeysetHandle::GenerateNew(*templ, config).status(), IsOk()); } } +TEST_F(KeysetHandleTest, GenerateNewWithBespokeConfig) { + KeyGenConfiguration config; + EXPECT_THAT( + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm(), config).status(), + StatusIs(absl::StatusCode::kNotFound)); + + ASSERT_THAT(internal::KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), config), + IsOk()); + EXPECT_THAT( + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm(), config).status(), + IsOk()); +} + +TEST_F(KeysetHandleTest, GenerateNewWithGlobalRegistryConfig) { + KeyGenConfiguration config; + ASSERT_THAT(internal::KeyGenConfigurationImpl::SetGlobalRegistryMode(config), + IsOk()); + EXPECT_THAT(KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm(), config), + IsOk()); +} + TEST_F(KeysetHandleTest, GenerateNewWithAnnotations) { const absl::flat_hash_map<std::string, std::string> kAnnotations = { {"key1", "value1"}, {"key2", "value2"}}; - // The template used doesn't make any different w.r.t. annotations. - util::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = + // `handle` depends on the global registry. + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm(), kAnnotations); - ASSERT_THAT(keyset_handle, IsOk()); - auto primitive_wrapper = absl::make_unique<MockAeadPrimitiveWrapper>(); - absl::flat_hash_map<std::string, std::string> generated_annotations; - EXPECT_CALL(*primitive_wrapper, Wrap(_)) - .WillOnce( - [&generated_annotations]( - std::unique_ptr<PrimitiveSet<Aead>> generated_primitive_set) { - generated_annotations = generated_primitive_set->get_annotations(); - std::unique_ptr<Aead> aead = absl::make_unique<DummyAead>(""); - return aead; - }); - Registry::Reset(); - ASSERT_THAT(Registry::RegisterPrimitiveWrapper(std::move(primitive_wrapper)), - IsOk()); - ASSERT_THAT(Registry::RegisterKeyManager( - absl::make_unique<FakeAeadKeyManager>( - "type.googleapis.com/google.crypto.tink.AesGcmKey"), - true), - IsOk()); + ASSERT_THAT(handle, IsOk()); - EXPECT_THAT((*keyset_handle)->GetPrimitive<Aead>(), IsOk()); - EXPECT_EQ(generated_annotations, kAnnotations); - // This is needed to cleanup mocks. - Registry::Reset(); + // `config_handle` uses a config that depends on the global registry. + KeyGenConfiguration config; + ASSERT_THAT(internal::KeyGenConfigurationImpl::SetGlobalRegistryMode(config), + IsOk()); + util::StatusOr<std::unique_ptr<KeysetHandle>> config_handle = + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm(), config, + kAnnotations); + ASSERT_THAT(config_handle, IsOk()); + + for (KeysetHandle h : {**handle, **config_handle}) { + auto primitive_wrapper = absl::make_unique<MockAeadPrimitiveWrapper>(); + absl::flat_hash_map<std::string, std::string> generated_annotations; + EXPECT_CALL(*primitive_wrapper, Wrap(_)) + .WillOnce( + [&generated_annotations]( + std::unique_ptr<PrimitiveSet<Aead>> generated_primitive_set) { + generated_annotations = + generated_primitive_set->get_annotations(); + std::unique_ptr<Aead> aead = absl::make_unique<DummyAead>(""); + return aead; + }); + + Registry::Reset(); + ASSERT_THAT( + Registry::RegisterPrimitiveWrapper(std::move(primitive_wrapper)), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<FakeAeadKeyManager>( + "type.googleapis.com/google.crypto.tink.AesGcmKey"), + /*new_key_allowed=*/true), + IsOk()); + + EXPECT_THAT(h.GetPrimitive<Aead>(), IsOk()); + EXPECT_EQ(generated_annotations, kAnnotations); + + // This is needed to cleanup mocks. + Registry::Reset(); + } } -TEST_F(KeysetHandleTest, GenerateNewKeysetHandleErrors) { +TEST_F(KeysetHandleTest, GenerateNewErrors) { KeyTemplate templ; templ.set_type_url("type.googleapis.com/some.unknown.KeyType"); templ.set_output_prefix_type(OutputPrefixType::TINK); @@ -621,7 +678,6 @@ EXPECT_FALSE(handle_result.ok()); } - void CompareKeyMetadata(const Keyset::Key& expected, const Keyset::Key& actual) { EXPECT_EQ(expected.status(), actual.status()); @@ -630,9 +686,9 @@ } TEST_F(KeysetHandleTest, GetPublicKeysetHandle) { - { // A keyset with a single key. - auto handle_result = KeysetHandle::GenerateNew( - SignatureKeyTemplates::EcdsaP256()); + { // A keyset with a single key. + auto handle_result = + KeysetHandle::GenerateNew(SignatureKeyTemplates::EcdsaP256()); ASSERT_TRUE(handle_result.ok()) << handle_result.status(); auto handle = std::move(handle_result.value()); auto public_handle_result = handle->GetPublicKeysetHandle(); @@ -646,7 +702,7 @@ EXPECT_EQ(KeyData::ASYMMETRIC_PUBLIC, public_keyset.key(0).key_data().key_material_type()); } - { // A keyset with multiple keys. + { // A keyset with multiple keys. EcdsaSignKeyManager key_manager; Keyset keyset; int key_count = 3; @@ -684,9 +740,9 @@ } TEST_F(KeysetHandleTest, GetPublicKeysetHandleErrors) { - { // A keyset with a single key. - auto handle_result = KeysetHandle::GenerateNew( - AeadKeyTemplates::Aes128Eax()); + { // A keyset with a single key. + auto handle_result = + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Eax()); ASSERT_TRUE(handle_result.ok()) << handle_result.status(); auto handle = std::move(handle_result.value()); auto public_handle_result = handle->GetPublicKeysetHandle(); @@ -694,7 +750,7 @@ EXPECT_PRED_FORMAT2(testing::IsSubstring, "ASYMMETRIC_PRIVATE", std::string(public_handle_result.status().message())); } - { // A keyset with multiple keys. + { // A keyset with multiple keys. Keyset keyset; EcdsaKeyFormat ecdsa_key_format; @@ -760,6 +816,84 @@ EXPECT_EQ(aead->Decrypt(raw_encryption, aad).value(), plaintext); } +TEST_F(KeysetHandleTest, GetPrimitiveWithBespokeConfigSucceeds) { + KeyGenConfiguration key_gen_config; + ASSERT_THAT(internal::KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), key_gen_config), + IsOk()); + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm(), key_gen_config); + ASSERT_THAT(handle, IsOk()); + + Configuration config; + ASSERT_THAT(internal::ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), config), + IsOk()); + ASSERT_THAT(internal::ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<AeadWrapper>(), config), + IsOk()); + + EXPECT_THAT((*handle)->GetPrimitive<Aead>(config).status(), IsOk()); +} + +TEST_F(KeysetHandleTest, GetPrimitiveWithBespokeConfigFailsIfEmpty) { + KeyGenConfiguration key_gen_config; + ASSERT_THAT(internal::KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), key_gen_config), + IsOk()); + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm(), key_gen_config); + ASSERT_THAT(handle, IsOk()); + + Configuration config; + EXPECT_THAT((*handle)->GetPrimitive<Aead>(config).status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST_F(KeysetHandleTest, GetPrimitiveWithGlobalRegistryConfig) { + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm(), + internal::KeyGenConfigGlobalRegistry()); + ASSERT_THAT(handle, IsOk()); + + // TODO(b/265705174): Replace with ConfigGlobalRegistry instance. + Configuration config; + ASSERT_THAT(internal::ConfigurationImpl::SetGlobalRegistryMode(config), + IsOk()); + EXPECT_THAT((*handle)->GetPrimitive<Aead>(config), IsOk()); +} + +TEST_F(KeysetHandleTest, GetPrimitiveWithConfigFips1402) { + if (!internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in FIPS mode"; + } + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm(), + KeyGenConfigFips140_2()); + ASSERT_THAT(handle, IsOk()); + EXPECT_THAT((*handle)->GetPrimitive<Aead>(ConfigFips140_2()), IsOk()); +} + +TEST_F(KeysetHandleTest, GetPrimitiveWithConfigFips1402FailsWithNonFipsHandle) { + if (!internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only test in FIPS mode"; + } + + Keyset keyset; + AesGcmSivKey key_proto; + *key_proto.mutable_key_value() = subtle::Random::GetRandomBytes(16); + test::AddTinkKey(AeadKeyTemplates::Aes256GcmSiv().type_url(), /*key_id=*/13, + key_proto, KeyStatusType::ENABLED, KeyData::SYMMETRIC, + &keyset); + keyset.set_primary_key_id(13); + + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + EXPECT_THAT(handle->GetPrimitive<Aead>(ConfigFips140_2()).status(), + StatusIs(absl::StatusCode::kNotFound)); +} + // Tests that GetPrimitive(nullptr) fails with a non-ok status. TEST_F(KeysetHandleTest, GetPrimitiveNullptrKeyManager) { Keyset keyset; @@ -803,9 +937,9 @@ TEST_F(KeysetHandleTest, ReadNoSecret) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PUBLIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::REMOTE, &keyset); keyset.set_primary_key_id(42); auto handle_result = KeysetHandle::ReadNoSecret(keyset.SerializeAsString()); @@ -841,12 +975,12 @@ Registry::Reset(); ASSERT_THAT(Registry::RegisterPrimitiveWrapper(std::move(primitive_wrapper)), IsOk()); - ASSERT_THAT(Registry::RegisterKeyManager( - absl::make_unique<FakeAeadKeyManager>("some key type"), + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<FakeAeadKeyManager>("some_key_type"), /*new_key_allowed=*/true), IsOk()); - ASSERT_THAT(Registry::RegisterKeyManager( - absl::make_unique<FakeAeadKeyManager>("some other key type"), + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<FakeAeadKeyManager>("some_other_key_type"), /*new_key_allowed=*/true), IsOk()); @@ -859,7 +993,7 @@ TEST_F(KeysetHandleTest, ReadNoSecretFailForTypeUnknown) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::UNKNOWN_KEYMATERIAL, &keyset); keyset.set_primary_key_id(42); auto result = KeysetHandle::ReadNoSecret(keyset.SerializeAsString()); @@ -869,7 +1003,7 @@ TEST_F(KeysetHandleTest, ReadNoSecretFailForTypeSymmetric) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); auto result = KeysetHandle::ReadNoSecret(keyset.SerializeAsString()); @@ -879,7 +1013,7 @@ TEST_F(KeysetHandleTest, ReadNoSecretFailForTypeAssymmetricPrivate) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PRIVATE, &keyset); keyset.set_primary_key_id(42); auto result = KeysetHandle::ReadNoSecret(keyset.SerializeAsString()); @@ -889,13 +1023,13 @@ TEST_F(KeysetHandleTest, ReadNoSecretFailForHidden) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PUBLIC, &keyset); for (int i = 0; i < 10; ++i) { AddTinkKey(absl::StrCat("more key type", i), i, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PUBLIC, &keyset); } - AddRawKey("some other key type", 10, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 10, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PRIVATE, &keyset); for (int i = 0; i < 10; ++i) { AddRawKey(absl::StrCat("more key type", i + 100), i + 100, key, @@ -916,9 +1050,9 @@ TEST_F(KeysetHandleTest, WriteNoSecret) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PUBLIC, &keyset); - AddRawKey("some other key type", 711, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 711, key, KeyStatusType::ENABLED, KeyData::REMOTE, &keyset); keyset.set_primary_key_id(42); @@ -935,7 +1069,7 @@ TEST_F(KeysetHandleTest, WriteNoSecretFailForTypeUnknown) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::UNKNOWN_KEYMATERIAL, &keyset); keyset.set_primary_key_id(42); @@ -952,7 +1086,7 @@ TEST_F(KeysetHandleTest, WriteNoSecretFailForTypeSymmetric) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::SYMMETRIC, &keyset); keyset.set_primary_key_id(42); @@ -969,7 +1103,7 @@ TEST_F(KeysetHandleTest, WriteNoSecretFailForTypeAssymmetricPrivate) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PRIVATE, &keyset); keyset.set_primary_key_id(42); @@ -986,13 +1120,13 @@ TEST_F(KeysetHandleTest, WriteNoSecretFailForHidden) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PUBLIC, &keyset); for (int i = 0; i < 10; ++i) { AddTinkKey(absl::StrCat("more key type", i), i, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PUBLIC, &keyset); } - AddRawKey("some other key type", 10, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 10, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PRIVATE, &keyset); for (int i = 0; i < 10; ++i) { AddRawKey(absl::StrCat("more key type", i + 100), i + 100, key, @@ -1014,13 +1148,13 @@ TEST_F(KeysetHandleTest, GetKeysetInfo) { Keyset keyset; Keyset::Key key; - AddTinkKey("some key type", 42, key, KeyStatusType::ENABLED, + AddTinkKey("some_key_type", 42, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PUBLIC, &keyset); for (int i = 0; i < 10; ++i) { AddTinkKey(absl::StrCat("more key type", i), i, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PUBLIC, &keyset); } - AddRawKey("some other key type", 10, key, KeyStatusType::ENABLED, + AddRawKey("some_other_key_type", 10, key, KeyStatusType::ENABLED, KeyData::ASYMMETRIC_PRIVATE, &keyset); for (int i = 0; i < 10; ++i) { AddRawKey(absl::StrCat("more key type", i + 100), i + 100, key, @@ -1042,6 +1176,192 @@ } } +TEST_F(KeysetHandleTest, GetEntryFromSingleKeyKeyset) { + Keyset keyset; + Keyset::Key key; + AddTinkKey("first_key_type", 11, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + keyset.set_primary_key_id(11); + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + ASSERT_THAT(handle->Validate(), IsOk()); + ASSERT_THAT(*handle, SizeIs(1)); + + ASSERT_THAT(handle->ValidateAt(0), IsOk()); + KeysetHandle::Entry entry = (*handle)[0]; + + EXPECT_THAT(entry.GetId(), Eq(11)); + EXPECT_THAT(entry.GetStatus(), Eq(KeyStatus::kEnabled)); + EXPECT_THAT(entry.IsPrimary(), IsTrue()); + EXPECT_THAT(entry.GetKey()->GetIdRequirement(), Eq(11)); + EXPECT_THAT(entry.GetKey()->GetParameters().HasIdRequirement(), IsTrue()); +} + +TEST_F(KeysetHandleTest, GetEntryFromMultipleKeyKeyset) { + Keyset keyset; + Keyset::Key key; + AddRawKey("first_key_type", 11, key, KeyStatusType::DISABLED, + KeyData::SYMMETRIC, &keyset); + AddTinkKey("second_key_type", 22, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + AddRawKey("third_key_type", 33, key, KeyStatusType::DESTROYED, + KeyData::SYMMETRIC, &keyset); + keyset.set_primary_key_id(22); + + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + ASSERT_THAT(handle->Validate(), IsOk()); + ASSERT_THAT(*handle, SizeIs(3)); + + ASSERT_THAT(handle->ValidateAt(0), IsOk()); + KeysetHandle::Entry entry0 = (*handle)[0]; + EXPECT_THAT(entry0.GetId(), Eq(11)); + EXPECT_THAT(entry0.GetStatus(), Eq(KeyStatus::kDisabled)); + EXPECT_THAT(entry0.IsPrimary(), IsFalse()); + EXPECT_THAT(entry0.GetKey()->GetIdRequirement(), Eq(absl::nullopt)); + EXPECT_THAT(entry0.GetKey()->GetParameters().HasIdRequirement(), IsFalse()); + + ASSERT_THAT(handle->ValidateAt(1), IsOk()); + KeysetHandle::Entry entry1 = (*handle)[1]; + EXPECT_THAT(entry1.GetId(), Eq(22)); + EXPECT_THAT(entry1.GetStatus(), Eq(KeyStatus::kEnabled)); + EXPECT_THAT(entry1.IsPrimary(), IsTrue()); + EXPECT_THAT(entry1.GetKey()->GetIdRequirement(), Eq(22)); + EXPECT_THAT(entry1.GetKey()->GetParameters().HasIdRequirement(), IsTrue()); + + ASSERT_THAT(handle->ValidateAt(2), IsOk()); + KeysetHandle::Entry entry2 = (*handle)[2]; + EXPECT_THAT(entry2.GetId(), Eq(33)); + EXPECT_THAT(entry2.GetStatus(), Eq(KeyStatus::kDestroyed)); + EXPECT_THAT(entry2.IsPrimary(), IsFalse()); + EXPECT_THAT(entry2.GetKey()->GetIdRequirement(), Eq(absl::nullopt)); + EXPECT_THAT(entry2.GetKey()->GetParameters().HasIdRequirement(), IsFalse()); +} + +TEST_F(KeysetHandleDeathTest, EntryWithIndexOutOfBoundsCrashes) { + Keyset keyset; + Keyset::Key key; + AddTinkKey("first_key_type", 11, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + keyset.set_primary_key_id(11); + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + ASSERT_THAT(handle->Validate(), IsOk()); + ASSERT_THAT(*handle, SizeIs(1)); + + EXPECT_DEATH_IF_SUPPORTED((*handle)[-1], + "Invalid index -1 for keyset of size 1"); + EXPECT_DEATH_IF_SUPPORTED((*handle)[1], + "Invalid index 1 for keyset of size 1"); +} + +TEST_F(KeysetHandleDeathTest, EntryWithUnknownStatusFails) { + Keyset keyset; + Keyset::Key key; + AddTinkKey("first_key_type", 11, key, KeyStatusType::UNKNOWN_STATUS, + KeyData::SYMMETRIC, &keyset); + keyset.set_primary_key_id(11); + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + ASSERT_THAT(*handle, SizeIs(1)); + + EXPECT_THAT(handle->Validate(), StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(handle->ValidateAt(0), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_DEATH_IF_SUPPORTED((*handle)[0], "Invalid key status type."); +} + +TEST_F(KeysetHandleDeathTest, EntryWithUnprintableTypeUrlFails) { + Keyset keyset; + Keyset::Key key; + AddRawKey("invalid key type url with spaces", 11, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + keyset.set_primary_key_id(11); + + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + ASSERT_THAT(*handle, SizeIs(1)); + + EXPECT_THAT(handle->Validate(), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_THAT(handle->ValidateAt(0), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_DEATH_IF_SUPPORTED((*handle)[0], + "Non-printable ASCII character in type URL."); +} + +TEST_F(KeysetHandleTest, GetPrimary) { + Keyset keyset; + Keyset::Key key; + AddTinkKey("first_key_type", 11, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + AddTinkKey("first_key_type", 22, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + AddTinkKey("first_key_type", 33, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + keyset.set_primary_key_id(33); + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + ASSERT_THAT(handle->Validate(), IsOk()); + ASSERT_THAT(*handle, SizeIs(3)); + + util::StatusOr<KeysetHandle::Entry> primary = handle->GetPrimary(); + ASSERT_THAT(primary, IsOk()); + + EXPECT_THAT(primary->GetId(), Eq(33)); + EXPECT_THAT(primary->GetStatus(), Eq(KeyStatus::kEnabled)); + EXPECT_THAT(primary->IsPrimary(), IsTrue()); +} + +TEST_F(KeysetHandleDeathTest, NonexistentPrimaryFails) { + Keyset keyset; + Keyset::Key key; + AddTinkKey("first_key_type", 11, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + ASSERT_THAT(*handle, SizeIs(1)); + + EXPECT_THAT(handle->Validate(), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_DEATH_IF_SUPPORTED(handle->GetPrimary(), "Keyset has no primary"); +} + +TEST_F(KeysetHandleDeathTest, MultiplePrimariesFail) { + Keyset keyset; + Keyset::Key key; + AddTinkKey("first_key_type", 11, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + AddTinkKey("second_key_type", 11, key, KeyStatusType::ENABLED, + KeyData::SYMMETRIC, &keyset); + // Multiple primaries since two distinct keys share the same key id. + keyset.set_primary_key_id(11); + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + ASSERT_THAT(*handle, SizeIs(2)); + + EXPECT_THAT(handle->Validate(), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_DEATH_IF_SUPPORTED(handle->GetPrimary(), + "Keyset has more than one primary"); +} + +TEST_F(KeysetHandleDeathTest, GetDisabledPrimaryFails) { + Keyset keyset; + Keyset::Key key; + AddTinkKey("first_key_type", 11, key, KeyStatusType::DISABLED, + KeyData::SYMMETRIC, &keyset); + keyset.set_primary_key_id(11); + std::unique_ptr<KeysetHandle> handle = + TestKeysetHandle::GetKeysetHandle(keyset); + ASSERT_THAT(*handle, SizeIs(1)); + + EXPECT_THAT(handle->Validate(), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_DEATH_IF_SUPPORTED(handle->GetPrimary(), + "Keyset has primary that is not enabled"); +} + } // namespace } // namespace tink } // namespace crypto
diff --git a/cc/core/keyset_manager.cc b/cc/core/keyset_manager.cc index 6ca880a..947b3bd 100644 --- a/cc/core/keyset_manager.cc +++ b/cc/core/keyset_manager.cc
@@ -17,14 +17,13 @@ #include "tink/keyset_manager.h" #include <memory> -#include <random> #include <utility> #include "absl/memory/memory.h" #include "absl/status/status.h" +#include "tink/internal/key_gen_configuration_impl.h" +#include "tink/key_gen_configuration.h" #include "tink/keyset_handle.h" -#include "tink/keyset_reader.h" -#include "tink/registry.h" #include "tink/util/enums.h" #include "tink/util/errors.h" #include "tink/util/status.h" @@ -34,12 +33,12 @@ namespace crypto { namespace tink { +using ::crypto::tink::util::Enums; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; using google::crypto::tink::Keyset; using google::crypto::tink::KeyStatusType; using google::crypto::tink::KeyTemplate; -using crypto::tink::util::Enums; -using crypto::tink::util::Status; -using crypto::tink::util::StatusOr; // static StatusOr<std::unique_ptr<KeysetManager>> KeysetManager::New( @@ -71,17 +70,22 @@ return Add(key_template, false); } -crypto::tink::util::StatusOr<uint32_t> KeysetManager::Add( +StatusOr<uint32_t> KeysetManager::Add( const google::crypto::tink::KeyTemplate& key_template, bool as_primary) { + KeyGenConfiguration config; + Status status = + internal::KeyGenConfigurationImpl::SetGlobalRegistryMode(config); + if (!status.ok()) { + return status; + } absl::MutexLock lock(&keyset_mutex_); - return KeysetHandle::AddToKeyset(key_template, as_primary, &keyset_); + return KeysetHandle::AddToKeyset(key_template, as_primary, config, &keyset_); } StatusOr<uint32_t> KeysetManager::Rotate(const KeyTemplate& key_template) { return Add(key_template, true); } - Status KeysetManager::Enable(uint32_t key_id) { absl::MutexLock lock(&keyset_mutex_); for (auto& key : *(keyset_.mutable_key())) { @@ -129,8 +133,7 @@ "Cannot delete primary key (key_id %u).", key_id); } auto key_field = keyset_.mutable_key(); - for (auto key_iter = key_field->begin(); - key_iter != key_field->end(); + for (auto key_iter = key_field->begin(); key_iter != key_field->end(); key_iter++) { auto key = *key_iter; if (key.key_id() == key_id) { @@ -184,7 +187,6 @@ "No key with key_id %u found in the keyset.", key_id); } - int KeysetManager::KeyCount() const { absl::MutexLock lock(&keyset_mutex_); return keyset_.key_size();
diff --git a/cc/core/keyset_manager_test.cc b/cc/core/keyset_manager_test.cc index e60838e..230d660 100644 --- a/cc/core/keyset_manager_test.cc +++ b/cc/core/keyset_manager_test.cc
@@ -20,14 +20,11 @@ #include "gtest/gtest.h" #include "tink/aead/aead_config.h" #include "tink/aead/aes_gcm_key_manager.h" -#include "tink/config.h" #include "tink/keyset_handle.h" #include "tink/util/test_keyset_handle.h" #include "proto/aes_gcm.pb.h" #include "proto/tink.pb.h" -using crypto::tink::TestKeysetHandle; - using google::crypto::tink::AesGcmKeyFormat; using google::crypto::tink::KeyData; using google::crypto::tink::KeyStatusType;
diff --git a/cc/core/partial_key_access_token_test.cc b/cc/core/partial_key_access_token_test.cc new file mode 100644 index 0000000..41087e5 --- /dev/null +++ b/cc/core/partial_key_access_token_test.cc
@@ -0,0 +1,52 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include <utility> + +#include "tink/partial_key_access_token.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "tink/partial_key_access.h" + +namespace crypto { +namespace tink { +namespace { + +TEST(PartialKeyAccessTokenTest, CopyConstructor) { + PartialKeyAccessToken token = GetPartialKeyAccess(); + PartialKeyAccessToken copy ABSL_ATTRIBUTE_UNUSED(token); +} + +TEST(PartialKeyAccessTokenTest, CopyAssignment) { + PartialKeyAccessToken token = GetPartialKeyAccess(); + PartialKeyAccessToken copy ABSL_ATTRIBUTE_UNUSED = token; +} + +TEST(PartialKeyAccessTokenTest, MoveConstructor) { + PartialKeyAccessToken token = GetPartialKeyAccess(); + PartialKeyAccessToken move ABSL_ATTRIBUTE_UNUSED(std::move(token)); +} + +TEST(PartialKeyAccessTokenTest, MoveAssignment) { + PartialKeyAccessToken token = GetPartialKeyAccess(); + PartialKeyAccessToken move ABSL_ATTRIBUTE_UNUSED = std::move(token); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/core/primitive_set_test.cc b/cc/core/primitive_set_test.cc index 3f3ebdf..ea877bd 100644 --- a/cc/core/primitive_set_test.cc +++ b/cc/core/primitive_set_test.cc
@@ -24,7 +24,9 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "tink/cleartext_keyset_handle.h" #include "tink/crypto_format.h" +#include "tink/keyderivation/keyset_deriver.h" #include "tink/mac.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" @@ -32,20 +34,21 @@ using ::crypto::tink::test::DummyMac; using ::crypto::tink::test::IsOk; +using ::google::crypto::tink::Keyset; using ::google::crypto::tink::KeysetInfo; using ::google::crypto::tink::KeyStatusType; using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::SizeIs; using ::testing::UnorderedElementsAreArray; namespace crypto { namespace tink { namespace { -class PrimitiveSetTest : public ::testing::Test { -}; +class PrimitiveSetTest : public ::testing::Test {}; -void add_primitives(PrimitiveSet<Mac>* primitive_set, - int key_id_offset, +void add_primitives(PrimitiveSet<Mac>* primitive_set, int key_id_offset, int primitives_count) { for (int i = 0; i < primitives_count; i++) { int key_id = key_id_offset + i; @@ -59,8 +62,20 @@ } } -void access_primitives(PrimitiveSet<Mac>* primitive_set, - int key_id_offset, +void add_primitives(PrimitiveSet<Mac>::Builder* primitive_set_builder, + int key_id_offset, int primitives_count) { + for (int i = 0; i < primitives_count; i++) { + int key_id = key_id_offset + i; + KeysetInfo::KeyInfo key_info; + key_info.set_output_prefix_type(OutputPrefixType::TINK); + key_info.set_key_id(key_id); + key_info.set_status(KeyStatusType::ENABLED); + std::unique_ptr<Mac> mac(new DummyMac("dummy MAC")); + primitive_set_builder->AddPrimitive(std::move(mac), key_info); + } +} + +void access_primitives(PrimitiveSet<Mac>* primitive_set, int key_id_offset, int primitives_count) { for (int i = 0; i < primitives_count; i++) { int key_id = key_id_offset + i; @@ -76,17 +91,24 @@ } TEST_F(PrimitiveSetTest, ConcurrentOperations) { - PrimitiveSet<Mac> mac_set; + PrimitiveSet<Mac>::Builder mac_set_builder; int offset_a = 100; int offset_b = 150; int count = 100; // Add some primitives. - std::thread add_primitives_a(add_primitives, &mac_set, offset_a, count); - std::thread add_primitives_b(add_primitives, &mac_set, offset_b, count); + // See go/totw/133 on why we use a lambda here. + std::thread add_primitives_a( + [&]() { add_primitives(&mac_set_builder, offset_a, count); }); + std::thread add_primitives_b( + [&]() { add_primitives(&mac_set_builder, offset_b, count); }); add_primitives_a.join(); add_primitives_b.join(); + auto mac_set_result = std::move(mac_set_builder).Build(); + ASSERT_TRUE(mac_set_result.ok()) << mac_set_result.status(); + PrimitiveSet<Mac> mac_set = std::move(mac_set_result.value()); + // Access primitives. std::thread access_primitives_a(access_primitives, &mac_set, offset_a, count); std::thread access_primitives_b(access_primitives, &mac_set, offset_b, count); @@ -137,7 +159,7 @@ key_2.set_key_id(key_id_2); key_2.set_status(KeyStatusType::ENABLED); - uint32_t key_id_3 = key_id_2; // same id as key_2 + uint32_t key_id_3 = key_id_2; // same id as key_2 KeysetInfo::KeyInfo key_3; key_3.set_output_prefix_type(OutputPrefixType::TINK); key_3.set_key_id(key_id_3); @@ -155,7 +177,408 @@ key_5.set_key_id(key_id_5); key_5.set_status(KeyStatusType::ENABLED); - uint32_t key_id_6 = key_id_1; // same id as key_1 + uint32_t key_id_6 = key_id_1; // same id as key_1 + KeysetInfo::KeyInfo key_6; + key_6.set_output_prefix_type(OutputPrefixType::TINK); + key_6.set_key_id(key_id_6); + key_6.set_status(KeyStatusType::ENABLED); + + PrimitiveSet<Mac>::Builder primitive_set_builder; + + // Add all the primitives. + auto primitive_set_result = PrimitiveSet<Mac>::Builder{} + .AddPrimitive(std::move(mac_1), key_1) + .AddPrimitive(std::move(mac_2), key_2) + .AddPrimaryPrimitive(std::move(mac_3), key_3) + .AddPrimitive(std::move(mac_4), key_4) + .AddPrimitive(std::move(mac_5), key_5) + .AddPrimitive(std::move(mac_6), key_6) + .Build(); + + ASSERT_TRUE(primitive_set_result.ok()) << primitive_set_result.status(); + PrimitiveSet<Mac> primitive_set = std::move(primitive_set_result.value()); + + std::string data = "some data"; + + { // Check the primary. + auto primary = primitive_set.get_primary(); + EXPECT_FALSE(primary == nullptr); + EXPECT_EQ(KeyStatusType::ENABLED, primary->get_status()); + EXPECT_EQ(DummyMac(mac_name_3).ComputeMac(data).value(), + primary->get_primitive().ComputeMac(data).value()); + } + + { // Check raw primitives. + auto& primitives = *(primitive_set.get_raw_primitives().value()); + EXPECT_EQ(2, primitives.size()); + EXPECT_EQ(DummyMac(mac_name_4).ComputeMac(data).value(), + primitives[0]->get_primitive().ComputeMac(data).value()); + EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status()); + EXPECT_EQ(key_4.key_id(), primitives[0]->get_key_id()); + EXPECT_EQ(OutputPrefixType::RAW, primitives[0]->get_output_prefix_type()); + EXPECT_EQ(DummyMac(mac_name_5).ComputeMac(data).value(), + primitives[1]->get_primitive().ComputeMac(data).value()); + EXPECT_EQ(KeyStatusType::ENABLED, primitives[1]->get_status()); + EXPECT_EQ(key_5.key_id(), primitives[1]->get_key_id()); + EXPECT_EQ(OutputPrefixType::RAW, primitives[1]->get_output_prefix_type()); + } + + { // Check Tink primitives. + std::string prefix = CryptoFormat::GetOutputPrefix(key_1).value(); + auto& primitives = *(primitive_set.get_primitives(prefix).value()); + EXPECT_EQ(2, primitives.size()); + EXPECT_EQ(DummyMac(mac_name_1).ComputeMac(data).value(), + primitives[0]->get_primitive().ComputeMac(data).value()); + EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status()); + EXPECT_EQ(key_1.key_id(), primitives[0]->get_key_id()); + EXPECT_EQ(OutputPrefixType::TINK, primitives[0]->get_output_prefix_type()); + EXPECT_EQ(DummyMac(mac_name_6).ComputeMac(data).value(), + primitives[1]->get_primitive().ComputeMac(data).value()); + EXPECT_EQ(KeyStatusType::ENABLED, primitives[1]->get_status()); + EXPECT_EQ(key_1.key_id(), primitives[1]->get_key_id()); + EXPECT_EQ(OutputPrefixType::TINK, primitives[1]->get_output_prefix_type()); + } + + { // Check another Tink primitive. + std::string prefix = CryptoFormat::GetOutputPrefix(key_3).value(); + auto& primitives = *(primitive_set.get_primitives(prefix).value()); + EXPECT_EQ(1, primitives.size()); + EXPECT_EQ(DummyMac(mac_name_3).ComputeMac(data).value(), + primitives[0]->get_primitive().ComputeMac(data).value()); + EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status()); + EXPECT_EQ(key_3.key_id(), primitives[0]->get_key_id()); + EXPECT_EQ(OutputPrefixType::TINK, primitives[0]->get_output_prefix_type()); + } + + { // Check legacy primitive. + std::string prefix = CryptoFormat::GetOutputPrefix(key_2).value(); + auto& primitives = *(primitive_set.get_primitives(prefix).value()); + EXPECT_EQ(1, primitives.size()); + EXPECT_EQ(DummyMac(mac_name_2).ComputeMac(data).value(), + primitives[0]->get_primitive().ComputeMac(data).value()); + EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status()); + EXPECT_EQ(key_2.key_id(), primitives[0]->get_key_id()); + EXPECT_EQ(OutputPrefixType::LEGACY, + primitives[0]->get_output_prefix_type()); + } +} + +TEST_F(PrimitiveSetTest, PrimaryKeyWithIdCollisions) { + std::string mac_name_1 = "MAC#1"; + std::string mac_name_2 = "MAC#2"; + + uint32_t key_id_1 = 1234543; + KeysetInfo::KeyInfo key_info_1; + key_info_1.set_key_id(key_id_1); + key_info_1.set_status(KeyStatusType::ENABLED); + + uint32_t key_id_2 = key_id_1; // same id as key_2 + KeysetInfo::KeyInfo key_info_2; + key_info_2.set_key_id(key_id_2); + key_info_2.set_status(KeyStatusType::ENABLED); + + { // Test with RAW-keys. + std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1)); + std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2)); + key_info_1.set_output_prefix_type(OutputPrefixType::RAW); + key_info_2.set_output_prefix_type(OutputPrefixType::RAW); + PrimitiveSet<Mac>::Builder primitive_set_builder; + + // Add the first primitive, and set it as primary. + primitive_set_builder.AddPrimaryPrimitive(std::move(mac_1), key_info_1); + + auto primitive_set_result = std::move(primitive_set_builder).Build(); + ASSERT_TRUE(primitive_set_result.ok()) << primitive_set_result.status(); + PrimitiveSet<Mac> primitive_set = std::move(primitive_set_result.value()); + + std::string identifier = ""; + const auto& primitives = + *(primitive_set.get_primitives(identifier).value()); + EXPECT_EQ(1, primitives.size()); + EXPECT_EQ(primitive_set.get_primary(), primitives[0].get()); + } + + { // Test with TINK-keys. + std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1)); + std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2)); + key_info_1.set_output_prefix_type(OutputPrefixType::TINK); + key_info_2.set_output_prefix_type(OutputPrefixType::TINK); + PrimitiveSet<Mac>::Builder primitive_set_builder; + + // Add the first primitive, and set it as primary. + primitive_set_builder.AddPrimaryPrimitive(std::move(mac_1), key_info_1); + + auto primitive_set_result = std::move(primitive_set_builder).Build(); + ASSERT_TRUE(primitive_set_result.ok()) << primitive_set_result.status(); + PrimitiveSet<Mac> primitive_set = std::move(primitive_set_result.value()); + std::string identifier = CryptoFormat::GetOutputPrefix(key_info_1).value(); + const auto& primitives = + *(primitive_set.get_primitives(identifier).value()); + EXPECT_EQ(1, primitives.size()); + EXPECT_EQ(primitive_set.get_primary(), primitives[0].get()); + } + + { // Test with LEGACY-keys. + std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1)); + std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2)); + key_info_1.set_output_prefix_type(OutputPrefixType::LEGACY); + key_info_2.set_output_prefix_type(OutputPrefixType::LEGACY); + PrimitiveSet<Mac>::Builder primitive_set_builder; + + // Add the first primitive, and set it as primary. + primitive_set_builder.AddPrimaryPrimitive(std::move(mac_1), key_info_1); + + auto primitive_set_result = std::move(primitive_set_builder).Build(); + ASSERT_TRUE(primitive_set_result.ok()) << primitive_set_result.status(); + PrimitiveSet<Mac> primitive_set = std::move(primitive_set_result.value()); + std::string identifier = CryptoFormat::GetOutputPrefix(key_info_1).value(); + const auto& primitives = + *(primitive_set.get_primitives(identifier).value()); + EXPECT_EQ(1, primitives.size()); + EXPECT_EQ(primitive_set.get_primary(), primitives[0].get()); + } +} + +TEST_F(PrimitiveSetTest, DisabledKey) { + std::string mac_name_1 = "MAC#1"; + std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1)); + + uint32_t key_id_1 = 1234543; + KeysetInfo::KeyInfo key_info_1; + key_info_1.set_output_prefix_type(OutputPrefixType::TINK); + key_info_1.set_key_id(key_id_1); + key_info_1.set_status(KeyStatusType::DISABLED); + + // Add all the primitives. + auto add_primitive_result = PrimitiveSet<Mac>::Builder{} + .AddPrimitive(std::move(mac_1), key_info_1) + .Build(); + EXPECT_FALSE(add_primitive_result.ok()); +} + +KeysetInfo::KeyInfo CreateKey(uint32_t key_id, + OutputPrefixType output_prefix_type, + KeyStatusType key_status, + absl::string_view type_url) { + KeysetInfo::KeyInfo key_info; + key_info.set_output_prefix_type(output_prefix_type); + key_info.set_key_id(key_id); + key_info.set_status(key_status); + std::string type_url_str(type_url); + key_info.set_type_url(type_url_str); + return key_info; +} + +// Struct to hold MAC, Id and type_url. +struct MacIdAndTypeUrl { + std::string mac; + std::string id; + std::string type_url; +}; + +bool operator==(const MacIdAndTypeUrl& first, const MacIdAndTypeUrl& other) { + return first.mac == other.mac && first.id == other.id && + first.type_url == other.type_url; +} + +TEST_F(PrimitiveSetTest, GetAll) { + auto pset_result = + PrimitiveSet<Mac>::Builder{} + .AddPrimitive( + absl::make_unique<DummyMac>("MAC1"), + CreateKey(0x01010101, OutputPrefixType::TINK, + KeyStatusType::ENABLED, /*type_url=*/ + "type.googleapis.com/google.crypto.tink.HmacKey")) + .AddPrimitive( + absl::make_unique<DummyMac>("MAC2"), + CreateKey(0x02020202, OutputPrefixType::TINK, + KeyStatusType::ENABLED, /*type_url=*/ + "type.googleapis.com/google.crypto.tink.HmacKey")) + // Add primitive and make it primary. + .AddPrimaryPrimitive( + absl::make_unique<DummyMac>("MAC3"), + CreateKey(0x02020202, OutputPrefixType::TINK, + KeyStatusType::ENABLED, /*type_url=*/ + "type.googleapis.com/google.crypto.tink.AesCmacKey")) + .AddPrimitive( + absl::make_unique<DummyMac>("MAC4"), + CreateKey(0x02020202, OutputPrefixType::RAW, + KeyStatusType::ENABLED, /*type_url=*/ + "type.googleapis.com/google.crypto.tink.AesCmacKey")) + .AddPrimitive( + absl::make_unique<DummyMac>("MAC5"), + CreateKey(0x01010101, OutputPrefixType::TINK, + KeyStatusType::ENABLED, /*type_url=*/ + "type.googleapis.com/google.crypto.tink.AesCmacKey")) + .Build(); + + ASSERT_TRUE(pset_result.ok()) << pset_result.status(); + PrimitiveSet<Mac> pset = std::move(pset_result.value()); + + std::vector<MacIdAndTypeUrl> mac_id_and_type; + for (auto* entry : pset.get_all()) { + auto mac_or = entry->get_primitive().ComputeMac(""); + ASSERT_THAT(mac_or, IsOk()); + mac_id_and_type.push_back({mac_or.value(), entry->get_identifier(), + std::string(entry->get_key_type_url())}); + } + + // In the following id part, the first byte is 1 for Tink. + std::vector<MacIdAndTypeUrl> expected_result = { + {/*mac=*/"13:0:DummyMac:MAC1", /*id=*/absl::StrCat("\1\1\1\1\1"), + /*type_url=*/"type.googleapis.com/google.crypto.tink.HmacKey"}, + {/*mac=*/"13:0:DummyMac:MAC2", /*id=*/absl::StrCat("\1\2\2\2\2"), + /*type_url=*/"type.googleapis.com/google.crypto.tink.HmacKey"}, + {/*mac=*/"13:0:DummyMac:MAC3", /*id=*/absl::StrCat("\1\2\2\2\2"), + /*type_url=*/"type.googleapis.com/google.crypto.tink.AesCmacKey"}, + {/*mac=*/"13:0:DummyMac:MAC4", /*id=*/"", + /*type_url=*/"type.googleapis.com/google.crypto.tink.AesCmacKey"}, + {/*mac=*/"13:0:DummyMac:MAC5", /*id=*/absl::StrCat("\1\1\1\1\1"), + /*type_url=*/"type.googleapis.com/google.crypto.tink.AesCmacKey"}}; + + EXPECT_THAT(mac_id_and_type, UnorderedElementsAreArray(expected_result)); +} + +class FakeDeriver : public KeysetDeriver { + public: + explicit FakeDeriver() = default; + crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> DeriveKeyset( + absl::string_view salt) const override { + Keyset keyset; + return CleartextKeysetHandle::GetKeysetHandle(keyset); + } +}; + +TEST_F(PrimitiveSetTest, GetAllInKeysetOrder) { + auto pset = absl::make_unique<PrimitiveSet<KeysetDeriver>>(); + std::vector<KeysetInfo::KeyInfo> key_infos; + + KeysetInfo::KeyInfo key_info; + key_info.set_key_id(1010101); + key_info.set_status(KeyStatusType::ENABLED); + key_info.set_output_prefix_type(OutputPrefixType::RAW); + key_info.set_type_url( + "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey"); + ASSERT_THAT(pset->AddPrimitive(absl::make_unique<FakeDeriver>(), key_info), + IsOk()); + key_infos.push_back(key_info); + + key_info.set_key_id(2020202); + key_info.set_status(KeyStatusType::ENABLED); + key_info.set_output_prefix_type(OutputPrefixType::LEGACY); + key_info.set_type_url( + "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey"); + ASSERT_THAT(pset->AddPrimitive(absl::make_unique<FakeDeriver>(), key_info), + IsOk()); + key_infos.push_back(key_info); + + key_info.set_key_id(3030303); + key_info.set_status(KeyStatusType::ENABLED); + key_info.set_output_prefix_type(OutputPrefixType::TINK); + key_info.set_type_url( + "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey"); + ASSERT_THAT(pset->AddPrimitive(absl::make_unique<FakeDeriver>(), key_info), + IsOk()); + key_infos.push_back(key_info); + + std::vector<PrimitiveSet<KeysetDeriver>::Entry<KeysetDeriver>*> entries = + pset->get_all_in_keyset_order(); + ASSERT_THAT(entries, SizeIs(key_infos.size())); + + for (int i = 0; i < entries.size(); i++) { + EXPECT_THAT(entries[i]->get_identifier(), + Eq(*CryptoFormat::GetOutputPrefix(key_infos[i]))); + EXPECT_THAT(entries[i]->get_status(), Eq(KeyStatusType::ENABLED)); + EXPECT_THAT(entries[i]->get_key_id(), Eq(key_infos[i].key_id())); + EXPECT_THAT(entries[i]->get_output_prefix_type(), + Eq(key_infos[i].output_prefix_type())); + EXPECT_THAT(entries[i]->get_key_type_url(), Eq(key_infos[i].type_url())); + } +} + +TEST_F(PrimitiveSetTest, LegacyConcurrentOperations) { + PrimitiveSet<Mac> mac_set; + int offset_a = 100; + int offset_b = 150; + int count = 100; + + // Add some primitives. + std::thread add_primitives_a( + [&]() { add_primitives(&mac_set, offset_a, count); }); + std::thread add_primitives_b( + [&]() { add_primitives(&mac_set, offset_b, count); }); + add_primitives_a.join(); + add_primitives_b.join(); + + // Access primitives. + std::thread access_primitives_a(access_primitives, &mac_set, offset_a, count); + std::thread access_primitives_b(access_primitives, &mac_set, offset_b, count); + access_primitives_a.join(); + access_primitives_b.join(); + + // Verify the common key ids added by both threads. + for (int key_id = offset_a; key_id < offset_b + count; key_id++) { + KeysetInfo::KeyInfo key_info; + key_info.set_output_prefix_type(OutputPrefixType::TINK); + key_info.set_key_id(key_id); + key_info.set_status(KeyStatusType::ENABLED); + std::string prefix = CryptoFormat::GetOutputPrefix(key_info).value(); + auto get_result = mac_set.get_primitives(prefix); + EXPECT_TRUE(get_result.ok()) << get_result.status(); + auto macs = get_result.value(); + if (key_id >= offset_b && key_id < offset_a + count) { + EXPECT_EQ(2, macs->size()); // overlapping key_id range + } else { + EXPECT_EQ(1, macs->size()); + } + } +} + +TEST_F(PrimitiveSetTest, LegacyBasic) { + std::string mac_name_1 = "MAC#1"; + std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1)); + std::string mac_name_2 = "MAC#2"; + std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2)); + std::string mac_name_3 = "MAC#3"; + std::unique_ptr<Mac> mac_3(new DummyMac(mac_name_3)); + std::string mac_name_4 = "MAC#3"; + std::unique_ptr<Mac> mac_4(new DummyMac(mac_name_4)); + std::string mac_name_5 = "MAC#3"; + std::unique_ptr<Mac> mac_5(new DummyMac(mac_name_5)); + std::string mac_name_6 = "MAC#3"; + std::unique_ptr<Mac> mac_6(new DummyMac(mac_name_6)); + + uint32_t key_id_1 = 1234543; + KeysetInfo::KeyInfo key_1; + key_1.set_output_prefix_type(OutputPrefixType::TINK); + key_1.set_key_id(key_id_1); + key_1.set_status(KeyStatusType::ENABLED); + + uint32_t key_id_2 = 7213743; + KeysetInfo::KeyInfo key_2; + key_2.set_output_prefix_type(OutputPrefixType::LEGACY); + key_2.set_key_id(key_id_2); + key_2.set_status(KeyStatusType::ENABLED); + + uint32_t key_id_3 = key_id_2; // same id as key_2 + KeysetInfo::KeyInfo key_3; + key_3.set_output_prefix_type(OutputPrefixType::TINK); + key_3.set_key_id(key_id_3); + key_3.set_status(KeyStatusType::ENABLED); + + uint32_t key_id_4 = 947327; + KeysetInfo::KeyInfo key_4; + key_4.set_output_prefix_type(OutputPrefixType::RAW); + key_4.set_key_id(key_id_4); + key_4.set_status(KeyStatusType::ENABLED); + + uint32_t key_id_5 = 529472; + KeysetInfo::KeyInfo key_5; + key_5.set_output_prefix_type(OutputPrefixType::RAW); + key_5.set_key_id(key_id_5); + key_5.set_status(KeyStatusType::ENABLED); + + uint32_t key_id_6 = key_id_1; // same id as key_1 KeysetInfo::KeyInfo key_6; key_6.set_output_prefix_type(OutputPrefixType::TINK); key_6.set_key_id(key_id_6); @@ -260,7 +683,7 @@ } } -TEST_F(PrimitiveSetTest, PrimaryKeyWithIdCollisions) { +TEST_F(PrimitiveSetTest, LegacyPrimaryKeyWithIdCollisions) { std::string mac_name_1 = "MAC#1"; std::string mac_name_2 = "MAC#2"; @@ -269,7 +692,7 @@ key_info_1.set_key_id(key_id_1); key_info_1.set_status(KeyStatusType::ENABLED); - uint32_t key_id_2 = key_id_1; // same id as key_2 + uint32_t key_id_2 = key_id_1; // same id as key_2 KeysetInfo::KeyInfo key_info_2; key_info_2.set_key_id(key_id_2); key_info_2.set_status(KeyStatusType::ENABLED); @@ -362,7 +785,7 @@ } } -TEST_F(PrimitiveSetTest, DisabledKey) { +TEST_F(PrimitiveSetTest, LegacyDisabledKey) { std::string mac_name_1 = "MAC#1"; std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1)); @@ -379,32 +802,7 @@ EXPECT_FALSE(add_primitive_result.ok()); } -KeysetInfo::KeyInfo CreateKey(uint32_t key_id, - OutputPrefixType output_prefix_type, - KeyStatusType key_status, - absl::string_view type_url) { - KeysetInfo::KeyInfo key_info; - key_info.set_output_prefix_type(output_prefix_type); - key_info.set_key_id(key_id); - key_info.set_status(key_status); - std::string type_url_str(type_url); - key_info.set_type_url(type_url_str); - return key_info; -} - -// Struct to hold MAC, Id and type_url. -struct MacIdAndTypeUrl { - std::string mac; - std::string id; - std::string type_url; -}; - -bool operator==(const MacIdAndTypeUrl& first, const MacIdAndTypeUrl& other) { - return first.mac == other.mac && first.id == other.id && - first.type_url == other.type_url; -} - -TEST_F(PrimitiveSetTest, GetAll) { +TEST_F(PrimitiveSetTest, LegacyGetAll) { PrimitiveSet<Mac> pset; EXPECT_THAT( pset.AddPrimitive(
diff --git a/cc/core/private_key_manager_impl.h b/cc/core/private_key_manager_impl.h index 5d2d6ae..63a3e93 100644 --- a/cc/core/private_key_manager_impl.h +++ b/cc/core/private_key_manager_impl.h
@@ -16,6 +16,7 @@ #ifndef TINK_CORE_PRIVATE_KEY_MANAGER_IMPL_H_ #define TINK_CORE_PRIVATE_KEY_MANAGER_IMPL_H_ +#include <memory> #include <string> #include <utility>
diff --git a/cc/core/private_key_manager_impl_test.cc b/cc/core/private_key_manager_impl_test.cc index 512531e..a874884 100644 --- a/cc/core/private_key_manager_impl_test.cc +++ b/cc/core/private_key_manager_impl_test.cc
@@ -43,17 +43,22 @@ using ::google::crypto::tink::EcdsaPrivateKey; using ::google::crypto::tink::EcdsaPublicKey; using ::google::crypto::tink::EcdsaSignatureEncoding; -using ::google::crypto::tink::KeyData; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::Return; +} // namespace + // Placeholders for the primitives. We don't really want to test anything with // these except that things compile and List<PrivatePrimitive> is never confused // with List<PublicPrimitive> in private_key_manager_impl. +// NOTE: These are outside of the anonymous namespace to allow compiling with +// MSVC. class PrivatePrimitive {}; class PublicPrimitive {}; +namespace { + class ExamplePrivateKeyTypeManager : public PrivateKeyTypeManager<EcdsaPrivateKey, EcdsaKeyFormat, EcdsaPublicKey, List<PrivatePrimitive>> {
diff --git a/cc/core/restricted_data.cc b/cc/core/restricted_data.cc new file mode 100644 index 0000000..4f66030 --- /dev/null +++ b/cc/core/restricted_data.cc
@@ -0,0 +1,45 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/restricted_data.h" + +#include <iostream> + +#include "absl/log/check.h" +#include "openssl/crypto.h" +#include "tink/subtle/random.h" +#include "tink/util/secret_data.h" + +namespace crypto { +namespace tink { + +RestrictedData::RestrictedData(int64_t num_random_bytes) { + CHECK_GE(num_random_bytes, 0) + << "Cannot generate a negative number of random bytes.\n"; + secret_ = util::SecretDataFromStringView( + subtle::Random::GetRandomBytes(num_random_bytes)); +} + +bool RestrictedData::operator==(const RestrictedData& other) const { + if (secret_.size() != other.secret_.size()) { + return false; + } + return CRYPTO_memcmp(secret_.data(), other.secret_.data(), secret_.size()) == + 0; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/core/restricted_data_test.cc b/cc/core/restricted_data_test.cc new file mode 100644 index 0000000..4012563 --- /dev/null +++ b/cc/core/restricted_data_test.cc
@@ -0,0 +1,117 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/restricted_data.h" + +#include <string> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/subtle/random.h" +#include "tink/util/secret_data.h" + +namespace crypto { +namespace tink { + +using ::crypto::tink::subtle::Random; +using ::testing::Eq; +using ::testing::SizeIs; + +TEST(RestrictedDataTest, CreateAndGetSecret) { + const std::string secret = Random::GetRandomBytes(32); + RestrictedData data(secret, InsecureSecretKeyAccess::Get()); + + EXPECT_THAT(data.GetSecret(InsecureSecretKeyAccess::Get()), Eq(secret)); +} + +TEST(RestrictedDataTest, GenerateRandomAndSize) { + RestrictedData data(/*num_random_bytes=*/32); + + EXPECT_THAT(data.GetSecret(InsecureSecretKeyAccess::Get()), SizeIs(32)); + EXPECT_THAT(data.size(), Eq(32)); +} + +TEST(RestrictedDataTest, GenerateRandomNegative) { + EXPECT_DEATH_IF_SUPPORTED( + RestrictedData(/*num_random_bytes=*/-1), + "Cannot generate a negative number of random bytes.\n"); +} + +TEST(RestrictedDataTest, Equals) { + const std::string secret = Random::GetRandomBytes(32); + RestrictedData data(secret, InsecureSecretKeyAccess::Get()); + RestrictedData same_data(secret, InsecureSecretKeyAccess::Get()); + + EXPECT_TRUE(data == same_data); + EXPECT_TRUE(same_data == data); + EXPECT_FALSE(data != same_data); + EXPECT_FALSE(same_data != data); +} + +TEST(RestrictedDataTest, NotEquals) { + RestrictedData data( + util::SecretDataAsStringView(Random::GetRandomKeyBytes(32)), + InsecureSecretKeyAccess::Get()); + RestrictedData diff_data( + util::SecretDataAsStringView(Random::GetRandomKeyBytes(32)), + InsecureSecretKeyAccess::Get()); + + EXPECT_TRUE(data != diff_data); + EXPECT_TRUE(diff_data != data); + EXPECT_FALSE(data == diff_data); + EXPECT_FALSE(diff_data == data); +} + +TEST(RestrictedDataTest, CopyConstructor) { + RestrictedData data(/*num_random_bytes=*/32); + RestrictedData copy(data); + + EXPECT_THAT(copy, SizeIs(32)); + EXPECT_THAT(copy.GetSecret(InsecureSecretKeyAccess::Get()), + Eq(data.GetSecret(InsecureSecretKeyAccess::Get()))); +} + +TEST(RestrictedDataTest, CopyAssignment) { + RestrictedData data(/*num_random_bytes=*/32); + RestrictedData copy = data; + + EXPECT_THAT(copy, SizeIs(32)); + EXPECT_THAT(copy.GetSecret(InsecureSecretKeyAccess::Get()), + Eq(copy.GetSecret(InsecureSecretKeyAccess::Get()))); +} + +TEST(RestrictedDataTest, MoveConstructor) { + const std::string secret = Random::GetRandomBytes(32); + RestrictedData data(secret, InsecureSecretKeyAccess::Get()); + RestrictedData move(std::move(data)); + + EXPECT_THAT(move, SizeIs(32)); + EXPECT_THAT(move.GetSecret(InsecureSecretKeyAccess::Get()), Eq(secret)); +} + +TEST(RestrictedDataTest, MoveAssignment) { + const std::string secret = Random::GetRandomBytes(32); + RestrictedData data(secret, InsecureSecretKeyAccess::Get()); + RestrictedData move = std::move(data); + + EXPECT_THAT(move, SizeIs(32)); + EXPECT_THAT(move.GetSecret(InsecureSecretKeyAccess::Get()), Eq(secret)); +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/core/version_test.cc b/cc/core/version_test.cc index d25babd..3a4bac5 100644 --- a/cc/core/version_test.cc +++ b/cc/core/version_test.cc
@@ -20,12 +20,16 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "tink/internal/util.h" namespace crypto { namespace tink { namespace { -TEST(VersionTest, testVersionFormat) { +using ::testing::AnyOf; +using ::testing::MatchesRegex; + +TEST(VersionTest, VersionHasCorrectFormat) { // The regex represents Semantic Versioning syntax (www.semver.org), // i.e. three dot-separated numbers, with an optional suffix // that starts with a hyphen, to cover alpha/beta releases and @@ -33,8 +37,16 @@ // 1.2.3 // 1.2.3-beta // 1.2.3-RC1 - std::string version_regex = "[0-9]+[.][0-9]+[.][0-9]+(-[A-Za-z0-9]+)?"; - EXPECT_THAT(Version::kTinkVersion, testing::MatchesRegex(version_regex)); + if (crypto::tink::internal::IsWindows()) { + // Using the syntax in + // https://github.com/google/googletest/blob/main/docs/advanced.md#regular-expression-syntax. + EXPECT_THAT(Version::kTinkVersion, + AnyOf(MatchesRegex(R"regex(\d+\.\d+\.\d+)regex"), + MatchesRegex(R"regex(\d+\.\d+\.\d+-\w+)regex"))); + } else { + std::string version_regex = "[0-9]+[.][0-9]+[.][0-9]+(-[A-Za-z0-9]+)?"; + EXPECT_THAT(Version::kTinkVersion, testing::MatchesRegex(version_regex)); + } } } // namespace
diff --git a/cc/daead/BUILD.bazel b/cc/daead/BUILD.bazel index 2a4c933..e6bb7b0 100644 --- a/cc/daead/BUILD.bazel +++ b/cc/daead/BUILD.bazel
@@ -56,9 +56,9 @@ visibility = ["//visibility:public"], deps = [ ":aes_siv_key_manager", + ":aes_siv_proto_serialization", ":deterministic_aead_wrapper", "//:registry", - "//config:config_util", "//config:tink_fips", "//proto:config_cc_proto", "//util:status", @@ -97,6 +97,91 @@ ], ) +cc_library( + name = "failing_daead", + srcs = ["failing_daead.cc"], + hdrs = ["failing_daead.h"], + include_prefix = "tink/daead", + deps = [ + "//:deterministic_aead", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "deterministic_aead_parameters", + hdrs = ["deterministic_aead_parameters.h"], + include_prefix = "tink/daead", + deps = ["//:parameters"], +) + +cc_library( + name = "deterministic_aead_key", + hdrs = ["deterministic_aead_key.h"], + include_prefix = "tink/daead", + deps = [ + ":deterministic_aead_parameters", + "//:key", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "aes_siv_parameters", + srcs = ["aes_siv_parameters.cc"], + hdrs = ["aes_siv_parameters.h"], + include_prefix = "tink/daead", + deps = [ + ":deterministic_aead_parameters", + "//util:statusor", + ], +) + +cc_library( + name = "aes_siv_key", + srcs = ["aes_siv_key.cc"], + hdrs = ["aes_siv_key.h"], + include_prefix = "tink/daead", + deps = [ + ":aes_siv_parameters", + ":deterministic_aead_key", + "//:partial_key_access_token", + "//:restricted_data", + "//subtle:subtle_util", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + ], +) + +cc_library( + name = "aes_siv_proto_serialization", + srcs = ["aes_siv_proto_serialization.cc"], + hdrs = ["aes_siv_proto_serialization.h"], + include_prefix = "tink/daead", + deps = [ + ":aes_siv_key", + ":aes_siv_parameters", + "//:partial_key_access", + "//:restricted_data", + "//:secret_key_access_token", + "//internal:key_parser", + "//internal:key_serializer", + "//internal:mutable_serialization_registry", + "//internal:parameters_parser", + "//internal:parameters_serializer", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:aes_siv_cc_proto", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + ], +) + # tests cc_test( @@ -143,14 +228,20 @@ srcs = ["deterministic_aead_config_test.cc"], tags = ["fips"], deps = [ + ":aes_siv_key", ":aes_siv_key_manager", + ":aes_siv_parameters", ":deterministic_aead_config", ":deterministic_aead_key_templates", - "//:config", "//:deterministic_aead", + "//:insecure_secret_key_access", "//:keyset_handle", + "//:partial_key_access", "//:registry", "//config:tink_fips", + "//internal:mutable_serialization_registry", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", "//util:status", "//util:test_matchers", "//util:test_util", @@ -195,17 +286,6 @@ ], ) -cc_library( - name = "failing_daead", - srcs = ["failing_daead.cc"], - hdrs = ["failing_daead.h"], - include_prefix = "tink/daead", - deps = [ - "//:deterministic_aead", - "@com_google_absl//absl/strings", - ], -) - cc_test( name = "failing_daead_test", srcs = ["failing_daead_test.cc"], @@ -216,3 +296,51 @@ "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "aes_siv_parameters_test", + srcs = ["aes_siv_parameters_test.cc"], + deps = [ + ":aes_siv_parameters", + "//util:statusor", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "aes_siv_key_test", + srcs = ["aes_siv_key_test.cc"], + deps = [ + ":aes_siv_key", + ":aes_siv_parameters", + "//:partial_key_access", + "//:restricted_data", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "aes_siv_proto_serialization_test", + size = "small", + srcs = ["aes_siv_proto_serialization_test.cc"], + deps = [ + ":aes_siv_key", + ":aes_siv_parameters", + ":aes_siv_proto_serialization", + "//:insecure_secret_key_access", + "//:partial_key_access", + "//:restricted_data", + "//internal:mutable_serialization_registry", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:aes_siv_cc_proto", + "//proto:tink_cc_proto", + "//subtle:random", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/cc/daead/CMakeLists.txt b/cc/daead/CMakeLists.txt index 4876217..95a2b78 100644 --- a/cc/daead/CMakeLists.txt +++ b/cc/daead/CMakeLists.txt
@@ -53,11 +53,11 @@ deterministic_aead_config.h DEPS tink::daead::aes_siv_key_manager + tink::daead::aes_siv_proto_serialization tink::daead::deterministic_aead_wrapper absl::core_headers absl::memory tink::core::registry - tink::config::config_util tink::config::tink_fips tink::util::status tink::proto::config_cc_proto @@ -90,6 +90,87 @@ tink::proto::tink_cc_proto ) +tink_cc_library( + NAME failing_daead + SRCS + failing_daead.cc + failing_daead.h + DEPS + absl::strings + tink::core::deterministic_aead +) + +tink_cc_library( + NAME deterministic_aead_parameters + SRCS + deterministic_aead_parameters.h + DEPS + tink::core::parameters +) + +tink_cc_library( + NAME deterministic_aead_key + SRCS + deterministic_aead_key.h + DEPS + tink::daead::deterministic_aead_parameters + absl::strings + tink::core::key +) + +tink_cc_library( + NAME aes_siv_parameters + SRCS + aes_siv_parameters.cc + aes_siv_parameters.h + DEPS + tink::daead::deterministic_aead_parameters + tink::util::statusor +) + +tink_cc_library( + NAME aes_siv_key + SRCS + aes_siv_key.cc + aes_siv_key.h + DEPS + tink::daead::aes_siv_parameters + tink::daead::deterministic_aead_key + absl::strings + absl::optional + tink::core::partial_key_access_token + tink::core::restricted_data + tink::subtle::subtle_util + tink::util::status + tink::util::statusor +) + +tink_cc_library( + NAME aes_siv_proto_serialization + SRCS + aes_siv_proto_serialization.cc + aes_siv_proto_serialization.h + DEPS + tink::daead::aes_siv_key + tink::daead::aes_siv_parameters + absl::status + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::core::secret_key_access_token + tink::internal::key_parser + tink::internal::key_serializer + tink::internal::mutable_serialization_registry + tink::internal::parameters_parser + tink::internal::parameters_serializer + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::util::status + tink::util::statusor + tink::proto::aes_siv_cc_proto + tink::proto::tink_cc_proto +) + # tests tink_cc_test( @@ -133,16 +214,22 @@ SRCS deterministic_aead_config_test.cc DEPS + tink::daead::aes_siv_key tink::daead::aes_siv_key_manager + tink::daead::aes_siv_parameters tink::daead::deterministic_aead_config tink::daead::deterministic_aead_key_templates gmock absl::status - tink::core::config tink::core::deterministic_aead + tink::core::insecure_secret_key_access tink::core::keyset_handle + tink::core::partial_key_access tink::core::registry tink::config::tink_fips + tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization tink::util::status tink::util::test_matchers tink::util::test_util @@ -182,16 +269,6 @@ tink::proto::tink_cc_proto ) -tink_cc_library( - NAME failing_daead - SRCS - failing_daead.cc - failing_daead.h - DEPS - absl::strings - tink::core::deterministic_aead -) - tink_cc_test( NAME failing_daead_test SRCS @@ -202,3 +279,50 @@ absl::status tink::util::test_matchers ) + +tink_cc_test( + NAME aes_siv_parameters_test + SRCS + aes_siv_parameters_test.cc + DEPS + tink::daead::aes_siv_parameters + gmock + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_test( + NAME aes_siv_key_test + SRCS + aes_siv_key_test.cc + DEPS + tink::daead::aes_siv_key + tink::daead::aes_siv_parameters + gmock + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_test( + NAME aes_siv_proto_serialization_test + SRCS + aes_siv_proto_serialization_test.cc + DEPS + tink::daead::aes_siv_key + tink::daead::aes_siv_parameters + tink::daead::aes_siv_proto_serialization + gmock + tink::core::insecure_secret_key_access + tink::core::partial_key_access + tink::core::restricted_data + tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::subtle::random + tink::util::test_matchers + tink::proto::aes_siv_cc_proto + tink::proto::tink_cc_proto +)
diff --git a/cc/daead/aes_siv_key.cc b/cc/daead/aes_siv_key.cc new file mode 100644 index 0000000..00582b2 --- /dev/null +++ b/cc/daead/aes_siv_key.cc
@@ -0,0 +1,107 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/daead/aes_siv_key.h" + +#include <string> + +#include "absl/strings/escaping.h" +#include "absl/types/optional.h" +#include "tink/daead/aes_siv_parameters.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/subtle/subtle_util.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace { + +util::StatusOr<std::string> ComputeOutputPrefix( + const AesSivParameters& parameters, absl::optional<int> id_requirement) { + switch (parameters.GetVariant()) { + case AesSivParameters::Variant::kNoPrefix: + return std::string(""); // Empty prefix. + case AesSivParameters::Variant::kCrunchy: + if (!id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "id requirement must have value with kCrunchy or kLegacy"); + } + return absl::StrCat(absl::HexStringToBytes("00"), + subtle::BigEndian32(*id_requirement)); + case AesSivParameters::Variant::kTink: + if (!id_requirement.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "id requirement must have value with kTink"); + } + return absl::StrCat(absl::HexStringToBytes("01"), + subtle::BigEndian32(*id_requirement)); + default: + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Invalid variant: ", parameters.GetVariant())); + } +} + +} // namespace + +util::StatusOr<AesSivKey> AesSivKey::Create(const AesSivParameters& parameters, + const RestrictedData& key_bytes, + absl::optional<int> id_requirement, + PartialKeyAccessToken token) { + if (parameters.KeySizeInBytes() != key_bytes.size()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Key size does not match AES-SIV parameters"); + } + if (parameters.HasIdRequirement() && !id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key without ID requirement with parameters with ID " + "requirement"); + } + if (!parameters.HasIdRequirement() && id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key with ID requirement with parameters without ID " + "requirement"); + } + util::StatusOr<std::string> output_prefix = + ComputeOutputPrefix(parameters, id_requirement); + if (!output_prefix.ok()) { + return output_prefix.status(); + } + return AesSivKey(parameters, key_bytes, id_requirement, + *std::move(output_prefix)); +} + +bool AesSivKey::operator==(const Key& other) const { + const AesSivKey* that = dynamic_cast<const AesSivKey*>(&other); + if (that == nullptr) { + return false; + } + if (GetParameters() != that->GetParameters()) { + return false; + } + if (id_requirement_ != that->id_requirement_) { + return false; + } + return key_bytes_ == that->key_bytes_; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/daead/aes_siv_key.h b/cc/daead/aes_siv_key.h new file mode 100644 index 0000000..8ca060d --- /dev/null +++ b/cc/daead/aes_siv_key.h
@@ -0,0 +1,83 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_DAEAD_AES_SIV_KEY_H_ +#define TINK_DAEAD_AES_SIV_KEY_H_ + +#include <string> +#include <utility> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/daead/aes_siv_parameters.h" +#include "tink/daead/deterministic_aead_key.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +// Represents a Deterministic AEAD that uses AES-SIV. +class AesSivKey : public DeterministicAeadKey { + public: + // Copyable and movable. + AesSivKey(const AesSivKey& other) = default; + AesSivKey& operator=(const AesSivKey& other) = default; + AesSivKey(AesSivKey&& other) = default; + AesSivKey& operator=(AesSivKey&& other) = default; + + // Creates a new AES-SIV key. If the parameters specify a variant that uses + // a prefix, then the id is used to compute this prefix. + static util::StatusOr<AesSivKey> Create(const AesSivParameters& parameters, + const RestrictedData& key_bytes, + absl::optional<int> id_requirement, + PartialKeyAccessToken token); + + // Returns the underlying AES-SIV key. + util::StatusOr<RestrictedData> GetKeyBytes( + PartialKeyAccessToken token) const { + return key_bytes_; + } + + absl::string_view GetOutputPrefix() const override { return output_prefix_; } + + const AesSivParameters& GetParameters() const override { return parameters_; } + + absl::optional<int> GetIdRequirement() const override { + return id_requirement_; + } + + bool operator==(const Key& other) const override; + + private: + AesSivKey(const AesSivParameters& parameters, const RestrictedData& key_bytes, + absl::optional<int> id_requirement, std::string output_prefix) + : parameters_(parameters), + key_bytes_(key_bytes), + id_requirement_(id_requirement), + output_prefix_(std::move(output_prefix)) {} + + AesSivParameters parameters_; + RestrictedData key_bytes_; + absl::optional<int> id_requirement_; + std::string output_prefix_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_DAEAD_AES_SIV_KEY_H_
diff --git a/cc/daead/aes_siv_key_manager.h b/cc/daead/aes_siv_key_manager.h index 4d2429c..29aa509 100644 --- a/cc/daead/aes_siv_key_manager.h +++ b/cc/daead/aes_siv_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_DAEAD_AES_SIV_KEY_MANAGER_H_ #define TINK_DAEAD_AES_SIV_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/daead/aes_siv_key_manager_test.cc b/cc/daead/aes_siv_key_manager_test.cc index f00daec..aebb705 100644 --- a/cc/daead/aes_siv_key_manager_test.cc +++ b/cc/daead/aes_siv_key_manager_test.cc
@@ -16,6 +16,8 @@ #include "tink/daead/aes_siv_key_manager.h" +#include <sstream> + #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/status/status.h"
diff --git a/cc/daead/aes_siv_key_test.cc b/cc/daead/aes_siv_key_test.cc new file mode 100644 index 0000000..294525e --- /dev/null +++ b/cc/daead/aes_siv_key_test.cc
@@ -0,0 +1,230 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/daead/aes_siv_key.h" + +#include <string> +#include <tuple> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/types/optional.h" +#include "tink/daead/aes_siv_parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + AesSivParameters::Variant variant; + absl::optional<int> id_requirement; + std::string output_prefix; +}; + +using AesSivKeyTest = TestWithParam<std::tuple<int, TestCase>>; + +INSTANTIATE_TEST_SUITE_P( + AesSivKeyTestSuite, AesSivKeyTest, + Combine(Values(32, 48, 64), + Values(TestCase{AesSivParameters::Variant::kTink, 0x02030400, + std::string("\x01\x02\x03\x04\x00", 5)}, + TestCase{AesSivParameters::Variant::kCrunchy, 0x01030005, + std::string("\x00\x01\x03\x00\x05", 5)}, + TestCase{AesSivParameters::Variant::kNoPrefix, absl::nullopt, + ""}))); + +TEST_P(AesSivKeyTest, CreateSucceeds) { + int key_size; + TestCase test_case; + std::tie(key_size, test_case) = GetParam(); + + util::StatusOr<AesSivParameters> params = + AesSivParameters::Create(key_size, test_case.variant); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + util::StatusOr<AesSivKey> key = AesSivKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + EXPECT_THAT(key->GetParameters(), Eq(*params)); + EXPECT_THAT(key->GetIdRequirement(), Eq(test_case.id_requirement)); + EXPECT_THAT(key->GetOutputPrefix(), Eq(test_case.output_prefix)); +} + +TEST(AesSivKeyTest, CreateKeyWithMismatchedKeySizeFails) { + // Key size parameter is 64 bytes. + util::StatusOr<AesSivParameters> params = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + // Key material is 32 bytes (another valid key length). + RestrictedData mismatched_secret = RestrictedData(/*num_random_bytes=*/32); + + EXPECT_THAT(AesSivKey::Create(*params, mismatched_secret, + /*id_requirement=*/123, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesSivKeyTest, CreateKeyWithInvalidIdRequirementFails) { + util::StatusOr<AesSivParameters> no_prefix_params = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kNoPrefix); + ASSERT_THAT(no_prefix_params, IsOk()); + + util::StatusOr<AesSivParameters> tink_params = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(tink_params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/64); + + EXPECT_THAT(AesSivKey::Create(*no_prefix_params, secret, + /*id_requirement=*/123, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT( + AesSivKey::Create(*tink_params, secret, + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesSivKeyTest, GetKeyBytes) { + int key_size; + TestCase test_case; + std::tie(key_size, test_case) = GetParam(); + + util::StatusOr<AesSivParameters> params = + AesSivParameters::Create(key_size, test_case.variant); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + + util::StatusOr<AesSivKey> key = AesSivKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT(key->GetKeyBytes(GetPartialKeyAccess()), IsOkAndHolds(secret)); +} + +TEST_P(AesSivKeyTest, KeyEquals) { + int key_size; + TestCase test_case; + std::tie(key_size, test_case) = GetParam(); + + util::StatusOr<AesSivParameters> params = + AesSivParameters::Create(key_size, test_case.variant); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + util::StatusOr<AesSivKey> key = AesSivKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<AesSivKey> other_key = AesSivKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key == *other_key); + EXPECT_TRUE(*other_key == *key); + EXPECT_FALSE(*key != *other_key); + EXPECT_FALSE(*other_key != *key); +} + +TEST(AesSivKeyTest, DifferentVariantNotEqual) { + util::StatusOr<AesSivParameters> crunchy_params = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kCrunchy); + ASSERT_THAT(crunchy_params, IsOk()); + + util::StatusOr<AesSivParameters> tink_params = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(tink_params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/64); + + util::StatusOr<AesSivKey> key = + AesSivKey::Create(*crunchy_params, secret, /*id_requirement=*/0x01020304, + GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<AesSivKey> other_key = + AesSivKey::Create(*tink_params, secret, /*id_requirement=*/0x01020304, + GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(AesSivKeyTest, DifferentSecretDataNotEqual) { + util::StatusOr<AesSivParameters> params = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret1 = RestrictedData(/*num_random_bytes=*/64); + RestrictedData secret2 = RestrictedData(/*num_random_bytes=*/64); + + util::StatusOr<AesSivKey> key = AesSivKey::Create( + *params, secret1, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<AesSivKey> other_key = AesSivKey::Create( + *params, secret2, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(AesSivKeyTest, DifferentIdRequirementNotEqual) { + util::StatusOr<AesSivParameters> params = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/64); + + util::StatusOr<AesSivKey> key = AesSivKey::Create( + *params, secret, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<AesSivKey> other_key = AesSivKey::Create( + *params, secret, /*id_requirement=*/0x02030405, GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/daead/aes_siv_parameters.cc b/cc/daead/aes_siv_parameters.cc new file mode 100644 index 0000000..996be60 --- /dev/null +++ b/cc/daead/aes_siv_parameters.cc
@@ -0,0 +1,58 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/daead/aes_siv_parameters.h" + +#include <set> + +namespace crypto { +namespace tink { + +util::StatusOr<AesSivParameters> AesSivParameters::Create(int key_size_in_bytes, + Variant variant) { + if (key_size_in_bytes != 32 && key_size_in_bytes != 48 && + key_size_in_bytes != 64) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Key size should be 32, 48, or 64 bytes, got ", + key_size_in_bytes, " bytes.")); + } + static const std::set<Variant>* supported_variants = new std::set<Variant>( + {Variant::kTink, Variant::kCrunchy, Variant::kNoPrefix}); + if (supported_variants->find(variant) == supported_variants->end()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create AES-SIV parameters with unknown variant."); + } + return AesSivParameters(key_size_in_bytes, variant); +} + +bool AesSivParameters::operator==(const Parameters& other) const { + const AesSivParameters* that = dynamic_cast<const AesSivParameters*>(&other); + if (that == nullptr) { + return false; + } + if (key_size_in_bytes_ != that->key_size_in_bytes_) { + return false; + } + if (variant_ != that->variant_) { + return false; + } + return true; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/daead/aes_siv_parameters.h b/cc/daead/aes_siv_parameters.h new file mode 100644 index 0000000..3d828b9 --- /dev/null +++ b/cc/daead/aes_siv_parameters.h
@@ -0,0 +1,73 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_DAEAD_AES_SIV_PARAMETERS_H_ +#define TINK_DAEAD_AES_SIV_PARAMETERS_H_ + +#include "tink/daead/deterministic_aead_parameters.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +// Describes the parameters of an `AesSivKey`. +class AesSivParameters : public DeterministicAeadParameters { + public: + // Description of the output prefix prepended to the ciphertext. + enum class Variant : int { + // Prepends '0x01<big endian key id>' to the ciphertext. + kTink = 1, + // Prepends '0x00<big endian key id>' to the ciphertext. + kCrunchy = 2, + // Does not prepend any prefix (i.e., keys must have no ID requirement). + kNoPrefix = 3, + // Added to guard from failures that may be caused by future expansions. + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements = 20, + }; + + // Copyable and movable. + AesSivParameters(const AesSivParameters& other) = default; + AesSivParameters& operator=(const AesSivParameters& other) = default; + AesSivParameters(AesSivParameters&& other) = default; + AesSivParameters& operator=(AesSivParameters&& other) = default; + + // Creates `AesSivParameters` object from `key_size_in_bytes` and `variant`. + // Only allows 32-, 48-, and 64-byte key sizes as specified in RFC 5297. + static util::StatusOr<AesSivParameters> Create(int key_size_in_bytes, + Variant variant); + + int KeySizeInBytes() const { return key_size_in_bytes_; } + + Variant GetVariant() const { return variant_; } + + bool HasIdRequirement() const override { + return variant_ != Variant::kNoPrefix; + } + + bool operator==(const Parameters& other) const override; + + private: + AesSivParameters(int key_size_in_bytes, Variant variant) + : key_size_in_bytes_(key_size_in_bytes), variant_(variant) {} + + int key_size_in_bytes_; + Variant variant_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_DAEAD_AES_SIV_PARAMETERS_H_
diff --git a/cc/daead/aes_siv_parameters_test.cc b/cc/daead/aes_siv_parameters_test.cc new file mode 100644 index 0000000..ac57baf --- /dev/null +++ b/cc/daead/aes_siv_parameters_test.cc
@@ -0,0 +1,182 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/daead/aes_siv_parameters.h" + +#include <tuple> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::TestWithParam; +using ::testing::Values; + +struct CreateTestCase { + AesSivParameters::Variant variant; + int key_size; + bool has_id_requirement; +}; + +using AesSivParametersBuildTest = TestWithParam<CreateTestCase>; + +INSTANTIATE_TEST_SUITE_P( + AesSivParametersBuildTestSuite, AesSivParametersBuildTest, + Values(CreateTestCase{AesSivParameters::Variant::kTink, /*key_size=*/32, + /*has_id_requirement=*/true}, + CreateTestCase{AesSivParameters::Variant::kCrunchy, /*key_size=*/48, + /*has_id_requirement=*/true}, + CreateTestCase{AesSivParameters::Variant::kNoPrefix, /*key_size=*/64, + /*has_id_requirement=*/false})); + +TEST_P(AesSivParametersBuildTest, Create) { + CreateTestCase test_case = GetParam(); + + util::StatusOr<AesSivParameters> parameters = + AesSivParameters::Create(test_case.key_size, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + EXPECT_THAT(parameters->KeySizeInBytes(), Eq(test_case.key_size)); + EXPECT_THAT(parameters->GetVariant(), Eq(test_case.variant)); + EXPECT_THAT(parameters->HasIdRequirement(), Eq(test_case.has_id_requirement)); +} + +TEST(AesSivParametersTest, CreateWithInvalidVariantFails) { + EXPECT_THAT(AesSivParameters::Create( + /*key_size_in_bytes=*/64, + AesSivParameters::Variant:: + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesSivParametersTest, CreateWithInvalidKeySizeFails) { + EXPECT_THAT(AesSivParameters::Create(/*key_size_in_bytes=*/31, + AesSivParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesSivParameters::Create(/*key_size_in_bytes=*/33, + AesSivParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesSivParameters::Create(/*key_size_in_bytes=*/47, + AesSivParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesSivParameters::Create(/*key_size_in_bytes=*/49, + AesSivParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesSivParameters::Create(/*key_size_in_bytes=*/63, + AesSivParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesSivParameters::Create(/*key_size_in_bytes=*/65, + AesSivParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesSivParametersTest, CopyConstructor) { + util::StatusOr<AesSivParameters> parameters = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + AesSivParameters copy(*parameters); + EXPECT_THAT(copy.KeySizeInBytes(), Eq(64)); + EXPECT_THAT(copy.GetVariant(), Eq(AesSivParameters::Variant::kTink)); + EXPECT_THAT(copy.HasIdRequirement(), IsTrue()); +} + +TEST(AesSivParametersTest, CopyAssignment) { + util::StatusOr<AesSivParameters> parameters = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + AesSivParameters copy = *parameters; + EXPECT_THAT(copy.KeySizeInBytes(), Eq(64)); + EXPECT_THAT(copy.GetVariant(), Eq(AesSivParameters::Variant::kTink)); + EXPECT_THAT(copy.HasIdRequirement(), IsTrue()); +} + +using AesSivParametersVariantTest = + TestWithParam<std::tuple<int, AesSivParameters::Variant>>; + +INSTANTIATE_TEST_SUITE_P(AesSivParametersVariantTestSuite, + AesSivParametersVariantTest, + Combine(Values(32, 48, 64), + Values(AesSivParameters::Variant::kTink, + AesSivParameters::Variant::kCrunchy, + AesSivParameters::Variant::kNoPrefix))); + +TEST_P(AesSivParametersVariantTest, ParametersEquals) { + int key_size; + AesSivParameters::Variant variant; + std::tie(key_size, variant) = GetParam(); + + util::StatusOr<AesSivParameters> parameters = + AesSivParameters::Create(key_size, variant); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<AesSivParameters> other_parameters = + AesSivParameters::Create(key_size, variant); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters == *other_parameters); + EXPECT_TRUE(*other_parameters == *parameters); + EXPECT_FALSE(*parameters != *other_parameters); + EXPECT_FALSE(*other_parameters != *parameters); +} + +TEST(AesSivParametersTest, KeySizeNotEqual) { + util::StatusOr<AesSivParameters> parameters = AesSivParameters::Create( + /*key_size_in_bytes=*/48, AesSivParameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<AesSivParameters> other_parameters = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +TEST(AesSivParametersTest, VariantNotEqual) { + util::StatusOr<AesSivParameters> parameters = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<AesSivParameters> other_parameters = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kNoPrefix); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/daead/aes_siv_proto_serialization.cc b/cc/daead/aes_siv_proto_serialization.cc new file mode 100644 index 0000000..c1711c5 --- /dev/null +++ b/cc/daead/aes_siv_proto_serialization.cc
@@ -0,0 +1,237 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/daead/aes_siv_proto_serialization.h" + +#include <string> + +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/daead/aes_siv_key.h" +#include "tink/daead/aes_siv_parameters.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/aes_siv.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::google::crypto::tink::AesSivKeyFormat; +using ::google::crypto::tink::OutputPrefixType; + +using AesSivProtoParametersParserImpl = + internal::ParametersParserImpl<internal::ProtoParametersSerialization, + AesSivParameters>; +using AesSivProtoParametersSerializerImpl = + internal::ParametersSerializerImpl<AesSivParameters, + internal::ProtoParametersSerialization>; +using AesSivProtoKeyParserImpl = + internal::KeyParserImpl<internal::ProtoKeySerialization, AesSivKey>; +using AesSivProtoKeySerializerImpl = + internal::KeySerializerImpl<AesSivKey, internal::ProtoKeySerialization>; + +const absl::string_view kTypeUrl = + "type.googleapis.com/google.crypto.tink.AesSivKey"; + +util::StatusOr<AesSivParameters::Variant> ToVariant( + OutputPrefixType output_prefix_type) { + switch (output_prefix_type) { + case OutputPrefixType::LEGACY: + ABSL_FALLTHROUGH_INTENDED; // Parse LEGACY output prefix as CRUNCHY. + case OutputPrefixType::CRUNCHY: + return AesSivParameters::Variant::kCrunchy; + case OutputPrefixType::RAW: + return AesSivParameters::Variant::kNoPrefix; + case OutputPrefixType::TINK: + return AesSivParameters::Variant::kTink; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine AesSivParameters::Variant"); + } +} + +util::StatusOr<OutputPrefixType> ToOutputPrefixType( + AesSivParameters::Variant variant) { + switch (variant) { + case AesSivParameters::Variant::kCrunchy: + return OutputPrefixType::CRUNCHY; + case AesSivParameters::Variant::kNoPrefix: + return OutputPrefixType::RAW; + case AesSivParameters::Variant::kTink: + return OutputPrefixType::TINK; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine output prefix type"); + } +} + +util::StatusOr<AesSivParameters> ParseParameters( + const internal::ProtoParametersSerialization& serialization) { + if (serialization.GetKeyTemplate().type_url() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing AesSivParameters."); + } + + AesSivKeyFormat proto_key_format; + if (!proto_key_format.ParseFromString( + serialization.GetKeyTemplate().value())) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse AesSivKeyFormat proto"); + } + if (proto_key_format.version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + util::StatusOr<AesSivParameters::Variant> variant = + ToVariant(serialization.GetKeyTemplate().output_prefix_type()); + if (!variant.ok()) return variant.status(); + + return AesSivParameters::Create(proto_key_format.key_size(), *variant); +} + +util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters( + const AesSivParameters& parameters) { + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(parameters.GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + + AesSivKeyFormat proto_key_format; + proto_key_format.set_key_size(parameters.KeySizeInBytes()); + + return internal::ProtoParametersSerialization::Create( + kTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString()); +} + +util::StatusOr<AesSivKey> ParseKey( + const internal::ProtoKeySerialization& serialization, + absl::optional<SecretKeyAccessToken> token) { + if (serialization.TypeUrl() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing AesSivKey."); + } + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + google::crypto::tink::AesSivKey proto_key; + RestrictedData restricted_data = serialization.SerializedKeyProto(); + // OSS proto library complains if input is not converted to a string. + if (!proto_key.ParseFromString( + std::string(restricted_data.GetSecret(*token)))) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse AesSivKey proto"); + } + if (proto_key.version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + util::StatusOr<AesSivParameters::Variant> variant = + ToVariant(serialization.GetOutputPrefixType()); + if (!variant.ok()) return variant.status(); + + util::StatusOr<AesSivParameters> parameters = + AesSivParameters::Create(proto_key.key_value().length(), *variant); + if (!parameters.ok()) return parameters.status(); + + return AesSivKey::Create( + *parameters, RestrictedData(proto_key.key_value(), *token), + serialization.IdRequirement(), GetPartialKeyAccess()); +} + +util::StatusOr<internal::ProtoKeySerialization> SerializeKey( + const AesSivKey& key, absl::optional<SecretKeyAccessToken> token) { + util::StatusOr<RestrictedData> restricted_input = + key.GetKeyBytes(GetPartialKeyAccess()); + if (!restricted_input.ok()) return restricted_input.status(); + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + + google::crypto::tink::AesSivKey proto_key; + proto_key.set_version(0); + // OSS proto library complains if input is not converted to a string. + proto_key.set_key_value(std::string(restricted_input->GetSecret(*token))); + + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(key.GetParameters().GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + + RestrictedData restricted_output = + RestrictedData(proto_key.SerializeAsString(), *token); + return internal::ProtoKeySerialization::Create( + kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC, + *output_prefix_type, key.GetIdRequirement()); +} + +AesSivProtoParametersParserImpl* AesSivProtoParametersParser() { + static auto* parser = + new AesSivProtoParametersParserImpl(kTypeUrl, ParseParameters); + return parser; +} + +AesSivProtoParametersSerializerImpl* AesSivProtoParametersSerializer() { + static auto* serializer = + new AesSivProtoParametersSerializerImpl(kTypeUrl, SerializeParameters); + return serializer; +} + +AesSivProtoKeyParserImpl* AesSivProtoKeyParser() { + static auto* parser = new AesSivProtoKeyParserImpl(kTypeUrl, ParseKey); + return parser; +} + +AesSivProtoKeySerializerImpl* AesSivProtoKeySerializer() { + static auto* serializer = new AesSivProtoKeySerializerImpl(SerializeKey); + return serializer; +} + +} // namespace + +util::Status RegisterAesSivProtoSerialization() { + util::Status status = + internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersParser(AesSivProtoParametersParser()); + if (!status.ok()) return status; + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersSerializer(AesSivProtoParametersSerializer()); + if (!status.ok()) return status; + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeyParser(AesSivProtoKeyParser()); + if (!status.ok()) return status; + + return internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeySerializer(AesSivProtoKeySerializer()); +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/daead/aes_siv_proto_serialization.h b/cc/daead/aes_siv_proto_serialization.h new file mode 100644 index 0000000..daff867 --- /dev/null +++ b/cc/daead/aes_siv_proto_serialization.h
@@ -0,0 +1,31 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_DAEAD_AES_SIV_PROTO_SERIALIZATION_H_ +#define TINK_DAEAD_AES_SIV_PROTO_SERIALIZATION_H_ + +#include "tink/util/status.h" + +namespace crypto { +namespace tink { + +// Registers proto parsers and serializers for AES-SIV parameters and keys. +crypto::tink::util::Status RegisterAesSivProtoSerialization(); + +} // namespace tink +} // namespace crypto + +#endif // TINK_DAEAD_AES_SIV_PROTO_SERIALIZATION_H_
diff --git a/cc/daead/aes_siv_proto_serialization_test.cc b/cc/daead/aes_siv_proto_serialization_test.cc new file mode 100644 index 0000000..1f6a45a --- /dev/null +++ b/cc/daead/aes_siv_proto_serialization_test.cc
@@ -0,0 +1,395 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/daead/aes_siv_proto_serialization.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/daead/aes_siv_key.h" +#include "tink/daead/aes_siv_parameters.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/subtle/random.h" +#include "tink/util/test_matchers.h" +#include "proto/aes_siv.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::subtle::Random; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesSivKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::NotNull; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + AesSivParameters::Variant variant; + OutputPrefixType output_prefix_type; + int key_size; + absl::optional<int> id; + std::string output_prefix; +}; + +class AesSivProtoSerializationTest : public TestWithParam<TestCase> { + protected: + void SetUp() override { + internal::MutableSerializationRegistry::GlobalInstance().Reset(); + } +}; + +INSTANTIATE_TEST_SUITE_P( + AesSivProtoSerializationTestSuite, AesSivProtoSerializationTest, + Values(TestCase{AesSivParameters::Variant::kTink, OutputPrefixType::TINK, + /*key_size=*/32, /*id=*/0x02030400, + /*output_prefix=*/std::string("\x01\x02\x03\x04\x00", 5)}, + TestCase{AesSivParameters::Variant::kCrunchy, + OutputPrefixType::CRUNCHY, /*key_size=*/48, + /*id=*/0x01030005, + /*output_prefix=*/std::string("\x00\x01\x03\x00\x05", 5)}, + TestCase{AesSivParameters::Variant::kNoPrefix, OutputPrefixType::RAW, + /*key_size=*/64, /*id=*/absl::nullopt, + /*output_prefix=*/""})); + +TEST_P(AesSivProtoSerializationTest, ParseParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + AesSivKeyFormat key_format_proto; + key_format_proto.set_version(0); + key_format_proto.set_key_size(test_case.key_size); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", + test_case.output_prefix_type, key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(params, IsOk()); + EXPECT_THAT((*params)->HasIdRequirement(), test_case.id.has_value()); + + const AesSivParameters* siv_params = + dynamic_cast<const AesSivParameters*>(params->get()); + ASSERT_THAT(siv_params, NotNull()); + EXPECT_THAT(siv_params->GetVariant(), Eq(test_case.variant)); + EXPECT_THAT(siv_params->KeySizeInBytes(), Eq(test_case.key_size)); +} + +TEST_F(AesSivProtoSerializationTest, ParseParametersWithInvalidSerialization) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + AesSivKeyFormat key_format_proto; + key_format_proto.set_version(0); + key_format_proto.set_key_size(64); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", + OutputPrefixType::RAW, "invalid_serialization"); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesSivProtoSerializationTest, ParseParametersWithUnkownOutputPrefix) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + AesSivKeyFormat key_format_proto; + key_format_proto.set_version(0); + key_format_proto.set_key_size(64); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", + OutputPrefixType::UNKNOWN_PREFIX, + key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesSivProtoSerializationTest, ParseParametersWithInvalidVersion) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + AesSivKeyFormat key_format_proto; + key_format_proto.set_version(1); + key_format_proto.set_key_size(64); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", + OutputPrefixType::RAW, key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesSivProtoSerializationTest, SerializeParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + util::StatusOr<AesSivParameters> parameters = + AesSivParameters::Create(test_case.key_size, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>( + *parameters); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.AesSivKey")); + + const internal::ProtoParametersSerialization* proto_serialization = + dynamic_cast<const internal::ProtoParametersSerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->GetKeyTemplate().type_url(), + Eq("type.googleapis.com/google.crypto.tink.AesSivKey")); + EXPECT_THAT(proto_serialization->GetKeyTemplate().output_prefix_type(), + Eq(test_case.output_prefix_type)); + + AesSivKeyFormat key_format; + ASSERT_THAT( + key_format.ParseFromString(proto_serialization->GetKeyTemplate().value()), + IsTrue()); + EXPECT_THAT(key_format.key_size(), Eq(test_case.key_size)); +} + +TEST_P(AesSivProtoSerializationTest, ParseKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + google::crypto::tink::AesSivKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, test_case.output_prefix_type, test_case.id); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(test_case.id)); + EXPECT_THAT((*key)->GetParameters().HasIdRequirement(), + test_case.id.has_value()); + + util::StatusOr<AesSivParameters> expected_parameters = + AesSivParameters::Create(test_case.key_size, test_case.variant); + ASSERT_THAT(expected_parameters, IsOk()); + + util::StatusOr<AesSivKey> expected_key = AesSivKey::Create( + *expected_parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(expected_key, IsOk()); + + EXPECT_THAT(**key, Eq(*expected_key)); +} + +TEST_F(AesSivProtoSerializationTest, ParseLegacyKeyAsCrunchy) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(64); + google::crypto::tink::AesSivKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::LEGACY, /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + + const AesSivKey* aes_siv_key = dynamic_cast<const AesSivKey*>(key->get()); + ASSERT_THAT(aes_siv_key, NotNull()); + EXPECT_THAT(aes_siv_key->GetParameters().GetVariant(), + Eq(AesSivParameters::Variant::kCrunchy)); +} + +TEST_F(AesSivProtoSerializationTest, ParseKeyWithInvalidSerialization) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + RestrictedData serialized_key = + RestrictedData("invalid_serialization", InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesSivProtoSerializationTest, ParseKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(64); + google::crypto::tink::AesSivKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, /*token=*/absl::nullopt); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesSivProtoSerializationTest, ParseKeyWithInvalidVersion) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(64); + google::crypto::tink::AesSivKey key_proto; + key_proto.set_version(1); // Invalid version number. + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesSivProtoSerializationTest, SerializeKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + util::StatusOr<AesSivParameters> parameters = + AesSivParameters::Create(test_case.key_size, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + util::StatusOr<AesSivKey> key = AesSivKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.AesSivKey")); + + const internal::ProtoKeySerialization* proto_serialization = + dynamic_cast<const internal::ProtoKeySerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->TypeUrl(), + Eq("type.googleapis.com/google.crypto.tink.AesSivKey")); + EXPECT_THAT(proto_serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(proto_serialization->GetOutputPrefixType(), + Eq(test_case.output_prefix_type)); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(test_case.id)); + + google::crypto::tink::AesSivKey proto_key; + // OSS proto library complains if input is not converted to a string. + ASSERT_THAT(proto_key.ParseFromString(std::string( + proto_serialization->SerializedKeyProto().GetSecret( + InsecureSecretKeyAccess::Get()))), + IsTrue()); + EXPECT_THAT(proto_key.key_value().size(), Eq(test_case.key_size)); +} + +TEST_F(AesSivProtoSerializationTest, SerializeKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + util::StatusOr<AesSivParameters> parameters = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kNoPrefix); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(64); + util::StatusOr<AesSivKey> key = AesSivKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>(*key, absl::nullopt); + EXPECT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/daead/deterministic_aead_config.cc b/cc/daead/deterministic_aead_config.cc index 749f8cf..5c1a229 100644 --- a/cc/daead/deterministic_aead_config.cc +++ b/cc/daead/deterministic_aead_config.cc
@@ -17,26 +17,17 @@ #include "tink/daead/deterministic_aead_config.h" #include "absl/memory/memory.h" -#include "tink/config/config_util.h" #include "tink/config/tink_fips.h" #include "tink/daead/aes_siv_key_manager.h" +#include "tink/daead/aes_siv_proto_serialization.h" #include "tink/daead/deterministic_aead_wrapper.h" #include "tink/registry.h" #include "tink/util/status.h" -#include "proto/config.pb.h" - -using google::crypto::tink::RegistryConfig; namespace crypto { namespace tink { // static -const RegistryConfig& DeterministicAeadConfig::Latest() { - static const RegistryConfig* config = new RegistryConfig(); - return *config; -} - -// static util::Status DeterministicAeadConfig::Register() { // Currently there are no FIPS-validated deterministic AEAD key managers // available, therefore none will be registered in FIPS only mode. @@ -49,6 +40,9 @@ absl::make_unique<AesSivKeyManager>(), true); if (!status.ok()) return status; + status = RegisterAesSivProtoSerialization(); + if (!status.ok()) return status; + // Register primitive wrapper. return Registry::RegisterPrimitiveWrapper( absl::make_unique<DeterministicAeadWrapper>());
diff --git a/cc/daead/deterministic_aead_config.h b/cc/daead/deterministic_aead_config.h index 1441fdd..3fdd199 100644 --- a/cc/daead/deterministic_aead_config.h +++ b/cc/daead/deterministic_aead_config.h
@@ -35,14 +35,6 @@ // class DeterministicAeadConfig { public: - static constexpr char kCatalogueName[] = "TinkDeterministicAead"; - static constexpr char kPrimitiveName[] = "DeterministicAead"; - - // Returns config of DeterministicAead implementations supported - // in the current Tink release. - ABSL_DEPRECATED("This is not supported anymore.") - static const google::crypto::tink::RegistryConfig& Latest(); - // Registers DeterministicAead primitive wrapper and key managers for all // DeterministicAead key types from the current Tink release. static crypto::tink::util::Status Register();
diff --git a/cc/daead/deterministic_aead_config_test.cc b/cc/daead/deterministic_aead_config_test.cc index 1a59d14..ba833d1 100644 --- a/cc/daead/deterministic_aead_config_test.cc +++ b/cc/daead/deterministic_aead_config_test.cc
@@ -17,17 +17,24 @@ #include "tink/daead/deterministic_aead_config.h" #include <list> +#include <memory> #include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/status/status.h" -#include "tink/config.h" #include "tink/config/tink_fips.h" +#include "tink/daead/aes_siv_key.h" #include "tink/daead/aes_siv_key_manager.h" +#include "tink/daead/aes_siv_parameters.h" #include "tink/daead/deterministic_aead_key_templates.h" #include "tink/deterministic_aead.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" #include "tink/keyset_handle.h" +#include "tink/partial_key_access.h" #include "tink/registry.h" #include "tink/util/status.h" #include "tink/util/test_matchers.h" @@ -40,11 +47,16 @@ using ::crypto::tink::test::DummyDeterministicAead; using ::crypto::tink::test::IsOk; using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; using ::testing::Eq; class DeterministicAeadConfigTest : public ::testing::Test { protected: - void SetUp() override { Registry::Reset(); } + void SetUp() override { + Registry::Reset(); + internal::MutableSerializationRegistry::GlobalInstance().Reset(); + } }; TEST_F(DeterministicAeadConfigTest, Basic) { @@ -121,6 +133,98 @@ } } +TEST_F(DeterministicAeadConfigTest, AesSivProtoParamsSerializationRegistered) { + if (IsFipsModeEnabled()) { + GTEST_SKIP() << "Not supported in FIPS-only mode"; + } + + util::StatusOr<internal::ProtoParametersSerialization> + proto_params_serialization = + internal::ProtoParametersSerialization::Create( + DeterministicAeadKeyTemplates::Aes256Siv()); + ASSERT_THAT(proto_params_serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> parsed_params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *proto_params_serialization); + ASSERT_THAT(parsed_params.status(), StatusIs(absl::StatusCode::kNotFound)); + + util::StatusOr<AesSivParameters> params = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_params = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>(*params); + ASSERT_THAT(serialized_params.status(), + StatusIs(absl::StatusCode::kNotFound)); + + ASSERT_THAT(DeterministicAeadConfig::Register(), IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> parsed_params2 = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *proto_params_serialization); + ASSERT_THAT(parsed_params2, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_params2 = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>(*params); + ASSERT_THAT(serialized_params2, IsOk()); +} + +TEST_F(DeterministicAeadConfigTest, AesSivProtoKeySerializationRegistered) { + if (IsFipsModeEnabled()) { + GTEST_SKIP() << "Not supported in FIPS-only mode"; + } + + google::crypto::tink::AesSivKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(subtle::Random::GetRandomBytes(64)); + + util::StatusOr<internal::ProtoKeySerialization> proto_key_serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", + RestrictedData(key_proto.SerializeAsString(), + InsecureSecretKeyAccess::Get()), + KeyData::SYMMETRIC, OutputPrefixType::TINK, /*id_requirement=*/123); + ASSERT_THAT(proto_key_serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> parsed_key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *proto_key_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_key.status(), StatusIs(absl::StatusCode::kNotFound)); + + util::StatusOr<AesSivParameters> params = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + util::StatusOr<AesSivKey> key = + AesSivKey::Create(*params, + RestrictedData(subtle::Random::GetRandomBytes(64), + InsecureSecretKeyAccess::Get()), + /*id_requirement=*/123, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_key = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialized_key.status(), StatusIs(absl::StatusCode::kNotFound)); + + ASSERT_THAT(DeterministicAeadConfig::Register(), IsOk()); + + util::StatusOr<std::unique_ptr<Key>> parsed_key2 = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *proto_key_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_key2, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_key2 = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialized_key2, IsOk()); +} + } // namespace } // namespace tink } // namespace crypto
diff --git a/cc/daead/deterministic_aead_factory.cc b/cc/daead/deterministic_aead_factory.cc index bdd23ed..8ae089a 100644 --- a/cc/daead/deterministic_aead_factory.cc +++ b/cc/daead/deterministic_aead_factory.cc
@@ -16,6 +16,8 @@ #include "tink/daead/deterministic_aead_factory.h" +#include <memory> + #include "tink/daead/deterministic_aead_wrapper.h" #include "tink/deterministic_aead.h" #include "tink/key_manager.h"
diff --git a/cc/daead/deterministic_aead_factory.h b/cc/daead/deterministic_aead_factory.h index 5359a29..dcd365b 100644 --- a/cc/daead/deterministic_aead_factory.h +++ b/cc/daead/deterministic_aead_factory.h
@@ -17,6 +17,8 @@ #ifndef TINK_DAEAD_DETERMINISTIC_AEAD_FACTORY_H_ #define TINK_DAEAD_DETERMINISTIC_AEAD_FACTORY_H_ +#include <memory> + #include "absl/base/macros.h" #include "tink/deterministic_aead.h" #include "tink/key_manager.h"
diff --git a/cc/daead/deterministic_aead_factory_test.cc b/cc/daead/deterministic_aead_factory_test.cc index 78cc363..ae6ac6d 100644 --- a/cc/daead/deterministic_aead_factory_test.cc +++ b/cc/daead/deterministic_aead_factory_test.cc
@@ -31,7 +31,6 @@ #include "tink/util/test_util.h" #include "proto/aes_siv.pb.h" -using crypto::tink::TestKeysetHandle; using crypto::tink::test::AddRawKey; using crypto::tink::test::AddTinkKey; using google::crypto::tink::AesSivKeyFormat;
diff --git a/cc/daead/deterministic_aead_key.h b/cc/daead/deterministic_aead_key.h new file mode 100644 index 0000000..1534754 --- /dev/null +++ b/cc/daead/deterministic_aead_key.h
@@ -0,0 +1,52 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_DAEAD_DETERMINISTIC_AEAD_KEY_H_ +#define TINK_DAEAD_DETERMINISTIC_AEAD_KEY_H_ + +#include "absl/strings/string_view.h" +#include "tink/daead/deterministic_aead_parameters.h" +#include "tink/key.h" + +namespace crypto { +namespace tink { + +// Represents a function to encrypt and decrypt data using deterministic +// authenticated encryption with associated data (Deterministic AEAD). +class DeterministicAeadKey : public Key { + public: + // Returns the bytes prefixed to every ciphertext generated by this key. + // + // In order to make key rotation more efficient, Tink allows every + // Deterministic AEAD key to have an associated ciphertext output prefix. When + // decrypting a ciphertext, only keys with a matching prefix have to be tried. + // + // Note that a priori, the output prefix may not be unique in a keyset + // (i.e., different keys in a keyset may have the same prefix or one prefix + // may be a prefix of another). To avoid this, built-in Tink keys use the + // convention that the prefix is either '0x00<big endian key id>' or + // '0x01<big endian key id>'. + virtual absl::string_view GetOutputPrefix() const = 0; + + const DeterministicAeadParameters& GetParameters() const override = 0; + + bool operator==(const Key& other) const override = 0; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_DAEAD_DETERMINISTIC_AEAD_KEY_H_
diff --git a/cc/daead/deterministic_aead_parameters.h b/cc/daead/deterministic_aead_parameters.h new file mode 100644 index 0000000..53b746a --- /dev/null +++ b/cc/daead/deterministic_aead_parameters.h
@@ -0,0 +1,32 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_DAEAD_DETERMINISTIC_AEAD_PARAMETERS_H_ +#define TINK_DAEAD_DETERMINISTIC_AEAD_PARAMETERS_H_ + +#include "tink/parameters.h" + +namespace crypto { +namespace tink { + +// Describes a `DeterministicAeadKey` (e.g., key attributes), excluding the +// randomly chosen key material. +class DeterministicAeadParameters : public Parameters {}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_DAEAD_DETERMINISTIC_AEAD_PARAMETERS_H_
diff --git a/cc/daead/deterministic_aead_wrapper.cc b/cc/daead/deterministic_aead_wrapper.cc index 3db1e99..13cc57f 100644 --- a/cc/daead/deterministic_aead_wrapper.cc +++ b/cc/daead/deterministic_aead_wrapper.cc
@@ -16,6 +16,7 @@ #include "tink/daead/deterministic_aead_wrapper.h" +#include <memory> #include <string> #include <utility> @@ -70,7 +71,7 @@ absl::string_view ciphertext, absl::string_view associated_data) const override; - ~DeterministicAeadSetWrapper() override {} + ~DeterministicAeadSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<DeterministicAead>> daead_set_;
diff --git a/cc/daead/deterministic_aead_wrapper.h b/cc/daead/deterministic_aead_wrapper.h index ea95b94..77dd3d7 100644 --- a/cc/daead/deterministic_aead_wrapper.h +++ b/cc/daead/deterministic_aead_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_DAEAD_DETERMINISTIC_AEAD_WRAPPER_H_ #define TINK_DAEAD_DETERMINISTIC_AEAD_WRAPPER_H_ +#include <memory> + #include "absl/strings/string_view.h" #include "tink/deterministic_aead.h" #include "tink/primitive_set.h"
diff --git a/cc/daead/failing_daead.cc b/cc/daead/failing_daead.cc index 61b30f9..b3fa26f 100644 --- a/cc/daead/failing_daead.cc +++ b/cc/daead/failing_daead.cc
@@ -15,6 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/daead/failing_daead.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/daead/failing_daead.h b/cc/daead/failing_daead.h index 551ad4b..74c7b50 100644 --- a/cc/daead/failing_daead.h +++ b/cc/daead/failing_daead.h
@@ -16,6 +16,7 @@ #ifndef TINK_DAEAD_FAILING_DAEAD_H_ #define TINK_DAEAD_FAILING_DAEAD_H_ +#include <memory> #include <string> #include "absl/strings/string_view.h"
diff --git a/cc/daead/subtle/aead_or_daead.cc b/cc/daead/subtle/aead_or_daead.cc index c8c8b57..419c333 100644 --- a/cc/daead/subtle/aead_or_daead.cc +++ b/cc/daead/subtle/aead_or_daead.cc
@@ -16,6 +16,7 @@ #include "tink/daead/subtle/aead_or_daead.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/daead/subtle/aead_or_daead_test.cc b/cc/daead/subtle/aead_or_daead_test.cc index 8244b72..a70e204 100644 --- a/cc/daead/subtle/aead_or_daead_test.cc +++ b/cc/daead/subtle/aead_or_daead_test.cc
@@ -16,6 +16,7 @@ #include "tink/daead/subtle/aead_or_daead.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/deterministic_aead.h b/cc/deterministic_aead.h index fa73762..310f72f 100644 --- a/cc/deterministic_aead.h +++ b/cc/deterministic_aead.h
@@ -53,7 +53,7 @@ absl::string_view ciphertext, absl::string_view associated_data) const = 0; - virtual ~DeterministicAead() {} + virtual ~DeterministicAead() = default; }; } // namespace tink
diff --git a/cc/examples/.bazelrc b/cc/examples/.bazelrc new file mode 100644 index 0000000..0fe2c3f --- /dev/null +++ b/cc/examples/.bazelrc
@@ -0,0 +1,4 @@ +# Minumum C++ version. Override it building this project with +# `bazel build --cxxopt='-std=c++<XY>' --host_cxxopt='c++<XY>' ...` +# (Both -std and --host_cxxopt must be set to force the desired version.) +build --cxxopt='-std=c++14' --host_cxxopt='-std=c++14'
diff --git a/cc/examples/.bazelversion b/cc/examples/.bazelversion index ac14c3d..09b254e 100644 --- a/cc/examples/.bazelversion +++ b/cc/examples/.bazelversion
@@ -1 +1 @@ -5.1.1 +6.0.0
diff --git a/cc/examples/CMakeLists.txt b/cc/examples/CMakeLists.txt new file mode 100644 index 0000000..a85f910 --- /dev/null +++ b/cc/examples/CMakeLists.txt
@@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.13) + +project(Examples CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_BUILD_TYPE Release) + +# Import Tink as an in-tree dependency. +add_subdirectory(../.. tink) + +# Make sure we have bash. +find_program(BASH_PROGRAM bash REQUIRED) + +# Include path at the base of the examples folder. +set(TINK_EXAMPLES_INCLUDE_PATH "${CMAKE_SOURCE_DIR}") + +include(FetchContent) + +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz + URL_HASH SHA256=b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5 +) + +FetchContent_GetProperties(googletest) +if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory( + ${googletest_SOURCE_DIR} + ${googletest_BINARY_DIR} + EXCLUDE_FROM_ALL) +endif() + +enable_testing() + +add_subdirectory(aead) +add_subdirectory(digital_signatures) +add_subdirectory(hybrid_encryption) +add_subdirectory(jwt) +add_subdirectory(mac) +add_subdirectory(util) +add_subdirectory(walkthrough)
diff --git a/cc/examples/MODULE.bazel b/cc/examples/MODULE.bazel new file mode 100644 index 0000000..654c59f --- /dev/null +++ b/cc/examples/MODULE.bazel
@@ -0,0 +1,11 @@ +"""Module definition for Tink C++ Examples.""" +# Omitting `version` because this is not meant to be depended on by other +# modules. +module(name = "tink_cc_examples") + +# Use local tink_cc. +bazel_dep(name = "tink_cc", version = "") +local_path_override(module_name = "tink_cc", path = "../") + +bazel_dep(name = "googletest", version = "1.12.1", repo_name = "com_google_googletest") +bazel_dep(name = "abseil-cpp", version = "20230125.1", repo_name="com_google_absl")
diff --git a/cc/examples/WORKSPACE b/cc/examples/WORKSPACE index b7fff2b..5cdbbc9 100644 --- a/cc/examples/WORKSPACE +++ b/cc/examples/WORKSPACE
@@ -1,4 +1,4 @@ -workspace(name = "examples_cc") +workspace(name = "tink_cc_examples") # The local_repository() rule is used below because the workspaces referenced # are all located within the Tink git repository.
diff --git a/cc/examples/WORKSPACE.bzlmod b/cc/examples/WORKSPACE.bzlmod new file mode 100644 index 0000000..057d6aa --- /dev/null +++ b/cc/examples/WORKSPACE.bzlmod
@@ -0,0 +1 @@ +# This replaces the content of the WORKSPACE file when using --enable_bzlmod.
diff --git a/cc/examples/aead/BUILD.bazel b/cc/examples/aead/BUILD.bazel index 80727bb..fa7907a 100644 --- a/cc/examples/aead/BUILD.bazel +++ b/cc/examples/aead/BUILD.bazel
@@ -11,14 +11,12 @@ name = "aead_cli", srcs = ["aead_cli.cc"], deps = [ + "//util", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/status", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/strings", "@tink_cc//:aead", - "@tink_cc//:cleartext_keyset_handle", - "@tink_cc//:json_keyset_reader", "@tink_cc//:keyset_handle", "@tink_cc//:keyset_reader", "@tink_cc//aead:aead_config",
diff --git a/cc/examples/aead/CMakeLists.txt b/cc/examples/aead/CMakeLists.txt new file mode 100644 index 0000000..ae04ba6 --- /dev/null +++ b/cc/examples/aead/CMakeLists.txt
@@ -0,0 +1,16 @@ +add_executable(aead_cli aead_cli.cc) +target_include_directories(aead_cli PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(aead_cli + tink::static + absl::check + absl::flags_parse + util) + +add_test( + NAME aead_cli_test + COMMAND "${BASH_PROGRAM}" + "${CMAKE_CURRENT_SOURCE_DIR}/aead_cli_test.sh" + "${CMAKE_CURRENT_BINARY_DIR}/aead_cli" + "${CMAKE_CURRENT_SOURCE_DIR}/aead_test_keyset.json")
diff --git a/cc/examples/aead/aead_cli.cc b/cc/examples/aead/aead_cli.cc index 028f9a1..36e3225 100644 --- a/cc/examples/aead/aead_cli.cc +++ b/cc/examples/aead/aead_cli.cc
@@ -15,184 +15,117 @@ /////////////////////////////////////////////////////////////////////////////// // [START aead-example] // A command-line utility for testing Tink AEAD. -#include <fstream> #include <iostream> #include <memory> -#include <sstream> +#include <ostream> #include <string> -#include <utility> #include "absl/flags/flag.h" #include "absl/flags/parse.h" -#include "absl/memory/memory.h" -#include "absl/status/status.h" -#include "absl/strings/str_cat.h" +#include "absl/log/check.h" #include "absl/strings/string_view.h" #include "tink/aead.h" #include "tink/aead/aead_config.h" -#include "tink/cleartext_keyset_handle.h" -#include "tink/json_keyset_reader.h" +#include "util/util.h" #include "tink/keyset_handle.h" -#include "tink/keyset_reader.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 AEAD"); +ABSL_FLAG(std::string, associated_data, "", + "Associated data for AEAD (default: empty"); namespace { using ::crypto::tink::Aead; using ::crypto::tink::AeadConfig; -using ::crypto::tink::CleartextKeysetHandle; -using ::crypto::tink::JsonKeysetReader; using ::crypto::tink::KeysetHandle; -using ::crypto::tink::KeysetReader; using ::crypto::tink::util::Status; using ::crypto::tink::util::StatusOr; constexpr absl::string_view kEncrypt = "encrypt"; constexpr absl::string_view kDecrypt = "decrypt"; -// Creates a KeysetReader that reads a JSON-formatted keyset -// from the given file. -StatusOr<std::unique_ptr<KeysetReader>> GetJsonKeysetReader( - const std::string& filename) { - std::clog << "Creating a JsonKeysetReader...\n"; - auto key_input_stream = absl::make_unique<std::ifstream>(); - key_input_stream->open(filename, std::ifstream::in); - return JsonKeysetReader::New(std::move(key_input_stream)); -} - -// Creates a KeysetHandle that for a keyset read from the given file, -// which is expected to contain a JSON-formatted keyset. -StatusOr<std::unique_ptr<KeysetHandle>> ReadKeyset( - const std::string& filename) { - StatusOr<std::unique_ptr<KeysetReader>> keyset_reader = - GetJsonKeysetReader(filename); - if (!keyset_reader.ok()) { - return keyset_reader.status(); - } - return CleartextKeysetHandle::Read(*std::move(keyset_reader)); -} - -// Reads `filename` and returns the read content as a string, or an error status -// if the file does not exist. -StatusOr<std::string> Read(const std::string& filename) { - std::clog << "Reading the input...\n"; - std::ifstream input_stream; - input_stream.open(filename, std::ifstream::in); - if (!input_stream.is_open()) { - return Status(absl::StatusCode::kInternal, - absl::StrCat("Error opening input file ", filename)); - } - std::stringstream input; - input << input_stream.rdbuf(); - return input.str(); -} - -// Writes the given `data_to_write` to the specified file `filename`. -Status Write(const std::string& data_to_write, const std::string& filename) { - std::clog << "Writing the output...\n"; - std::ofstream output_stream(filename, - std::ofstream::out | std::ofstream::binary); - if (!output_stream.is_open()) { - return Status(absl::StatusCode::kInternal, - absl::StrCat("Error opening output file ", filename)); - } - output_stream << data_to_write; - return crypto::tink::util::OkStatus(); +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 { + +// AEAD example CLI implementation. +Status AeadCli(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 = AeadConfig::Register(); + if (!result.ok()) return result; + + // Read the 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<Aead>> aead = (*keyset_handle)->GetPrimitive<Aead>(); + if (!aead.ok()) return aead.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> encrypt_result = + (*aead)->Encrypt(*input_file_content, associated_data); + if (!encrypt_result.ok()) return encrypt_result.status(); + output = encrypt_result.value(); + } else { // operation == kDecrypt. + StatusOr<std::string> decrypt_result = + (*aead)->Decrypt(*input_file_content, associated_data); + if (!decrypt_result.ok()) return decrypt_result.status(); + output = decrypt_result.value(); + } + + // Write the output to the output 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); - if (mode.empty()) { - std::cerr << "Mode must be specified with --mode=<" << kEncrypt << "|" - << kDecrypt << ">." << std::endl; - exit(1); - } - - if (mode != kEncrypt && mode != kDecrypt) { - std::cerr << "Unknown mode '" << mode << "'; " - << "Expected either " << kEncrypt << " or " << kDecrypt << "." - << std::endl; - exit(1); - } std::clog << "Using keyset from file " << keyset_filename << " to 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; - Status result = AeadConfig::Register(); - if (!result.ok()) { - std::cerr << result.message() << std::endl; - exit(1); - } - - // Read the keyset from file. - StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = - ReadKeyset(keyset_filename); - if (!keyset_handle.ok()) { - std::cerr << keyset_handle.status().message() << std::endl; - exit(1); - } - - // Get the primitive. - StatusOr<std::unique_ptr<Aead>> aead_primitive = - (*keyset_handle)->GetPrimitive<Aead>(); - if (!aead_primitive.ok()) { - std::cerr << aead_primitive.status().message() << std::endl; - exit(1); - } - - // Read the input. - StatusOr<std::string> input_file_content = Read(input_filename); - if (!input_file_content.ok()) { - std::cerr << input_file_content.status().message() << std::endl; - exit(1); - } - - // Compute the output. - std::clog << mode << "ing...\n"; - std::string output; - if (mode == kEncrypt) { - StatusOr<std::string> encrypt_result = - (*aead_primitive)->Encrypt(*input_file_content, associated_data); - if (!encrypt_result.ok()) { - std::cerr << encrypt_result.status().message() << std::endl; - exit(1); - } - output = encrypt_result.value(); - } else { // operation == kDecrypt. - StatusOr<std::string> decrypt_result = - (*aead_primitive)->Decrypt(*input_file_content, associated_data); - if (!decrypt_result.ok()) { - std::cerr << decrypt_result.status().message() << std::endl; - exit(1); - } - output = decrypt_result.value(); - } - - // Write the output to the output file. - Status write_result = Write(output, output_filename); - if (!write_result.ok()) { - std::cerr << write_result.message() << std::endl; - exit(1); - } - - std::clog << "All done." << std::endl; + CHECK_OK(tink_cc_examples::AeadCli(mode, keyset_filename, input_filename, + output_filename, associated_data)); return 0; } // [END aead-example]
diff --git a/cc/examples/aead/aead_cli_test.sh b/cc/examples/aead/aead_cli_test.sh index ad5579e..d1d2627 100755 --- a/cc/examples/aead/aead_cli_test.sh +++ b/cc/examples/aead/aead_cli_test.sh
@@ -20,6 +20,8 @@ # Tests for Tink CC AEAD. ############################################################################# +: "${TEST_TMPDIR:=$(mktemp -d)}" + readonly CLI="$1" readonly KEYSET_FILE="$2" readonly DATA_FILE="${TEST_TMPDIR}/example_data.txt" @@ -44,7 +46,7 @@ } ####################################### -# Asserts that the outcome of the latest test command was the expected one. +# Asserts that the outcome of the latest test command is 0. # # If not, it terminates the test execution. # @@ -52,23 +54,29 @@ # TEST_STATUS # TEST_NAME # TEST_CASE -# Arguments: -# expected_outcome: The expected outcome. ####################################### -_assert_test_command_outcome() { - expected_outcome="$1" - if (( TEST_STATUS != expected_outcome )); then - echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" - exit 1 +assert_command_succeeded() { + if (( TEST_STATUS != 0 )); then + echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" + exit 1 fi } -assert_command_succeeded() { - _assert_test_command_outcome 0 -} - +####################################### +# 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() { - _assert_test_command_outcome 1 + if (( TEST_STATUS == 0 )); then + echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" + exit 1 + fi } #######################################
diff --git a/cc/examples/aead/aead_test_keyset.json b/cc/examples/aead/aead_test_keyset.json index ff0aa56..204a2b4 100644 --- a/cc/examples/aead/aead_test_keyset.json +++ b/cc/examples/aead/aead_test_keyset.json
@@ -1,12 +1,15 @@ { - "primaryKeyId":1931667682, - "key":[{ - "keyData":{ - "typeUrl":"type.googleapis.com/google.crypto.tink.AesGcmKey", - "value":"GhD+9l0RANZjzZEZ8PDp7LRW", - "keyMaterialType":"SYMMETRIC"}, - "status":"ENABLED", - "keyId":1931667682, - "outputPrefixType":"TINK" - }] + "key": [ + { + "keyData": { + "keyMaterialType": "SYMMETRIC", + "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey", + "value": "GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg==" + }, + "keyId": 294406504, + "outputPrefixType": "TINK", + "status": "ENABLED" + } + ], + "primaryKeyId": 294406504 }
diff --git a/cc/examples/digital_signatures/BUILD.bazel b/cc/examples/digital_signatures/BUILD.bazel index 3cc0bf2..a6adfaa 100644 --- a/cc/examples/digital_signatures/BUILD.bazel +++ b/cc/examples/digital_signatures/BUILD.bazel
@@ -2,19 +2,11 @@ licenses(["notice"]) -cc_library( - name = "util", - srcs = ["util.cc"], - hdrs = ["util.h"], - deps = [ - "@tink_cc//:binary_keyset_reader", - "@tink_cc//:binary_keyset_writer", - "@tink_cc//:cleartext_keyset_handle", - "@tink_cc//:keyset_handle", - "@tink_cc//:keyset_reader", - "@tink_cc//:keyset_writer", - "@tink_cc//config:tink_config", - "@tink_cc//util:status", +filegroup( + name = "digital_signature_keyset", + srcs = [ + "digital_signature_private_keyset.json", + "digital_signature_public_keyset.json", ], ) @@ -22,10 +14,15 @@ name = "digital_signatures_cli", srcs = ["digital_signatures_cli.cc"], deps = [ - ":util", + "//util", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + "@com_google_absl//absl/log:check", + "@tink_cc//:keyset_handle", "@tink_cc//:public_key_sign", "@tink_cc//:public_key_verify", - "@tink_cc//signature:signature_key_templates", + "@tink_cc//signature:signature_config", + "@tink_cc//util:status", ], ) @@ -35,6 +32,10 @@ srcs = ["digital_signatures_cli_test.sh"], args = [ "$(rootpath :digital_signatures_cli)", + "$(rootpaths :digital_signature_keyset)", ], - data = [":digital_signatures_cli"], + data = [ + ":digital_signature_keyset", + ":digital_signatures_cli", + ], )
diff --git a/cc/examples/digital_signatures/CMakeLists.txt b/cc/examples/digital_signatures/CMakeLists.txt new file mode 100644 index 0000000..8a14e9e --- /dev/null +++ b/cc/examples/digital_signatures/CMakeLists.txt
@@ -0,0 +1,17 @@ +add_executable(digital_signatures_cli digital_signatures_cli.cc) +target_include_directories(digital_signatures_cli PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(digital_signatures_cli + tink::static + absl::check + absl::flags_parse + util) + +add_test( + NAME digital_signatures_cli_test + COMMAND "${BASH_PROGRAM}" + "${CMAKE_CURRENT_SOURCE_DIR}/digital_signatures_cli_test.sh" + "${CMAKE_CURRENT_BINARY_DIR}/digital_signatures_cli" + "${CMAKE_CURRENT_SOURCE_DIR}/digital_signature_private_keyset.json" + "${CMAKE_CURRENT_SOURCE_DIR}/digital_signature_public_keyset.json")
diff --git a/cc/examples/digital_signatures/README.md b/cc/examples/digital_signatures/README.md deleted file mode 100644 index 4e9558a..0000000 --- a/cc/examples/digital_signatures/README.md +++ /dev/null
@@ -1,27 +0,0 @@ -# C++ Digital Signatures CLI - -This is a command-line tool that can generate -[Digital Signature](../../../docs/PRIMITIVES.md#digital-signatures) -keys, and create and verify digital signatures. - -It demonstrates the basic steps of using Tink, namely loading key material, -obtaining a primitive, and using the primitive to do crypto. - -## Build and Run - -### Bazel - -```shell -git clone https://github.com/google/tink -cd tink/cc/examples -bazel build ... -echo "a message" > message.txt -./bazel-bin/digital_signatures/digital_signatures_cli gen-private-key private_keyset.bin -./bazel-bin/digital_signatures/digital_signatures_cli get-public-key private_keyset.bin \ - public_keyset.bin -./bazel-bin/digital_signatures/digital_signatures_cli sign private_keyset.bin \ - message.txt signature.bin -./bazel-bin/digital_signatures/digital_signatures_cli verify public_keyset.bin \ - message.txt signature.bin result.txt -cat result.txt -```
diff --git a/cc/examples/digital_signatures/digital_signature_private_keyset.json b/cc/examples/digital_signatures/digital_signature_private_keyset.json new file mode 100644 index 0000000..215dad2 --- /dev/null +++ b/cc/examples/digital_signatures/digital_signature_private_keyset.json
@@ -0,0 +1,15 @@ +{ + "primaryKeyId": 1487078030, + "key": [ + { + "keyData": { + "typeUrl": "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey", + "value": "Ek0SBggDEAIYAhohANUKuRXZHBD8rPcB5M6+pmgVSjk3gLSD/htdVvbrfbnPIiAXepWekQPRS74qUTMEwN6nXeizXucBxDk0SoKoeqShOBogbJEwIZASdx42tIitAe8UoBxWyi11Mq+HnWNtcQWkG18=", + "keyMaterialType": "ASYMMETRIC_PRIVATE" + }, + "status": "ENABLED", + "keyId": 1487078030, + "outputPrefixType": "TINK" + } + ] +}
diff --git a/cc/examples/digital_signatures/digital_signature_public_keyset.json b/cc/examples/digital_signatures/digital_signature_public_keyset.json new file mode 100644 index 0000000..01ebcfa --- /dev/null +++ b/cc/examples/digital_signatures/digital_signature_public_keyset.json
@@ -0,0 +1,15 @@ +{ + "primaryKeyId": 1487078030, + "key": [ + { + "keyData": { + "typeUrl": "type.googleapis.com/google.crypto.tink.EcdsaPublicKey", + "value": "EgYIAxACGAIaIQDVCrkV2RwQ/Kz3AeTOvqZoFUo5N4C0g/4bXVb26325zyIgF3qVnpED0Uu+KlEzBMDep13os17nAcQ5NEqCqHqkoTg=", + "keyMaterialType": "ASYMMETRIC_PUBLIC" + }, + "status": "ENABLED", + "keyId": 1487078030, + "outputPrefixType": "TINK" + } + ] +}
diff --git a/cc/examples/digital_signatures/digital_signatures_cli.cc b/cc/examples/digital_signatures/digital_signatures_cli.cc index a6a2a79..caa37cc 100644 --- a/cc/examples/digital_signatures/digital_signatures_cli.cc +++ b/cc/examples/digital_signatures/digital_signatures_cli.cc
@@ -13,246 +13,123 @@ // limitations under the License. // /////////////////////////////////////////////////////////////////////////////// - -// A command-line utility for generating Digital Signatures keys, and creating -// and verifying digital signatures. -// -// The first argument is the operation and it should be one of the following: -// gen-private-key get-public-key sign verify. -// Additional arguments depend on the operation. -// -// gen-private-key -// Generates a new private keyset using the RsaSsaPkcs13072Sha256F4 template. -// It requires 1 additional argument: -// output-file: name of the file for the resulting output -// -// get-public-key -// Extracts a public keyset associated with the given private keyset. -// It requires 2 additional arguments: -// private-keyset-file: name of the file with the private keyset -// output-file: name of the file for the resulting output -// -// sign -// Signs the message using the given private keyset. -// It requires 3 additional arguments: -// private-keyset-file: name of the file with the private keyset -// message-file: name of the file with the message -// output-file: name of the file for the resulting output -// -// verify -// Verifies the signature of the message using the given public keyset. -// It requires 4 additional arguments: -// public-keyset-file: name of the file with the public keyset -// message-file: name of the file with the message -// signature-file: name of the file with the signature -// output-file: name of the file for the resulting output (valid/invalid) - +// [START digital-signature-example] +// A utility for signing and verifying files using digital signatures. #include <iostream> +#include <memory> +#include <ostream> #include <string> -#include <utility> +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/log/check.h" +#include "util/util.h" +#include "tink/keyset_handle.h" #include "tink/public_key_sign.h" #include "tink/public_key_verify.h" -#include "tink/signature/signature_key_templates.h" -#include "digital_signatures/util.h" +#include "tink/signature/signature_config.h" +#include "tink/util/status.h" -// Prints usage info. -void PrintUsageInfo() { - std::clog << "Usage: operation arguments\n" - << "where operation is one of the following:\n" - << " gen-private-key get-public-key sign verify\n" - << "and, depending on the operation, arguments are:\n" - << " gen-private-key: output-file\n" - << " get-public-key: private-keyset-file output-file\n" - << " sign: private-keyset-file message-file output-file\n" - << " verify: public-keyset-file message-file signature-file " - << "output-file" << std::endl; +ABSL_FLAG(std::string, keyset_filename, "", "Keyset file in JSON format"); +ABSL_FLAG(std::string, mode, "", "Mode of operation (sign|verify)"); +ABSL_FLAG(std::string, input_filename, "", "Filename to operate on"); +ABSL_FLAG(std::string, signature_filename, "", "Path to the signature file"); + +namespace { + +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::PublicKeySign; +using ::crypto::tink::PublicKeyVerify; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; + +constexpr absl::string_view kSign = "sign"; +constexpr absl::string_view kVerify = "verify"; + +void ValidateParams() { + // [START_EXCLUDE] + CHECK(absl::GetFlag(FLAGS_mode) == kSign || + absl::GetFlag(FLAGS_mode) == kVerify) + << "Invalid mode; must be `" << kSign << "` or `" << kVerify << "`" + << std::endl; + 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_signature_filename).empty()) + << "Signature file must be specified"; + // [END_EXCLUDE] } -// Generates a new private keyset using the RsaSsaPkcs13072Sha256F4 template -// and writes it to the output file. -void GeneratePrivateKey(const std::string& output_filename) { - std::clog << "Generating a new private keyset.." << std::endl; +} // namespace - auto key_template = - crypto::tink::SignatureKeyTemplates::RsaSsaPkcs13072Sha256F4(); - auto new_keyset_handle_result = - crypto::tink::KeysetHandle::GenerateNew(key_template); - if (!new_keyset_handle_result.ok()) { - std::clog << "Generating new keyset failed: " - << new_keyset_handle_result.status().message() << std::endl; - exit(1); +namespace tink_cc_examples { + +// Digital signature example CLI implementation. +Status DigitalSignatureCli(absl::string_view mode, + const std::string& keyset_filename, + const std::string& input_filename, + const std::string& signature_filename) { + Status result = crypto::tink::SignatureConfig::Register(); + if (!result.ok()) return result; + + // Read the keyset from file. + StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = + ReadJsonCleartextKeyset(keyset_filename); + if (!keyset_handle.ok()) return keyset_handle.status(); + + // Read the input. + StatusOr<std::string> input_file_content = ReadFile(input_filename); + if (!input_file_content.ok()) return input_file_content.status(); + + if (mode == kSign) { + StatusOr<std::unique_ptr<PublicKeySign>> public_key_sign = + (*keyset_handle)->GetPrimitive<PublicKeySign>(); + if (!public_key_sign.ok()) return public_key_sign.status(); + + StatusOr<std::string> signature = + (*public_key_sign)->Sign(*input_file_content); + if (!signature.ok()) return signature.status(); + + return WriteToFile(*signature, signature_filename); + } else { // mode == kVerify + StatusOr<std::unique_ptr<PublicKeyVerify>> public_key_verify = + (*keyset_handle)->GetPrimitive<PublicKeyVerify>(); + if (!public_key_verify.ok()) return public_key_verify.status(); + + // Read the signature. + StatusOr<std::string> signature_file_content = ReadFile(signature_filename); + if (!signature_file_content.ok()) return signature_file_content.status(); + + return (*public_key_verify) + ->Verify(*signature_file_content, *input_file_content); } - auto keyset_handle = std::move(new_keyset_handle_result.value()); - - std::clog << "Writing the keyset to file " << output_filename - << "..." << std::endl; - - Util::WriteKeyset(keyset_handle, output_filename); } -// Extracts a public keyset associated with the given private keyset -// and writes it to the output file. -void ExtractPublicKey(const std::string& private_keyset_filename, - const std::string& output_filename) { - std::clog << "Extracting a public keyset associated with the private " - << "keyset from file " << private_keyset_filename << "..." - << std::endl; - - auto private_keyset_handle = Util::ReadKeyset(private_keyset_filename); - - auto new_keyset_handle_result = - private_keyset_handle->GetPublicKeysetHandle(); - if (!new_keyset_handle_result.ok()) { - std::clog << "Getting the keyset failed: " - << new_keyset_handle_result.status().message() << std::endl; - exit(1); - } - auto public_keyset_handle = std::move(new_keyset_handle_result.value()); - - std::clog << "Writing the keyset to file " << output_filename - << "..." << std::endl; - - Util::WriteKeyset(public_keyset_handle, output_filename); -} - -// Signs the message using the given private keyset -// and writes the signature to the output file. -void Sign(const std::string& keyset_filename, - const std::string& message_filename, - const std::string& output_filename) { - auto keyset_handle = Util::ReadKeyset(keyset_filename); - - auto primitive_result = - keyset_handle->GetPrimitive<crypto::tink::PublicKeySign>(); - if (!primitive_result.ok()) { - std::clog << "Getting PublicKeySign-primitive from the factory failed: " - << primitive_result.status().message() << std::endl; - exit(1); - } - auto public_key_sign = std::move(primitive_result.value()); - - std::clog << "Signing message from file " << message_filename - << " using private keyset from file " << keyset_filename - << "..." << std::endl; - - std::string message = Util::Read(message_filename); - - auto sign_result = public_key_sign->Sign(message); - if (!sign_result.ok()) { - std::clog << "Error while signing the message: " - << sign_result.status().message() << std::endl; - exit(1); - } - std::string signature = sign_result.value(); - - std::clog << "Writing the resulting signature to file " << output_filename - << "..." << std::endl; - - Util::Write(signature, output_filename); -} - -// Verifies the signature of the message using the given public keyset -// and writes the result to the output file. -void Verify(const std::string& keyset_filename, - const std::string& message_filename, - const std::string& signature_filename, - const std::string& output_filename) { - auto keyset_handle = Util::ReadKeyset(keyset_filename); - - auto primitive_result = - keyset_handle->GetPrimitive<crypto::tink::PublicKeyVerify>(); - if (!primitive_result.ok()) { - std::clog << "Getting PublicKeyVerify-primitive from the factory " - << "failed: " << primitive_result.status().message() << std::endl; - exit(1); - } - auto public_key_verify = std::move(primitive_result.value()); - - std::clog << "Verifying signature from file " << signature_filename - << " of the message from file " << message_filename - << " using public keyset from file " << keyset_filename - << "..." << std::endl; - - std::string signature = Util::Read(signature_filename); - std::string message = Util::Read(message_filename); - - std::string result; - auto verify_status = public_key_verify->Verify(signature, message); - if (!verify_status.ok()) { - std::clog << "Error while verifying the signature: " - << verify_status.message() << std::endl; - result = "invalid"; - } else { - result = "valid"; - } - - std::clog << "Writing the result to file " << output_filename - << "..." << std::endl; - - Util::Write(result, output_filename); -} +} // namespace tink_cc_examples int main(int argc, char** argv) { - if (argc == 1) { - PrintUsageInfo(); - exit(1); + 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 signature_filename = absl::GetFlag(FLAGS_signature_filename); + + std::clog << "Using keyset in " << keyset_filename << " to " << mode; + if (mode == kSign) { + std::clog << " file " << input_filename + << "; the resulting signature is written to " + << signature_filename << std::endl; + } else { // mode == kVerify + std::clog << " the signature in " << signature_filename + << " over the content of " << input_filename << std::endl; } - Util::InitTink(); - - std::string operation = argv[1]; - - if (operation == "gen-private-key") { - if (argc != 3) { - PrintUsageInfo(); - exit(1); - } - - std::string output_filename = argv[2]; - - GeneratePrivateKey(output_filename); - } else if (operation == "get-public-key") { - if (argc != 4) { - PrintUsageInfo(); - exit(1); - } - - std::string private_keyset_filename = argv[2]; - std::string output_filename = argv[3]; - - ExtractPublicKey(private_keyset_filename, output_filename); - } else if (operation == "sign") { - if (argc != 5) { - PrintUsageInfo(); - exit(1); - } - - std::string keyset_filename = argv[2]; - std::string message_filename = argv[3]; - std::string output_filename = argv[4]; - - Sign(keyset_filename, message_filename, output_filename); - } else if (operation == "verify") { - if (argc != 6) { - PrintUsageInfo(); - exit(1); - } - - std::string keyset_filename = argv[2]; - std::string message_filename = argv[3]; - std::string signature_filename = argv[4]; - std::string output_filename = argv[5]; - - Verify(keyset_filename, message_filename, signature_filename, - output_filename); - } else { - std::clog << "Unknown operation. Supported operations are: " - << "gen-private-key get-public-key sign verify" << std::endl; - exit(1); - } - - std::clog << "Done!" << std::endl; - + CHECK_OK(tink_cc_examples::DigitalSignatureCli( + mode, keyset_filename, input_filename, signature_filename)); return 0; } +// [END digital-signature-example]
diff --git a/cc/examples/digital_signatures/digital_signatures_cli_test.sh b/cc/examples/digital_signatures/digital_signatures_cli_test.sh index c83e92c..b84f3df 100755 --- a/cc/examples/digital_signatures/digital_signatures_cli_test.sh +++ b/cc/examples/digital_signatures/digital_signatures_cli_test.sh
@@ -14,114 +14,166 @@ # limitations under the License. ################################################################################ +set -euo pipefail ############################################################################# -#### Tests for digital_signatures_cli binary. - -SIGNATURE_CLI="$1" - -PRIVATE_KEYSET_FILE="$TEST_TMPDIR/private_keyset.bin" -PUBLIC_KEYSET_FILE="$TEST_TMPDIR/public_keyset.bin" -MESSAGE_FILE="$TEST_TMPDIR/message.txt" -SIGNATURE_FILE="$TEST_TMPDIR/signature.bin" -RESULT_FILE="$TEST_TMPDIR/result.txt" - -OTHER_PRIVATE_KEYSET_FILE="$TEST_TMPDIR/other_private_keyset.bin" -OTHER_PUBLIC_KEYSET_FILE="$TEST_TMPDIR/other_public_keyset.bin" -OTHER_MESSAGE_FILE="$TEST_TMPDIR/other_message.txt" - -echo "This is a message." > $MESSAGE_FILE -echo "This is a different message." > $OTHER_MESSAGE_FILE - +# Tests for Tink C++ digital signature example. ############################################################################# -#### Helper function that checks if values are equal. -assert_equal() { - if [ "$1" == "$2" ]; then - echo "+++ Success: values are equal." - else - echo "--- Failure: values are different. Expected: [$1], actual: [$2]." +: "${TEST_TMPDIR:=$(mktemp -d)}" + +readonly CLI="$1" +readonly PRIVATE_KEYSET_FILE="$2" +readonly PUBLIC_KEYSET_FILE="$3" +readonly MESSAGE_FILE="${TEST_TMPDIR}/message.txt" +readonly SIGNATURE_FILE="${TEST_TMPDIR}/signature.txt" +readonly TEST_NAME="TinkCcExamplesDigitalSignaturesTest" + +echo "This is some message to be signed." > "${MESSAGE_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 } -############################################################################# -#### All good, everything should work. -test_name="all_good" -echo "+++ Starting test $test_name..." +####################################### +# 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 +} -#### Generate a private key and get a public key. -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 +####################################### +# 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}" +} -#### Sign the message. -$SIGNATURE_CLI sign $PRIVATE_KEYSET_FILE $MESSAGE_FILE $SIGNATURE_FILE || exit 1 - -#### Verify the signature. -$SIGNATURE_CLI verify $PUBLIC_KEYSET_FILE $MESSAGE_FILE $SIGNATURE_FILE $RESULT_FILE || exit 1 - -#### Check that the signature is valid. -RESULT=$(<$RESULT_FILE) -assert_equal "valid" "$RESULT" +####################################### +# Ends a test case printing a success message. +# +# Globals: +# TEST_NAME +# TEST_CASE +####################################### +end_test_case() { + echo "[ OK ] ${TEST_NAME}.${TEST_CASE}" +} ############################################################################# -#### Bad private key when getting the public key. -test_name="get_public_key_with_bad_private_key" -echo "+++ Starting test $test_name..." -echo "abcd" >> $PRIVATE_KEYSET_FILE -$SIGNATURE_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE +start_test_case "sign_verify_all_good" -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE" +# Sign. +test_command "${CLI}" \ + --mode sign \ + --keyset_filename "${PRIVATE_KEYSET_FILE}" \ + --input_filename "${MESSAGE_FILE}" \ + --signature_filename "${SIGNATURE_FILE}" +assert_command_succeeded + +# Verify. +test_command "${CLI}" \ + --mode verify \ + --keyset_filename "${PUBLIC_KEYSET_FILE}" \ + --input_filename "${MESSAGE_FILE}" \ + --signature_filename "${SIGNATURE_FILE}" +assert_command_succeeded + +end_test_case ############################################################################# -#### Different public key when verifying a signature. -test_name="verify_with_different_public_key" -echo "+++ Starting test $test_name..." -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI gen-private-key $OTHER_PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI get-public-key $OTHER_PRIVATE_KEYSET_FILE $OTHER_PUBLIC_KEYSET_FILE || exit 1 -$SIGNATURE_CLI sign $PRIVATE_KEYSET_FILE $MESSAGE_FILE $SIGNATURE_FILE || exit 1 -$SIGNATURE_CLI verify $OTHER_PUBLIC_KEYSET_FILE $MESSAGE_FILE $SIGNATURE_FILE $RESULT_FILE || exit 1 +start_test_case "verify_fails_with_modified_signature" -RESULT=$(<$RESULT_FILE) -assert_equal "invalid" "$RESULT" +# Sign. +test_command "${CLI}" \ + --mode sign \ + --keyset_filename "${PRIVATE_KEYSET_FILE}" \ + --input_filename "${MESSAGE_FILE}" \ + --signature_filename "${SIGNATURE_FILE}" +assert_command_succeeded + +# Modify signature. +echo "modified" >> "${SIGNATURE_FILE}" + +# Verify. +test_command "${CLI}" \ + --mode verify \ + --keyset_filename "${PUBLIC_KEYSET_FILE}" \ + --input_filename "${MESSAGE_FILE}" \ + --signature_filename "${SIGNATURE_FILE}" +assert_command_failed + +end_test_case ############################################################################# -#### Different message when verifying a signature. -test_name="verify_with_different_message" -echo "+++ Starting test $test_name..." -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 -$SIGNATURE_CLI sign $PRIVATE_KEYSET_FILE $MESSAGE_FILE $SIGNATURE_FILE || exit 1 -$SIGNATURE_CLI verify $PUBLIC_KEYSET_FILE $OTHER_MESSAGE_FILE $SIGNATURE_FILE $RESULT_FILE || exit 1 +start_test_case "verify_fails_with_modified_message" -RESULT=$(<$RESULT_FILE) -assert_equal "invalid" "$RESULT" +# Sign. +test_command "${CLI}" \ + --mode sign \ + --keyset_filename "${PRIVATE_KEYSET_FILE}" \ + --input_filename "${MESSAGE_FILE}" \ + --signature_filename "${SIGNATURE_FILE}" +assert_command_succeeded -############################################################################# -#### Sign with wrong key. -test_name="sign_with_wrong_key" -echo "+++ Starting test $test_name..." +# Modify message. +echo "modified" >> "${MESSAGE_FILE}" -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 -$SIGNATURE_CLI sign $PUBLIC_KEYSET_FILE $MESSAGE_FILE $SIGNATURE_FILE +# Verify. +test_command "${CLI}" \ + --mode verify \ + --keyset_filename "${PUBLIC_KEYSET_FILE}" \ + --input_filename "${MESSAGE_FILE}" \ + --signature_filename "${SIGNATURE_FILE}" +assert_command_failed -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE" - -############################################################################# -#### Verify with wrong key. -test_name="verify_with_wrong_key" -echo "+++ Starting test $test_name..." - -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI sign $PRIVATE_KEYSET_FILE $MESSAGE_FILE $SIGNATURE_FILE || exit 1 -$SIGNATURE_CLI verify $PRIVATE_KEYSET_FILE $MESSAGE_FILE $SIGNATURE_FILE $RESULT_FILE - -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE" +end_test_case
diff --git a/cc/examples/digital_signatures/util.cc b/cc/examples/digital_signatures/util.cc deleted file mode 100644 index 4ba9a10..0000000 --- a/cc/examples/digital_signatures/util.cc +++ /dev/null
@@ -1,129 +0,0 @@ -// Copyright 2020 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "digital_signatures/util.h" - -#include <fstream> -#include <iostream> -#include <string> -#include <utility> - -#include "tink/binary_keyset_reader.h" -#include "tink/binary_keyset_writer.h" -#include "tink/cleartext_keyset_handle.h" -#include "tink/config.h" -#include "tink/config/tink_config.h" -#include "tink/keyset_handle.h" -#include "tink/keyset_reader.h" -#include "tink/keyset_writer.h" -#include "tink/util/status.h" - -using crypto::tink::BinaryKeysetReader; -using crypto::tink::BinaryKeysetWriter; -using crypto::tink::CleartextKeysetHandle; -using crypto::tink::KeysetHandle; -using crypto::tink::KeysetReader; -using crypto::tink::KeysetWriter; -using crypto::tink::TinkConfig; - -// static -std::unique_ptr<KeysetReader> Util::GetBinaryKeysetReader( - const std::string& filename) { - std::unique_ptr<std::ifstream> keyset_stream(new std::ifstream()); - keyset_stream->open(filename, std::ifstream::in); - auto keyset_reader_result = BinaryKeysetReader::New(std::move(keyset_stream)); - if (!keyset_reader_result.ok()) { - std::clog << "Creation of the BinaryKeysetReader failed: " - << keyset_reader_result.status().message() << std::endl; - exit(1); - } - return std::move(keyset_reader_result.value()); -} - -// static -std::unique_ptr<KeysetWriter> Util::GetBinaryKeysetWriter( - const std::string& filename) { - std::unique_ptr<std::ofstream> keyset_stream(new std::ofstream()); - keyset_stream->open(filename, std::ofstream::out); - auto keyset_writer_result = BinaryKeysetWriter::New(std::move(keyset_stream)); - if (!keyset_writer_result.ok()) { - std::clog << "Creation of the BinaryKeysetWriter failed: " - << keyset_writer_result.status().message() << std::endl; - exit(1); - } - return std::move(keyset_writer_result.value()); -} - -// static -std::unique_ptr<KeysetHandle> Util::ReadKeyset(const std::string& filename) { - auto keyset_reader = GetBinaryKeysetReader(filename); - auto keyset_handle_result = - CleartextKeysetHandle::Read(std::move(keyset_reader)); - if (!keyset_handle_result.ok()) { - std::clog << "Reading the keyset failed: " - << keyset_handle_result.status().message() << std::endl; - exit(1); - } - return std::move(keyset_handle_result.value()); -} - -// static -void Util::WriteKeyset(const std::unique_ptr<KeysetHandle>& keyset_handle, - const std::string& filename) { - auto keyset_writer = GetBinaryKeysetWriter(filename); - auto status = CleartextKeysetHandle::Write(keyset_writer.get(), - *keyset_handle); - if (!status.ok()) { - std::clog << "Writing the keyset failed: " << status.message() << std::endl; - exit(1); - } -} - -// static -void Util::InitTink() { - auto status = TinkConfig::Register(); - if (!status.ok()) { - std::clog << "Initialization of Tink failed: " << status.message() - << std::endl; - exit(1); - } -} - -// static -std::string Util::Read(const std::string& filename) { - std::ifstream input_stream; - input_stream.open(filename, std::ifstream::in); - if (!input_stream.is_open()) { - std::clog << "Error opening input file " << filename << std::endl; - exit(1); - } - std::stringstream input; - input << input_stream.rdbuf(); - input_stream.close(); - return input.str(); -} - -// static -void Util::Write(const std::string& output, const std::string& filename) { - std::ofstream output_stream(filename, - std::ofstream::out | std::ofstream::binary); - if (!output_stream.is_open()) { - std::clog << "Error opening output file " << filename << std::endl; - exit(1); - } - output_stream << output; - output_stream.close(); -}
diff --git a/cc/examples/digital_signatures/util.h b/cc/examples/digital_signatures/util.h deleted file mode 100644 index de3bf60..0000000 --- a/cc/examples/digital_signatures/util.h +++ /dev/null
@@ -1,63 +0,0 @@ -// Copyright 2020 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef TINK_EXAMPLES_DIGITAL_SIGNATURES_UTIL_H_ -#define TINK_EXAMPLES_DIGITAL_SIGNATURES_UTIL_H_ - -#include <fstream> -#include <iostream> -#include <string> - -#include "tink/keyset_handle.h" - -// Helper functions for Digital Signatures CLI -class Util { - public: - // Returns a BinaryKeysetReader that reads from the specified file. - // In case of errors writes a log message and aborts. - static std::unique_ptr<crypto::tink::KeysetReader> GetBinaryKeysetReader( - const std::string& filename); - - // Returns a BinaryKeysetWriter that writes from the specified file. - // In case of errors writes a log message and aborts. - static std::unique_ptr<crypto::tink::KeysetWriter> GetBinaryKeysetWriter( - const std::string& filename); - - // Reads a keyset from the specified file. - // In case of errors writes a log message and aborts. - static std::unique_ptr<crypto::tink::KeysetHandle> ReadKeyset( - const std::string& filename); - - // Writes the keyset to the specified file. - // In case of errors writes a log message and aborts. - static void WriteKeyset( - const std::unique_ptr<crypto::tink::KeysetHandle>& keyset_handle, - const std::string& filename); - - // Initializes Tink registry. - // In case of errors writes a log message and aborts. - static void InitTink(); - - // Reads the specified file and returns the contents as a string. - // In case of errors writes a log message and aborts. - static std::string Read(const std::string& filename); - - // Writes the given 'output' to the specified file. - // In case of errors writes a log message and aborts. - static void Write(const std::string& output, const std::string& filename); -}; - -#endif // TINK_EXAMPLES_DIGITAL_SIGNATURES_UTIL_H_
diff --git a/cc/examples/helloworld/BUILD.bazel b/cc/examples/helloworld/BUILD.bazel deleted file mode 100644 index f6b6efb..0000000 --- a/cc/examples/helloworld/BUILD.bazel +++ /dev/null
@@ -1,32 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -exports_files(["aes128_gcm_test_keyset_json.txt"]) - -cc_binary( - name = "hello_world", - srcs = ["hello_world.cc"], - deps = [ - "@tink_cc", - "@tink_cc//:cleartext_keyset_handle", - "@tink_cc//config:tink_config", - "@tink_cc//util:status", - ], -) - -sh_test( - name = "hello_world_test", - size = "small", - srcs = [ - "hello_world_test.sh", - ], - args = [ - "$(rootpath :hello_world)", - "$(rootpath :aes128_gcm_test_keyset_json.txt)", - ], - data = [ - ":aes128_gcm_test_keyset_json.txt", - ":hello_world", - ], -)
diff --git a/cc/examples/helloworld/CMakeLists.txt b/cc/examples/helloworld/CMakeLists.txt deleted file mode 100644 index 32932a5..0000000 --- a/cc/examples/helloworld/CMakeLists.txt +++ /dev/null
@@ -1,11 +0,0 @@ -# 3.5 is the minimum version supported by Tink and its dependencies. -cmake_minimum_required(VERSION 3.5) -project(TinkHelloWorld CXX) - -set(CMAKE_BUILD_TYPE Release) - -# Import Tink as an in-tree dependency. -add_subdirectory(../../.. tink) - -add_executable(hello_world hello_world.cc) -target_link_libraries(hello_world tink::static)
diff --git a/cc/examples/helloworld/CMakeLists_for_CMakeBuildTest.txt b/cc/examples/helloworld/CMakeLists_for_CMakeBuildTest.txt deleted file mode 100644 index d8a0778..0000000 --- a/cc/examples/helloworld/CMakeLists_for_CMakeBuildTest.txt +++ /dev/null
@@ -1,8 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(HelloWorld CXX) -set(CMAKE_CXX_STANDARD 11) - -add_subdirectory(third_party/tink) - -add_executable(hello_world hello_world.cc) -target_link_libraries(hello_world tink::static)
diff --git a/cc/examples/helloworld/README.md b/cc/examples/helloworld/README.md deleted file mode 100644 index 92952a5..0000000 --- a/cc/examples/helloworld/README.md +++ /dev/null
@@ -1,87 +0,0 @@ -# C++ Hello World - -This is a command-line tool that can encrypt and decrypt small files using -[AEAD (Authenticated Encryption with Associated Data)](https://developers.google.com/tink/aead). - -It demonstrates the basic steps of using Tink, namely loading key material, -obtaining a primitive, and using the primitive to do crypto. - -## Build and Run - -### Bazel - -```shell -# Build the code. -git clone https://github.com/google/tink -cd tink/cc/examples -bazel build ... - -# Create some input. -echo "some plaintext" > foo.txt - -# Encrypt. -./bazel-bin/helloworld/hello_world \ - ./helloworld/aes128_gcm_test_keyset_json.txt \ - encrypt \ - foo.txt \ - "some aad" \ - foo.encrypted - -# Decrypt. -./bazel-bin/helloworld/hello_world \ - ./helloworld/aes128_gcm_test_keyset_json.txt \ - decrypt \ - foo.encrypted \ - "some aad" \ - foo-decrypted.tx - -# Inspect the output. -cat foo-decrypted.txt -``` - -### CMake - -```shell - -# Clone the Tink repository. -git clone https://github.com/google/tink -cd tink/cc/examples/helloworld - -# Build the hello world example. -( - mkdir build && cd build - # Note: Specify -DTINK_USE_SYSTEM_OPENSSL=ON if you want to build Tink against - # OpenSSL rather than BoringSSL. CMake will search for a suitable OpenSSL - # library in the system's library path. - cmake .. -DCMAKE_CXX_STANDARD=11 - make -j"$(nproc)" -) - -# Create some input. -echo "some plaintext" > foo.txt - -./build/hello_world \ - aes128_gcm_test_keyset_json.txt \ - encrypt \ - foo.txt \ - "some aad" \ - foo.txt.encrypted - -./build/hello_world \ - aes128_gcm_test_keyset_json.txt \ - decrypt \ - foo.txt.encrypted \ - "some aad" \ - foo.txt.decrypted - -if [[ "$(diff foo.txt foo.txt.decrypted)" == "" ]]; then - echo "File correctly encrypted and decrypted" -else - echo "Failed to encrypt-and-decrypt. Diff:" - diff foo.txt foo.txt.decrypted -fi -``` - -The `cmake_build_test.sh` script will run an encrypt-then-decrypt test with -AEAD. You can build Tink against OpenSSL by passing the `--openssl` option -- -the script will download, build and install OpenSSL in a temporary directory.
diff --git a/cc/examples/helloworld/aes128_gcm_test_keyset_json.txt b/cc/examples/helloworld/aes128_gcm_test_keyset_json.txt deleted file mode 100644 index 456c46a..0000000 --- a/cc/examples/helloworld/aes128_gcm_test_keyset_json.txt +++ /dev/null
@@ -1,13 +0,0 @@ -{ - "primaryKeyId": 515279322, - "key": [{ - "keyData": { - "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey", - "keyMaterialType": "SYMMETRIC", - "value": "GhCS/1+ejWpx68NfGt6ziYHd" - }, - "outputPrefixType": "TINK", - "keyId": 515279322, - "status": "ENABLED" - }] -}
diff --git a/cc/examples/helloworld/cmake_build_test.sh b/cc/examples/helloworld/cmake_build_test.sh deleted file mode 100755 index 2bbbcab..0000000 --- a/cc/examples/helloworld/cmake_build_test.sh +++ /dev/null
@@ -1,210 +0,0 @@ -# Copyright 2020 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. -################################################################################ - -#!/bin/bash - -set -e - -readonly TINK_USE_CXX_STANDARD=11 - -# Test for using Tink in a CMake project. - -if [[ -z "${TEST_TMPDIR}" ]]; then - echo "Error: TEST_TMPDIR must be set to a temporary working directory." - exit 1 -fi - -if [[ -z "${TEST_SRCDIR}" ]]; then - echo "Error: TEST_SRCDIR must be set to Tink's parent directory." - exit 1 -fi - -# XDG_CACHE_HOME must be set for a successful build of BoringSSL. -export XDG_CACHE_HOME="${TEST_TMPDIR}/cache" -TEST_DATA_DIR="${TEST_SRCDIR}/tink/cc/examples/helloworld" -CMAKE_LISTS_FILE="${TEST_DATA_DIR}/CMakeLists_for_CMakeBuildTest.txt" -HELLO_WORLD_SRC="${TEST_DATA_DIR}/hello_world.cc" -KEYSET_FILE="${TEST_DATA_DIR}/aes128_gcm_test_keyset_json.txt" - -PROJECT_DIR="${TEST_TMPDIR}/my_project" -PLAINTEXT_FILE="${TEST_TMPDIR}/example_plaintext.txt" -CIPHERTEXT_FILE="${TEST_TMPDIR}/ciphertext.bin" -DECRYPTED_FILE="${TEST_TMPDIR}/decrypted.txt" -AAD_TEXT="some associated data" - -# If "true" build and install OpenSSL and build Tink against it. -USE_OPENSSL="false" -# If "true" build and install Abseil and build Tink against it. -USE_INSTALLED_ABSEIL="false" - -# Parse parameters. -while [[ "$#" -gt 0 ]]; do - case "$1" in - --openssl) - USE_OPENSSL="true" - shift - ;; - # Use prebuilt static libraries of Abseil. - --use_installed_abseil) - USE_INSTALLED_ABSEIL="true" - shift - ;; - *) - echo "Unknown parameter - $1" - exit 1 - esac -done - -####################################### -# Install a given version of OpenSSL in a temporary directory -# -# Adds the directory to PATH and sets OPENSSL_ROOT_DIR accordingly. -# -# Globals: -# OPENSSL_ROOT_DIR Gets populated with OpenSSL temporary directory. -# PATH Gets updated with bin's path. -# Arguments: -# openssl_version Version of OpenSSL to install, e.g., 1.1.1l. -####################################### -install_openssl() { - local openssl_version="$1" - - local openssl_name="openssl-${openssl_version}" - local openssl_archive="${openssl_name}.tar.gz" - local openssl_url="https://www.openssl.org/source/${openssl_archive}" - local openssl_sha256="$(curl -sS https://www.openssl.org/source/${openssl_archive}.sha256)" - - local -r openssl_tmpdir="$(mktemp -dt tink-openssl.XXXXXX)" - ( - cd "${openssl_tmpdir}" - curl -OLsS "${openssl_url}" - echo "${openssl_sha256} ${openssl_archive}" | sha256sum -c - - tar xzf "${openssl_archive}" - cd "${openssl_name}" - ./config --prefix="${openssl_tmpdir}" --openssldir="${openssl_tmpdir}" - make -j"$(nproc)" - make install - ) - export OPENSSL_ROOT_DIR="${openssl_tmpdir}" - export PATH="${openssl_tmpdir}/bin:${PATH}" -} - -####################################### -# Install Abeseil into a temporary folder. -# -# Globals: -# ABSEIL_INSTALL_PATH Gets populated with Abseil's installation path. -# Arguments: -# abseil_commit Abseils commit to lookup. -####################################### -install_abseil() { - local -r abseil_commit="$1" - local -r abseil_tmpdir="$(mktemp -dt tink-abseil.XXXXXX)" - local -r abseil_install_dir="${abseil_tmpdir}/install" - ( - cd "${abseil_tmpdir}" - mkdir "install" - curl -OLsS "https://github.com/abseil/abseil-cpp/archive/${abseil_commit}.zip" - unzip "${abseil_commit}.zip" && cd "abseil-cpp-${abseil_commit}" - mkdir build && cd build - cmake .. \ - -DCMAKE_INSTALL_PREFIX="${abseil_install_dir}" \ - -DCMAKE_CXX_STANDARD="${TINK_USE_CXX_STANDARD}" - cmake --build . --target install - ) - export ABSEIL_INSTALL_PATH="${abseil_install_dir}" -} - - -####################################### -# Builds the hello world project -# -# Globals: -# USE_OPENSSL if "true" install OpenSSL and build against it. -# USE_INSTALLED_ABSEIL if "true" install Abseil and build against it. -# Arguments: -# None -####################################### -build_hello_world() { - local cmake_parameters=( - -DCMAKE_CXX_STANDARD="${TINK_USE_CXX_STANDARD}" - ) - if [[ "${USE_OPENSSL}" == "true" ]]; then - # Install OpenSSL in a temporary directory. - install_openssl "1.1.1l" - cmake_parameters+=( -DTINK_USE_SYSTEM_OPENSSL=ON ) - fi - if [[ "${USE_INSTALLED_ABSEIL}" == "true" ]]; then - # Commit from 2021-12-03 - install_abseil "9336be04a242237cd41a525bedfcf3be1bb55377" - cmake_parameters+=( -DCMAKE_PREFIX_PATH="${ABSEIL_INSTALL_PATH}" ) - cmake_parameters+=( -DTINK_USE_INSTALLED_ABSEIL=ON ) - fi - readonly cmake_parameters - ( - mkdir build && cd build - cmake --version - cmake .. "${cmake_parameters[@]}" - make -j"$(nproc)" - ) -} - -main() { - # Create necessary directories, and create a symlink to Tink in the - # "my_project" directory. - mkdir -p "${XDG_CACHE_HOME}" - mkdir -p "${PROJECT_DIR}" "${PROJECT_DIR}/third_party" - ln -s "${TEST_SRCDIR}/tink" "${PROJECT_DIR}/third_party/tink" - - # Copy "my_project" files. - cp "${HELLO_WORLD_SRC}" "${KEYSET_FILE}" "${PROJECT_DIR}" - cp "${CMAKE_LISTS_FILE}" "${PROJECT_DIR}/CMakeLists.txt" - - # Move into the newly populated project directory. - cd "${PROJECT_DIR}" - - # Build the project. This will produce ./build/hello_world. - build_hello_world - - # Create a plaintext. - echo "This is some message to be encrypted." > "${PLAINTEXT_FILE}" - - # Run encryption & decryption. - ./build/hello_world \ - "${KEYSET_FILE}" \ - encrypt \ - "${PLAINTEXT_FILE}" \ - "${AAD_TEXT}" \ - "${CIPHERTEXT_FILE}" - - ./build/hello_world \ - "${KEYSET_FILE}" \ - decrypt \ - "${CIPHERTEXT_FILE}" \ - "${AAD_TEXT}" \ - "${DECRYPTED_FILE}" - - # Check that decryption is correct. - diff -q "${DECRYPTED_FILE}" "${PLAINTEXT_FILE}" - if [ $? -ne 0 ]; then - echo "--- Failure: the decrypted file differs from the original plaintext." - diff "${DECRYPTED_FILE}" "${PLAINTEXT_FILE}" - exit 1 - fi - echo "+++ Success: decryption was correct." -} - -main
diff --git a/cc/examples/helloworld/hello_world.cc b/cc/examples/helloworld/hello_world.cc deleted file mode 100644 index 6653b2c..0000000 --- a/cc/examples/helloworld/hello_world.cc +++ /dev/null
@@ -1,187 +0,0 @@ -// Copyright 2018 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. -// -/////////////////////////////////////////////////////////////////////////////// - -// A command-line utility for testing Tink-primitives. -// It requires 5 arguments: -// keyset-file: name of the file with the keyset to be used for encryption -// operation: the actual AEAD-operation, i.e. "encrypt" or "decrypt" -// input-file: name of the file with input (plaintext for encryption, or -// or ciphertext for decryption) -// associated-data: a string to be used as assciated data -// output-file: name of the file for the resulting output - -#include <fstream> -#include <iostream> -#include <sstream> -#include <string> -#include <utility> - -#include "tink/aead.h" -#include "tink/cleartext_keyset_handle.h" -#include "tink/config.h" -#include "tink/config/tink_config.h" -#include "tink/json_keyset_reader.h" -#include "tink/keyset_handle.h" -#include "tink/keyset_reader.h" - -namespace { - -// Helper functions. -// Upon failure each function writes an error message, and terminates. - -// Initializes Tink. -void InitTink() { - std::clog << "Initializing Tink...\n"; - auto status = crypto::tink::TinkConfig::Register(); - if (!status.ok()) { - std::clog << "Initialization of Tink failed: " << status.message() - << std::endl; - exit(1); - } -} - -// Creates a KeysetReader that reads a JSON-formatted keyset -// from the given file. -std::unique_ptr<crypto::tink::KeysetReader> GetJsonKeysetReader( - const std::string& filename) { - std::clog << "Creating a JsonKeysetReader...\n"; - std::unique_ptr<std::ifstream> keyset_stream(new std::ifstream()); - keyset_stream->open(filename, std::ifstream::in); - auto keyset_reader_result = - crypto::tink::JsonKeysetReader::New(std::move(keyset_stream)); - if (!keyset_reader_result.ok()) { - std::clog << "Creation of the reader failed: " - << keyset_reader_result.status().message() << std::endl; - exit(1); - } - return std::move(keyset_reader_result.value()); -} - -// Creates a KeysetHandle that for a keyset read from the given file, -// which is expected to contain a JSON-formatted keyset. -std::unique_ptr<crypto::tink::KeysetHandle> ReadKeyset( - const std::string& filename) { - auto keyset_reader = GetJsonKeysetReader(filename); - auto keyset_handle_result = - crypto::tink::CleartextKeysetHandle::Read(std::move(keyset_reader)); - if (!keyset_handle_result.ok()) { - std::clog << "Reading the keyset failed: " - << keyset_handle_result.status().message() << std::endl; - exit(1); - } - return std::move(keyset_handle_result.value()); -} - -// Reads the specified file and returns the read content as a string. -std::string Read(const std::string& filename) { - std::clog << "Reading the input...\n"; - std::ifstream input_stream; - input_stream.open(filename, std::ifstream::in); - if (!input_stream.is_open()) { - std::clog << "Error opening input file " << filename << std::endl; - exit(1); - } - std::stringstream input; - input << input_stream.rdbuf(); - input_stream.close(); - return input.str(); -} - -// Writes the given string to the specified file. -void Write(const std::string& output, const std::string& filename) { - std::clog << "Writing the output...\n"; - std::ofstream output_stream(filename, - std::ofstream::out | std::ofstream::binary); - if (!output_stream.is_open()) { - std::clog << "Error opening output file " << filename << std::endl; - exit(1); - } - output_stream << output; - output_stream.close(); -} - -} // namespace - -int main(int argc, char** argv) { - if (argc != 6) { - std::clog << "Usage: " << argv[0] - << " keyset-file operation input-file associated-data output-file\n"; - exit(1); - } - - std::string keyset_filename(argv[1]); - std::string operation(argv[2]); - std::string input_filename(argv[3]); - std::string associated_data(argv[4]); - std::string output_filename(argv[5]); - if (!(operation == "encrypt" || operation == "decrypt")) { - std::clog << "Unknown operation '" << operation << "'.\n" - << "Expected 'encrypt' or 'decrypt'.\n"; - exit(1); - } - std::clog << "Using keyset from file " << keyset_filename - << " to AEAD-" << operation - << " file "<< input_filename - << " with associated data '" << associated_data << "'.\n" - << "The resulting output will be written to file " - << output_filename << std::endl; - - // Init Tink; - InitTink(); - - // Read the keyset. - auto keyset_handle = ReadKeyset(keyset_filename); - - // Get the primitive. - auto primitive_result = keyset_handle->GetPrimitive<crypto::tink::Aead>(); - if (!primitive_result.ok()) { - std::clog << "Getting AEAD-primitive from the factory failed: " - << primitive_result.status().message() << std::endl; - exit(1); - } - std::unique_ptr<crypto::tink::Aead> aead = - std::move(primitive_result.value()); - - // Read the input. - std::string input = Read(input_filename); - - // Compute the output. - std::clog << operation << "ing...\n"; - std::string output; - if (operation == "encrypt") { - auto encrypt_result = aead->Encrypt(input, associated_data); - if (!encrypt_result.ok()) { - std::clog << "Error while encrypting the input:" - << encrypt_result.status().message() << std::endl; - exit(1); - } - output = encrypt_result.value(); - } else { // operation == "decrypt" - auto decrypt_result = aead->Decrypt(input, associated_data); - if (!decrypt_result.ok()) { - std::clog << "Error while decrypting the input:" - << decrypt_result.status().message() << std::endl; - exit(1); - } - output = decrypt_result.value(); - } - - // Write the output to the output file. - Write(output, output_filename); - - std::clog << "All done.\n"; - return 0; -}
diff --git a/cc/examples/helloworld/hello_world_test.sh b/cc/examples/helloworld/hello_world_test.sh deleted file mode 100755 index aaf5dfb..0000000 --- a/cc/examples/helloworld/hello_world_test.sh +++ /dev/null
@@ -1,44 +0,0 @@ -# Copyright 2020 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. -################################################################################ - -#!/bin/bash - -############################################################################# -##### Tests for hello_world binary. - -HELLO_WORLD_CLI="$1" -KEYSET_FILE="$2" -PLAINTEXT_FILE="$TEST_TMPDIR/example_plaintext.txt" -CIPHERTEXT_FILE="$TEST_TMPDIR/ciphertext.bin" -DECRYPTED_FILE="$TEST_TMPDIR/decrypted.txt" -AAD_TEXT="some associated data" - -############################################################################# - -##### Create a plaintext. -echo "This is some message to be encrypted." > $PLAINTEXT_FILE - -##### Run encryption & decryption. -$HELLO_WORLD_CLI $KEYSET_FILE encrypt $PLAINTEXT_FILE "$AAD_TEXT" $CIPHERTEXT_FILE -$HELLO_WORLD_CLI $KEYSET_FILE decrypt $CIPHERTEXT_FILE "$AAD_TEXT" $DECRYPTED_FILE - -##### Check that decryption is correct. -diff -q $DECRYPTED_FILE $PLAINTEXT_FILE -if [ $? -ne 0 ]; then - echo "--- Failure: the decrypted file differs from the original plaintext." - diff $DECRYPTED_FILE $PLAINTEXT_FILE - exit 1 -fi -echo "+++ Success: decryption was correct."
diff --git a/cc/examples/hybrid_encryption/BUILD.bazel b/cc/examples/hybrid_encryption/BUILD.bazel index 39c43b7..dc2f7c7 100644 --- a/cc/examples/hybrid_encryption/BUILD.bazel +++ b/cc/examples/hybrid_encryption/BUILD.bazel
@@ -2,69 +2,21 @@ licenses(["notice"]) -cc_library( - name = "util", - srcs = ["util.cc"], - hdrs = ["util.h"], - deps = [ - "@tink_cc//:binary_keyset_reader", - "@tink_cc//:binary_keyset_writer", - "@tink_cc//:cleartext_keyset_handle", - "@tink_cc//:keyset_handle", - "@tink_cc//:keyset_reader", - "@tink_cc//:keyset_writer", - "@tink_cc//config:tink_config", - "@tink_cc//util:status", - ], -) - -cc_binary( - name = "hybrid_encryption_cli", - srcs = ["hybrid_encryption_cli.cc"], - deps = [ - ":util", - "@tink_cc//:hybrid_decrypt", - "@tink_cc//:hybrid_encrypt", - "@tink_cc//hybrid:hybrid_key_templates", - ], -) - -sh_test( - name = "hybrid_encryption_cli_test", - size = "small", - srcs = ["hybrid_encryption_cli_test.sh"], - args = [ - "$(rootpath :hybrid_encryption_cli)", - ], - data = [":hybrid_encryption_cli"], -) - -filegroup( - name = "hybrid_test_keyset", - srcs = [ - "hybrid_test_private_keyset.json", - "hybrid_test_public_keyset.json", - ], -) - cc_binary( name = "hybrid_cli", srcs = ["hybrid_cli.cc"], - data = [":hybrid_test_keyset"], deps = [ + "//util", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", - "@com_google_absl//absl/memory", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", - "@tink_cc//:cleartext_keyset_handle", "@tink_cc//:hybrid_decrypt", "@tink_cc//:hybrid_encrypt", - "@tink_cc//:json_keyset_reader", "@tink_cc//:keyset_handle", - "@tink_cc//:keyset_reader", - "@tink_cc//config:tink_config", "@tink_cc//hybrid:hpke_config", + "@tink_cc//hybrid:hybrid_config", "@tink_cc//util:status", ], ) @@ -75,10 +27,10 @@ srcs = ["hybrid_cli_test.sh"], args = [ "$(rootpath :hybrid_cli)", - "$(rootpaths :hybrid_test_keyset)", + "$(rootpaths //hybrid_encryption/testdata:hpke_test_keyset)", ], data = [ ":hybrid_cli", - ":hybrid_test_keyset", + "//hybrid_encryption/testdata:hpke_test_keyset", ], )
diff --git a/cc/examples/hybrid_encryption/CMakeLists.txt b/cc/examples/hybrid_encryption/CMakeLists.txt new file mode 100644 index 0000000..99b6157 --- /dev/null +++ b/cc/examples/hybrid_encryption/CMakeLists.txt
@@ -0,0 +1,20 @@ +add_executable(hybrid_cli hybrid_cli.cc) +target_include_directories(hybrid_cli PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(hybrid_cli + tink::static + absl::check + absl::flags_parse + util) +# Tink CMake's configuration doesn't expose tink::core::hpke_config. Remove +# HPKE from this example when building with CMake. +target_compile_definitions(hybrid_cli PRIVATE TINK_EXAMPLES_EXCLUDE_HPKE) + +add_test( + NAME hybrid_cli_test + COMMAND "${BASH_PROGRAM}" + "${CMAKE_CURRENT_SOURCE_DIR}/hybrid_cli_test.sh" + "${CMAKE_CURRENT_BINARY_DIR}/hybrid_cli" + "${CMAKE_CURRENT_SOURCE_DIR}/testdata/hybrid_test_private_keyset.json" + "${CMAKE_CURRENT_SOURCE_DIR}/testdata/hybrid_test_public_keyset.json")
diff --git a/cc/examples/hybrid_encryption/README.md b/cc/examples/hybrid_encryption/README.md deleted file mode 100644 index b72cb9d..0000000 --- a/cc/examples/hybrid_encryption/README.md +++ /dev/null
@@ -1,27 +0,0 @@ -# C++ Hybrid Encryption CLI - -This is a command-line tool that can generate -[Hybrid Encryption](../../../docs/PRIMITIVES.md#hybrid_encryption) keys, and -encrypt and decrypt using Hybrid encryption. - -It demonstrates the basic steps of using Tink, namely loading key material, -obtaining a primitive, and using the primitive to do crypto. - -## Build and Run - -### Bazel - -```shell -git clone https://github.com/google/tink -cd tink/cc/examples -bazel build ... -echo "a message" > message.txt -echo "context" > context_info.txt -./bazel-bin/hybrid_encryption/hybrid_encryption_cli gen-private-key private_keyset.bin -./bazel-bin/hybrid_encryption/hybrid_encryption_cli get-public-key private_keyset.bin \ - public_keyset.bin -./bazel-bin/hybrid_encryption/hybrid_encryption_cli encrypt public_keyset.bin \ - message.txt context_info.txt encrypted_message.bin -./bazel-bin/hybrid_encryption/hybrid_encryption_cli decrypt private_keyset.bin \ - encrypted_message.bin context_info.txt decrypted_message.txt -```
diff --git a/cc/examples/hybrid_encryption/hybrid_cli.cc b/cc/examples/hybrid_encryption/hybrid_cli.cc index fbf958b..8c4239e 100644 --- a/cc/examples/hybrid_encryption/hybrid_cli.cc +++ b/cc/examples/hybrid_encryption/hybrid_cli.cc
@@ -15,27 +15,23 @@ /////////////////////////////////////////////////////////////////////////////// // [START hybrid-example] // A command-line utility for testing Tink Hybrid Encryption. - -#include <fstream> #include <iostream> #include <memory> -#include <sstream> +#include <ostream> #include <string> -#include <utility> #include "absl/flags/flag.h" #include "absl/flags/parse.h" -#include "absl/memory/memory.h" -#include "absl/status/status.h" -#include "absl/strings/str_cat.h" +#include "absl/log/check.h" #include "absl/strings/string_view.h" -#include "tink/cleartext_keyset_handle.h" +#include "util/util.h" +#ifndef TINK_EXAMPLES_EXCLUDE_HPKE #include "tink/hybrid/hpke_config.h" +#endif +#include "tink/hybrid/hybrid_config.h" #include "tink/hybrid_decrypt.h" #include "tink/hybrid_encrypt.h" -#include "tink/json_keyset_reader.h" #include "tink/keyset_handle.h" -#include "tink/keyset_reader.h" #include "tink/util/status.h" ABSL_FLAG(std::string, keyset_filename, "", "Keyset file in JSON format"); @@ -47,163 +43,107 @@ namespace { -using ::crypto::tink::CleartextKeysetHandle; using ::crypto::tink::HybridDecrypt; using ::crypto::tink::HybridEncrypt; -using ::crypto::tink::JsonKeysetReader; using ::crypto::tink::KeysetHandle; -using ::crypto::tink::KeysetReader; using ::crypto::tink::util::Status; using ::crypto::tink::util::StatusOr; constexpr absl::string_view kEncrypt = "encrypt"; constexpr absl::string_view kDecrypt = "decrypt"; -// Creates a KeysetReader that reads a JSON-formatted keyset -// from the given file. -StatusOr<std::unique_ptr<KeysetReader>> GetJsonKeysetReader( - const std::string& filename) { - std::clog << "Creating a JsonKeysetReader...\n"; - auto key_input_stream = absl::make_unique<std::ifstream>(); - key_input_stream->open(filename, std::ifstream::in); - return JsonKeysetReader::New(std::move(key_input_stream)); -} - -// Creates a KeysetHandle that for a keyset read from the given file, -// which is expected to contain a JSON-formatted keyset. -StatusOr<std::unique_ptr<KeysetHandle>> ReadKeyset( - const std::string& filename) { - StatusOr<std::unique_ptr<KeysetReader>> keyset_reader = - GetJsonKeysetReader(filename); - if (!keyset_reader.ok()) { - return keyset_reader.status(); - } - return CleartextKeysetHandle::Read(*std::move(keyset_reader)); -} - -// Reads `filename` and returns the read content as a string, or an error status -// if the file does not exist. -StatusOr<std::string> Read(const std::string& filename) { - std::clog << "Reading the input...\n"; - std::ifstream input_stream; - input_stream.open(filename, std::ifstream::in); - if (!input_stream.is_open()) { - return Status(absl::StatusCode::kInternal, - absl::StrCat("Error opening input file ", filename)); - } - std::stringstream input; - input << input_stream.rdbuf(); - return input.str(); -} - -// Writes the given `data_to_write` to the specified file `filename`. -Status Write(const std::string& data_to_write, const std::string& filename) { - std::clog << "Writing the output...\n"; - std::ofstream output_stream(filename, - std::ofstream::out | std::ofstream::binary); - if (!output_stream.is_open()) { - return Status(absl::StatusCode::kInternal, - absl::StrCat("Error opening output file ", filename)); - } - output_stream << data_to_write; - return crypto::tink::util::OkStatus(); +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 { + +Status HybridCli(absl::string_view mode, const std::string& keyset_filename, + const std::string& input_filename, + const std::string& output_filename, + absl::string_view context_info) { + Status result = crypto::tink::HybridConfig::Register(); + if (!result.ok()) return result; +#ifndef TINK_EXAMPLES_EXCLUDE_HPKE + // HPKE isn't supported when using OpenSSL as a backend. + result = crypto::tink::RegisterHpke(); + if (!result.ok()) return result; +#endif + + // Read the keyset from file. + StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = + ReadJsonCleartextKeyset(keyset_filename); + if (!keyset_handle.ok()) return keyset_handle.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) { + // Get the hybrid encryption primitive. + StatusOr<std::unique_ptr<HybridEncrypt>> hybrid_encrypt_primitive = + (*keyset_handle)->GetPrimitive<HybridEncrypt>(); + if (!hybrid_encrypt_primitive.ok()) { + return hybrid_encrypt_primitive.status(); + } + // Generate the ciphertext. + StatusOr<std::string> encrypt_result = + (*hybrid_encrypt_primitive)->Encrypt(*input_file_content, context_info); + if (!encrypt_result.ok()) return encrypt_result.status(); + output = encrypt_result.value(); + } else { // operation == kDecrypt. + // Get the hybrid decryption primitive. + StatusOr<std::unique_ptr<HybridDecrypt>> hybrid_decrypt_primitive = + (*keyset_handle)->GetPrimitive<HybridDecrypt>(); + if (!hybrid_decrypt_primitive.ok()) { + return hybrid_decrypt_primitive.status(); + } + // Recover the plaintext. + StatusOr<std::string> decrypt_result = + (*hybrid_decrypt_primitive)->Decrypt(*input_file_content, context_info); + if (!decrypt_result.ok()) return decrypt_result.status(); + output = decrypt_result.value(); + } + + // Write the output to the output 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 context_info = absl::GetFlag(FLAGS_context_info); - if (mode.empty()) { - std::cerr << "Mode must be specified with --mode=<" << kEncrypt << "|" - << kDecrypt << ">." << std::endl; - exit(1); - } - - if (mode != kEncrypt && mode != kDecrypt) { - std::cerr << "Unknown mode '" << mode << "'; " - << "Expected either " << kEncrypt << " or " << kDecrypt << "." - << std::endl; - exit(1); - } std::clog << "Using keyset from file " << keyset_filename << " to hybrid " << mode << " file " << input_filename << " with context info '" << context_info << "'." << std::endl; std::clog << "The resulting output will be written to " << output_filename << std::endl; - Status result = crypto::tink::RegisterHpke(); - if (!result.ok()) { - std::cerr << result.message() << std::endl; - exit(1); - } - - // Read the keyset from file. - StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = - ReadKeyset(keyset_filename); - if (!keyset_handle.ok()) { - std::cerr << keyset_handle.status().message() << std::endl; - exit(1); - } - - // Read the input. - StatusOr<std::string> input_file_content = Read(input_filename); - if (!input_file_content.ok()) { - std::cerr << input_file_content.status().message() << std::endl; - exit(1); - } - - // Compute the output. - std::clog << mode << "ing...\n"; - std::string output; - if (mode == kEncrypt) { - // Get the hybrid encryption primitive. - StatusOr<std::unique_ptr<HybridEncrypt>> hybrid_encrypt_primitive = - (*keyset_handle)->GetPrimitive<HybridEncrypt>(); - if (!hybrid_encrypt_primitive.ok()) { - std::cerr << hybrid_encrypt_primitive.status().message() << std::endl; - exit(1); - } - // Generate the ciphertext. - StatusOr<std::string> encrypt_result = - (*hybrid_encrypt_primitive)->Encrypt(*input_file_content, context_info); - if (!encrypt_result.ok()) { - std::cerr << encrypt_result.status().message() << std::endl; - exit(1); - } - output = encrypt_result.value(); - } else { // operation == kDecrypt. - // Get the hybrid decryption primitive. - StatusOr<std::unique_ptr<HybridDecrypt>> hybrid_decrypt_primitive = - (*keyset_handle)->GetPrimitive<HybridDecrypt>(); - if (!hybrid_decrypt_primitive.ok()) { - std::cerr << hybrid_decrypt_primitive.status().message() << std::endl; - exit(1); - } - // Recover the plaintext. - StatusOr<std::string> decrypt_result = - (*hybrid_decrypt_primitive)->Decrypt(*input_file_content, context_info); - if (!decrypt_result.ok()) { - std::cerr << decrypt_result.status().message() << std::endl; - exit(1); - } - output = decrypt_result.value(); - } - - // Write the output to the output file. - Status write_result = Write(output, output_filename); - if (!write_result.ok()) { - std::cerr << write_result.message() << std::endl; - exit(1); - } - - std::clog << "All done." << std::endl; + CHECK_OK(tink_cc_examples::HybridCli(mode, keyset_filename, input_filename, + output_filename, context_info)); return 0; } // [END hybrid-example]
diff --git a/cc/examples/hybrid_encryption/hybrid_cli_test.sh b/cc/examples/hybrid_encryption/hybrid_cli_test.sh index 22142c7..1909902 100755 --- a/cc/examples/hybrid_encryption/hybrid_cli_test.sh +++ b/cc/examples/hybrid_encryption/hybrid_cli_test.sh
@@ -20,6 +20,8 @@ # Tests for Tink C++ hybrid encryption example. ############################################################################# +: "${TEST_TMPDIR:=$(mktemp -d)}" + readonly CLI="$1" readonly PRIVATE_KEYSET_FILE="$2" readonly PUBLIC_KEYSET_FILE="$3" @@ -45,7 +47,7 @@ } ####################################### -# Asserts that the outcome of the latest test command was the expected one. +# Asserts that the outcome of the latest test command is 0. # # If not, it terminates the test execution. # @@ -53,23 +55,29 @@ # TEST_STATUS # TEST_NAME # TEST_CASE -# Arguments: -# expected_outcome: The expected outcome. ####################################### -_assert_test_command_outcome() { - expected_outcome="$1" - if (( TEST_STATUS != expected_outcome )); then - echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" - exit 1 +assert_command_succeeded() { + if (( TEST_STATUS != 0 )); then + echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" + exit 1 fi } -assert_command_succeeded() { - _assert_test_command_outcome 0 -} - +####################################### +# 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() { - _assert_test_command_outcome 1 + if (( TEST_STATUS == 0 )); then + echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" + exit 1 + fi } #######################################
diff --git a/cc/examples/hybrid_encryption/hybrid_encryption_cli.cc b/cc/examples/hybrid_encryption/hybrid_encryption_cli.cc deleted file mode 100644 index 36dd752..0000000 --- a/cc/examples/hybrid_encryption/hybrid_encryption_cli.cc +++ /dev/null
@@ -1,266 +0,0 @@ -// Copyright 2020 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. -// -/////////////////////////////////////////////////////////////////////////////// - -// A command-line utility for generating Hybrid Encryption keys, and encrypting -// and decrypting files using hybrid encryption. -// -// The first argument is the operation and it should be one of the following: -// gen-private-key get-public-key encrypt decrypt. -// Additional arguments depend on the operation. -// -// gen-private-key -// Generates a new private keyset using the RsaSsaPkcs13072Sha256F4 template. -// It requires 1 additional argument: -// output-file: name of the file for the resulting output -// -// get-public-key -// Extracts a public keyset associated with the given private keyset. -// It requires 2 additional arguments: -// private-keyset-file: name of the file with the private keyset -// output-file: name of the file for the resulting output -// -// encrypt -// Encrypts the message using the given public keyset. -// It requires 4 additional arguments: -// public-keyset-file: name of the file with the public keyset -// message-file: name of the file with the message -// context-info-file: name of the file with the context-info, -// can also be an empty file -// output-file: name of the file for the resulting output -// -// decrypt -// Decrypts the encrypted message using the given private keyset. -// It requires 4 additional arguments: -// private-keyset-file: name of the file with the private keyset -// encrypted-message-file: name of the file with the message -// context-info-file: name of the file with the context-info -// output-file: name of the file for the decrypted message - -#include <iostream> -#include <string> -#include <utility> - -#include "tink/hybrid/hybrid_key_templates.h" -#include "tink/hybrid_decrypt.h" -#include "tink/hybrid_encrypt.h" -#include "hybrid_encryption/util.h" - -// Prints usage info. -void PrintUsageInfo() { - std::clog << "Usage: operation arguments\n" - << "where operation is one of the following:\n" - << " gen-private-key get-public-key encrypt decrypt\n" - << "and, depending on the operation, arguments are:\n" - << " gen-private-key: output-file\n" - << " get-public-key: private-keyset-file output-file\n" - << " encrypt: public-keyset-file message-file context-info-file" - << " output-file\n" - << " decrypt: private-keyset-file encrypted-file context-info-file" - << " output-file" - << std::endl; -} - -// Generates a new private keyset using the EciesP256HkdfHmacSha256Aes128Gcm -// template and writes it to the output file. -void GeneratePrivateKey(const std::string& output_filename) { - std::clog << "Generating a new private keyset.." << std::endl; - - auto key_template = - crypto::tink::HybridKeyTemplates::EciesP256HkdfHmacSha256Aes128Gcm(); - auto new_keyset_handle_result = - crypto::tink::KeysetHandle::GenerateNew(key_template); - if (!new_keyset_handle_result.ok()) { - std::clog << "Generating new keyset failed: " - << new_keyset_handle_result.status().message() << std::endl; - exit(1); - } - auto keyset_handle = std::move(new_keyset_handle_result.value()); - - std::clog << "Writing the keyset to file " << output_filename << "..." - << std::endl; - - Util::WriteKeyset(keyset_handle, output_filename); -} - -// Extracts a public keyset associated with the given private keyset -// and writes it to the output file. -void ExtractPublicKey(const std::string& private_keyset_filename, - const std::string& output_filename) { - std::clog << "Extracting a public keyset associated with the private " - << "keyset from file " << private_keyset_filename << "..." - << std::endl; - - auto private_keyset_handle = Util::ReadKeyset(private_keyset_filename); - - auto new_keyset_handle_result = - private_keyset_handle->GetPublicKeysetHandle(); - if (!new_keyset_handle_result.ok()) { - std::clog << "Getting the keyset failed: " - << new_keyset_handle_result.status().message() << std::endl; - exit(1); - } - auto public_keyset_handle = std::move(new_keyset_handle_result.value()); - - std::clog << "Writing the keyset to file " << output_filename << "..." - << std::endl; - - Util::WriteKeyset(public_keyset_handle, output_filename); -} - -// Encrypts the message using the given public keyset -// and writes the encrypted message to the output file. -void Encrypt(const std::string& keyset_filename, - const std::string& message_filename, - const std::string& context_info_filename, - const std::string& output_filename) { - auto keyset_handle = Util::ReadKeyset(keyset_filename); - - auto primitive_result = - keyset_handle->GetPrimitive<crypto::tink::HybridEncrypt>(); - if (!primitive_result.ok()) { - std::clog << "Getting HybridEncryption-primitive from the factory failed: " - << primitive_result.status().message() << std::endl; - exit(1); - } - auto hybrid_encrypt = std::move(primitive_result.value()); - - std::clog << "Encrypting message from file " << message_filename - << " using public keyset from file " << keyset_filename << "..." - << std::endl; - - std::string message = Util::Read(message_filename); - std::string context_info = Util::Read(context_info_filename); - - auto encrypt_result = hybrid_encrypt->Encrypt(message, context_info); - if (!encrypt_result.ok()) { - std::clog << "Error while encrypting the message: " - << encrypt_result.status().message() << std::endl; - exit(1); - } - std::string encrypted_message = encrypt_result.value(); - - std::clog << "Writing the resulting encrypted message to file " - << output_filename << "..." << std::endl; - - Util::Write(encrypted_message, output_filename); -} - -// Decrypts the encrypted message using the given private keyset -// and writes the result to the output file. -void Decrypt(const std::string& keyset_filename, - const std::string& message_filename, - const std::string& context_info_filename, - const std::string& output_filename) { - auto keyset_handle = Util::ReadKeyset(keyset_filename); - - auto primitive_result = - keyset_handle->GetPrimitive<crypto::tink::HybridDecrypt>(); - if (!primitive_result.ok()) { - std::clog << "Getting HybridDecrypt-primitive from the factory " - << "failed: " << primitive_result.status().message() << std::endl; - exit(1); - } - auto hybrid_decrypt = std::move(primitive_result.value()); - - std::clog << "Decrypting the encrypted file " << message_filename - << " to the file " << output_filename - << " using private keyset from file " << keyset_filename << "..." - << std::endl; - - std::string message = Util::Read(message_filename); - std::string context_info = Util::Read(context_info_filename); - - std::string result; - auto decrypt_status = hybrid_decrypt->Decrypt(message, context_info); - if (!decrypt_status.ok()) { - std::clog << "Error while decrypting the file: " - << decrypt_status.status().message() << std::endl; - exit(1); - } - - std::string decrypted_message = decrypt_status.value(); - - std::clog << "Writing the resulting decrypted message to file " - << output_filename << "..." << std::endl; - - Util::Write(decrypted_message, output_filename); -} - -int main(int argc, char** argv) { - if (argc == 1) { - PrintUsageInfo(); - exit(1); - } - - Util::InitTink(); - - std::string operation = argv[1]; - - if (operation == "gen-private-key") { - if (argc != 3) { - PrintUsageInfo(); - exit(1); - } - - std::string output_filename = argv[2]; - - GeneratePrivateKey(output_filename); - } else if (operation == "get-public-key") { - if (argc != 4) { - PrintUsageInfo(); - exit(1); - } - - std::string private_keyset_filename = argv[2]; - std::string output_filename = argv[3]; - - ExtractPublicKey(private_keyset_filename, output_filename); - } else if (operation == "encrypt") { - if (argc != 6) { - PrintUsageInfo(); - exit(1); - } - - std::string keyset_filename = argv[2]; - std::string message_filename = argv[3]; - std::string context_info_filename = argv[4]; - std::string output_filename = argv[5]; - - Encrypt(keyset_filename, message_filename, context_info_filename, - output_filename); - } else if (operation == "decrypt") { - if (argc != 6) { - PrintUsageInfo(); - exit(1); - } - - std::string keyset_filename = argv[2]; - std::string message_filename = argv[3]; - std::string context_info_filename = argv[4]; - std::string output_filename = argv[5]; - - Decrypt(keyset_filename, message_filename, context_info_filename, - output_filename); - } else { - std::clog << "Unknown operation. Supported operations are: " - << "gen-private-key get-public-key encrypt decrypt" << std::endl; - exit(1); - } - - std::clog << "Done!" << std::endl; - - return 0; -}
diff --git a/cc/examples/hybrid_encryption/hybrid_encryption_cli_test.sh b/cc/examples/hybrid_encryption/hybrid_encryption_cli_test.sh deleted file mode 100755 index d60d681..0000000 --- a/cc/examples/hybrid_encryption/hybrid_encryption_cli_test.sh +++ /dev/null
@@ -1,131 +0,0 @@ -#!/bin/bash -# Copyright 2020 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. -################################################################################ - - -############################################################################# -#### Tests for hybrid_encrypt_cli binary. - -HYBRID_CLI="$1" - -PRIVATE_KEYSET_FILE="$TEST_TMPDIR/private_keyset.bin" -PUBLIC_KEYSET_FILE="$TEST_TMPDIR/public_keyset.bin" -MESSAGE_FILE="$TEST_TMPDIR/message.txt" -CONTEXT_INFO_FILE="$TEST_TMPDIR/context_info.txt" -ENCRYPTED_MESSAGE_FILE="$TEST_TMPDIR/encrypted_message.bin" -DECRYPTED_MESSAGE_FILE="$TEST_TMPDIR/decrypted_message.txt" -RESULT_FILE="$TEST_TMPDIR/result.txt" - -OTHER_PRIVATE_KEYSET_FILE="$TEST_TMPDIR/other_private_keyset.bin" -OTHER_PUBLIC_KEYSET_FILE="$TEST_TMPDIR/other_public_keyset.bin" -OTHER_MESSAGE_FILE="$TEST_TMPDIR/other_message.txt" - -echo "This is a message." > $MESSAGE_FILE -echo "This is a different message." > $OTHER_MESSAGE_FILE -echo "context" > $CONTEXT_INFO_FILE -echo "different context" > $OTHER_CONTEXT_INFO_FILE - -############################################################################# -#### Helper function that checks if values are equal. - -assert_equal() { - if [ "$1" == "$2" ]; then - echo "+++ Success: values are equal." - else - echo "--- Failure: values are different. Expected: [$1], actual: [$2]." - exit 1 - fi -} - -############################################################################# -#### All good, everything should work. -test_name="all_good" -echo "+++ Starting test $test_name..." - -#### Generate a private key and get a public key. -$HYBRID_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$HYBRID_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 - -#### Encrypt the message. -$HYBRID_CLI encrypt $PUBLIC_KEYSET_FILE $MESSAGE_FILE $CONTEXT_INFO_FILE $ENCRYPTED_MESSAGE_FILE || exit 1 - -#### Decrypt the encrypted message. -$HYBRID_CLI decrypt $PRIVATE_KEYSET_FILE $ENCRYPTED_MESSAGE_FILE $CONTEXT_INFO_FILE $DECRYPTED_MESSAGE_FILE || exit 1 - -#### Check that the decrypted message is same as original message. -DECRYPTED_MESSAGE=$(<$DECRYPTED_MESSAGE_FILE) -ORIGINAL_MESSAGE=$(<$MESSAGE_FILE) -assert_equal "$ORIGINAL_MESSAGE" "$DECRYPTED_MESSAGE" - -############################################################################# -#### Bad private key when getting the public key. -test_name="get_public_key_with_bad_private_key" -echo "+++ Starting test $test_name..." - -echo "abcd" >> $PRIVATE_KEYSET_FILE -$HYBRID_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE - -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE" - -############################################################################# -#### Decrypting a bad encrypted file. -test_name="decrypt_a_bad_file" -echo "+++ Starting test $test_name..." - -$HYBRID_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$HYBRID_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 -$HYBRID_CLI encrypt $PUBLIC_KEYSET_FILE $MESSAGE_FILE $CONTEXT_INFO_FILE $ENCRYPTED_MESSAGE_FILE || exit 1 -$HYBRID_CLI decrypt $PRIVATE_KEYSET_FILE $OTHER_MESSAGE_FILE $CONTEXT_INFO_FILE $RESULT_FILE - -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE" - -############################################################################# -#### Encrypt with wrong key. -test_name="encrypt_with_wrong_key" -echo "+++ Starting test $test_name..." - -$HYBRID_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$HYBRID_CLI encrypt $PRIVATE_KEYSET_FILE $MESSAGE_FILE $ENCRYPTED_MESSAGE_FILE - -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE" - -############################################################################# -#### Decrypt with wrong key. -test_name="decrypt_with_wrong_key" -echo "+++ Starting test $test_name..." - -$HYBRID_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$HYBRID_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 -$HYBRID_CLI encrypt $PUBLIC_KEYSET_FILE $MESSAGE_FILE $CONTEXT_INFO_FILE $ENCRYPTED_MESSAGE_FILE || exit 1 -$HYBRID_CLI decrypt $PUBLIC_KEYSET_FILE $ENCRYPTED_MESSAGE_FILE $CONTEXT_INFO_FILE $RESULT_FILE - -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE" - -############################################################################# -#### Decrypt with different context. -test_name="decrypt_with_different_context" -echo "+++ Starting test $test_name..." - -$HYBRID_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$HYBRID_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 -$HYBRID_CLI encrypt $PUBLIC_KEYSET_FILE $MESSAGE_FILE $CONTEXT_INFO_FILE $ENCRYPTED_MESSAGE_FILE || exit 1 -$HYBRID_CLI decrypt $PRIVATE_KEYSET_FILE $ENCRYPTED_MESSAGE_FILE $OTHER_CONTEXT_INFO_FILE $RESULT_FILE - -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE"
diff --git a/cc/examples/hybrid_encryption/testdata/BUILD.bazel b/cc/examples/hybrid_encryption/testdata/BUILD.bazel new file mode 100644 index 0000000..323479b --- /dev/null +++ b/cc/examples/hybrid_encryption/testdata/BUILD.bazel
@@ -0,0 +1,19 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +filegroup( + name = "hpke_test_keyset", + srcs = [ + "hpke_test_private_keyset.json", + "hpke_test_public_keyset.json", + ], +) + +filegroup( + name = "hybrid_test_keyset", + srcs = [ + "hybrid_test_private_keyset.json", + "hybrid_test_public_keyset.json", + ], +)
diff --git a/cc/examples/hybrid_encryption/hybrid_test_private_keyset.json b/cc/examples/hybrid_encryption/testdata/hpke_test_private_keyset.json similarity index 100% rename from cc/examples/hybrid_encryption/hybrid_test_private_keyset.json rename to cc/examples/hybrid_encryption/testdata/hpke_test_private_keyset.json
diff --git a/cc/examples/hybrid_encryption/hybrid_test_public_keyset.json b/cc/examples/hybrid_encryption/testdata/hpke_test_public_keyset.json similarity index 100% rename from cc/examples/hybrid_encryption/hybrid_test_public_keyset.json rename to cc/examples/hybrid_encryption/testdata/hpke_test_public_keyset.json
diff --git a/cc/examples/hybrid_encryption/testdata/hybrid_test_private_keyset.json b/cc/examples/hybrid_encryption/testdata/hybrid_test_private_keyset.json new file mode 100644 index 0000000..b237360 --- /dev/null +++ b/cc/examples/hybrid_encryption/testdata/hybrid_test_private_keyset.json
@@ -0,0 +1,15 @@ +{ + "primaryKeyId": 548859458, + "key": [ + { + "keyData": { + "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey", + "value": "EowBEkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARohAKjjAxgGmD9j90UyzNunoC04kWqaWiXGFRhOYfLS7Z2tIiEAhqqb+D0Din92zHwGQefzui0hma5khIZQCWyWHHVgNpsaIBQrEEuEn3hClVKM+4bsvmaUOqFYMbl7E6lNFJzbr+lp", + "keyMaterialType": "ASYMMETRIC_PRIVATE" + }, + "status": "ENABLED", + "keyId": 548859458, + "outputPrefixType": "TINK" + } + ] +}
diff --git a/cc/examples/hybrid_encryption/testdata/hybrid_test_public_keyset.json b/cc/examples/hybrid_encryption/testdata/hybrid_test_public_keyset.json new file mode 100644 index 0000000..ddb1095 --- /dev/null +++ b/cc/examples/hybrid_encryption/testdata/hybrid_test_public_keyset.json
@@ -0,0 +1,15 @@ +{ + "primaryKeyId": 548859458, + "key": [ + { + "keyData": { + "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey", + "value": "EkQKBAgCEAMSOhI4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAEYARohAKjjAxgGmD9j90UyzNunoC04kWqaWiXGFRhOYfLS7Z2tIiEAhqqb+D0Din92zHwGQefzui0hma5khIZQCWyWHHVgNps=", + "keyMaterialType": "ASYMMETRIC_PUBLIC" + }, + "status": "ENABLED", + "keyId": 548859458, + "outputPrefixType": "TINK" + } + ] +}
diff --git a/cc/examples/hybrid_encryption/util.cc b/cc/examples/hybrid_encryption/util.cc deleted file mode 100644 index 3a9760f..0000000 --- a/cc/examples/hybrid_encryption/util.cc +++ /dev/null
@@ -1,129 +0,0 @@ -// Copyright 2020 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "hybrid_encryption/util.h" - -#include <fstream> -#include <iostream> -#include <string> -#include <utility> - -#include "tink/binary_keyset_reader.h" -#include "tink/binary_keyset_writer.h" -#include "tink/cleartext_keyset_handle.h" -#include "tink/config.h" -#include "tink/config/tink_config.h" -#include "tink/keyset_handle.h" -#include "tink/keyset_reader.h" -#include "tink/keyset_writer.h" -#include "tink/util/status.h" - -using crypto::tink::BinaryKeysetReader; -using crypto::tink::BinaryKeysetWriter; -using crypto::tink::CleartextKeysetHandle; -using crypto::tink::KeysetHandle; -using crypto::tink::KeysetReader; -using crypto::tink::KeysetWriter; -using crypto::tink::TinkConfig; - -// static -std::unique_ptr<KeysetReader> Util::GetBinaryKeysetReader( - const std::string& filename) { - std::unique_ptr<std::ifstream> keyset_stream(new std::ifstream()); - keyset_stream->open(filename, std::ifstream::in); - auto keyset_reader_result = BinaryKeysetReader::New(std::move(keyset_stream)); - if (!keyset_reader_result.ok()) { - std::clog << "Creation of the BinaryKeysetReader failed: " - << keyset_reader_result.status().message() << std::endl; - exit(1); - } - return std::move(keyset_reader_result.value()); -} - -// static -std::unique_ptr<KeysetWriter> Util::GetBinaryKeysetWriter( - const std::string& filename) { - std::unique_ptr<std::ofstream> keyset_stream(new std::ofstream()); - keyset_stream->open(filename, std::ofstream::out); - auto keyset_writer_result = BinaryKeysetWriter::New(std::move(keyset_stream)); - if (!keyset_writer_result.ok()) { - std::clog << "Creation of the BinaryKeysetWriter failed: " - << keyset_writer_result.status().message() << std::endl; - exit(1); - } - return std::move(keyset_writer_result.value()); -} - -// static -std::unique_ptr<KeysetHandle> Util::ReadKeyset(const std::string& filename) { - auto keyset_reader = GetBinaryKeysetReader(filename); - auto keyset_handle_result = - CleartextKeysetHandle::Read(std::move(keyset_reader)); - if (!keyset_handle_result.ok()) { - std::clog << "Reading the keyset failed: " - << keyset_handle_result.status().message() << std::endl; - exit(1); - } - return std::move(keyset_handle_result.value()); -} - -// static -void Util::WriteKeyset(const std::unique_ptr<KeysetHandle>& keyset_handle, - const std::string& filename) { - auto keyset_writer = GetBinaryKeysetWriter(filename); - auto status = - CleartextKeysetHandle::Write(keyset_writer.get(), *keyset_handle); - if (!status.ok()) { - std::clog << "Writing the keyset failed: " << status.message() << std::endl; - exit(1); - } -} - -// static -void Util::InitTink() { - auto status = TinkConfig::Register(); - if (!status.ok()) { - std::clog << "Initialization of Tink failed: " << status.message() - << std::endl; - exit(1); - } -} - -// static -std::string Util::Read(const std::string& filename) { - std::ifstream input_stream; - input_stream.open(filename, std::ifstream::in); - if (!input_stream.is_open()) { - std::clog << "Error opening input file " << filename << std::endl; - exit(1); - } - std::stringstream input; - input << input_stream.rdbuf(); - input_stream.close(); - return input.str(); -} - -// static -void Util::Write(const std::string& output, const std::string& filename) { - std::ofstream output_stream(filename, - std::ofstream::out | std::ofstream::binary); - if (!output_stream.is_open()) { - std::clog << "Error opening output file " << filename << std::endl; - exit(1); - } - output_stream << output; - output_stream.close(); -}
diff --git a/cc/examples/hybrid_encryption/util.h b/cc/examples/hybrid_encryption/util.h deleted file mode 100644 index 072fb14..0000000 --- a/cc/examples/hybrid_encryption/util.h +++ /dev/null
@@ -1,63 +0,0 @@ -// Copyright 2020 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef TINK_EXAMPLES_HYBRID_ENCRYPTION_UTIL_H_ -#define TINK_EXAMPLES_HYBRID_ENCRYPTION_UTIL_H_ - -#include <fstream> -#include <iostream> -#include <string> - -#include "tink/keyset_handle.h" - -// Helper functions for Digital Signatures CLI -class Util { - public: - // Returns a BinaryKeysetReader that reads from the specified file. - // In case of errors writes a log message and aborts. - static std::unique_ptr<crypto::tink::KeysetReader> GetBinaryKeysetReader( - const std::string& filename); - - // Returns a BinaryKeysetWriter that writes from the specified file. - // In case of errors writes a log message and aborts. - static std::unique_ptr<crypto::tink::KeysetWriter> GetBinaryKeysetWriter( - const std::string& filename); - - // Reads a keyset from the specified file. - // In case of errors writes a log message and aborts. - static std::unique_ptr<crypto::tink::KeysetHandle> ReadKeyset( - const std::string& filename); - - // Writes the keyset to the specified file. - // In case of errors writes a log message and aborts. - static void WriteKeyset( - const std::unique_ptr<crypto::tink::KeysetHandle>& keyset_handle, - const std::string& filename); - - // Initializes Tink registry. - // In case of errors writes a log message and aborts. - static void InitTink(); - - // Reads the specified file and returns the contents as a string. - // In case of errors writes a log message and aborts. - static std::string Read(const std::string& filename); - - // Writes the given 'output' to the specified file. - // In case of errors writes a log message and aborts. - static void Write(const std::string& output, const std::string& filename); -}; - -#endif // TINK_EXAMPLES_HYBRID_ENCRYPTION_UTIL_H_
diff --git a/cc/examples/jwt/BUILD.bazel b/cc/examples/jwt/BUILD.bazel index 5c38728..e35bad5 100644 --- a/cc/examples/jwt/BUILD.bazel +++ b/cc/examples/jwt/BUILD.bazel
@@ -2,19 +2,11 @@ licenses(["notice"]) -cc_library( - name = "util", - srcs = ["util.cc"], - hdrs = ["util.h"], - deps = [ - "@com_google_absl//absl/strings", - "@tink_cc//:binary_keyset_reader", - "@tink_cc//:binary_keyset_writer", - "@tink_cc//:cleartext_keyset_handle", - "@tink_cc//:keyset_handle", - "@tink_cc//:keyset_reader", - "@tink_cc//:keyset_writer", - "@tink_cc//util:status", +filegroup( + name = "jwt_signature_keysets", + srcs = [ + "jwt_signature_private_keyset.json", + "jwt_signature_public_keyset.json", ], ) @@ -22,14 +14,17 @@ name = "jwt_signature_cli", srcs = ["jwt_signature_cli.cc"], deps = [ - ":util", - "@tink_cc//jwt:jwt_key_templates", + "//util", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + "@com_google_absl//absl/log:check", + "@tink_cc//:keyset_handle", "@tink_cc//jwt:jwt_public_key_sign", "@tink_cc//jwt:jwt_public_key_verify", "@tink_cc//jwt:jwt_signature_config", "@tink_cc//jwt:jwt_validator", "@tink_cc//jwt:raw_jwt", - "@tink_cc//jwt:verified_jwt", + "@tink_cc//util:status", ], ) @@ -39,6 +34,10 @@ srcs = ["jwt_signature_cli_test.sh"], args = [ "$(rootpath :jwt_signature_cli)", + "$(rootpaths :jwt_signature_keysets)", ], - data = [":jwt_signature_cli"], + data = [ + ":jwt_signature_cli", + ":jwt_signature_keysets", + ], )
diff --git a/cc/examples/jwt/CMakeLists.txt b/cc/examples/jwt/CMakeLists.txt new file mode 100644 index 0000000..3bced16 --- /dev/null +++ b/cc/examples/jwt/CMakeLists.txt
@@ -0,0 +1,18 @@ +add_executable(jwt_signature_cli jwt_signature_cli.cc) +target_include_directories(jwt_signature_cli PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(jwt_signature_cli + tink::static + tink::jwt::jwt_signature_config + absl::check + absl::flags_parse + util) + +add_test( + NAME jwt_signature_cli_test + COMMAND "${BASH_PROGRAM}" + "${CMAKE_CURRENT_SOURCE_DIR}/jwt_signature_cli_test.sh" + "${CMAKE_CURRENT_BINARY_DIR}/jwt_signature_cli" + "${CMAKE_CURRENT_SOURCE_DIR}/jwt_signature_private_keyset.json" + "${CMAKE_CURRENT_SOURCE_DIR}/jwt_signature_public_keyset.json")
diff --git a/cc/examples/jwt/README.md b/cc/examples/jwt/README.md deleted file mode 100644 index 6867d75..0000000 --- a/cc/examples/jwt/README.md +++ /dev/null
@@ -1,25 +0,0 @@ -# C++ JWT Signatures CLI - -This is a command-line utility for generating JSON Web Token (JWT) keys, and -creating and verifying JWTs. - -It demonstrates the basic steps of using Tink, namely loading key material, -obtaining a primitive, and using the primitive to do crypto. - -## Build and Run - -### Bazel - -```shell -git clone https://github.com/google/tink -cd tink/cc/examples -bazel build ... -./bazel-bin/jwt/jwt_signature_cli gen-private-key private_keyset.bin -./bazel-bin/jwt/jwt_signature_cli get-public-key private_keyset.bin \ - public_keyset.bin -./bazel-bin/jwt/jwt_signature_cli sign private_keyset.bin \ - my-audience token.txt -./bazel-bin/jwt/jwt_signature_cli verify public_keyset.bin \ - my-audience token.txt result.txt -cat result.txt -```
diff --git a/cc/examples/jwt/jwt_signature_cli.cc b/cc/examples/jwt/jwt_signature_cli.cc index 8ffc229..34f6b05 100644 --- a/cc/examples/jwt/jwt_signature_cli.cc +++ b/cc/examples/jwt/jwt_signature_cli.cc
@@ -13,276 +13,131 @@ // limitations under the License. // /////////////////////////////////////////////////////////////////////////////// - -// A command-line utility for generating JSON Web Token (JWT) keys, and creating -// and verifying JWTs. -// -// The first argument is the operation and it should be one of the following: -// gen-private-key get-public-key sign verify. -// Additional arguments depend on the operation. -// -// gen-private-key -// Generates a new private keyset using JwtRs256_2048_F4_Template. -// It requires 1 additional argument: -// output-file: name of the file for the resulting output -// -// get-public-key -// Extracts a public keyset associated with the given private keyset. -// It requires 2 additional arguments: -// private-keyset-file: name of the file with the private keyset -// output-file: name of the file for the resulting output -// -// sign -// Generates and signs a token using the given private keyset. -// It requires 3 additional arguments: -// private-keyset-file: name of the file with the private keyset -// audience: audience claim to be put in the token. -// output-file: name of the file for the resulting output -// -// verify -// Verifies a token using the given public keyset. -// It requires 4 additional arguments: -// public-keyset-file: name of the file with the public keyset -// audience: expected audience in the token -// token-file: name of the file with the token -// output-file: name of the file for the resulting output (valid/invalid) - +// [START jwt-example] +// A utility for creating, signing and verifying JSON Web Tokens (JWT). #include <iostream> +#include <memory> +#include <ostream> #include <string> +#include <utility> -#include "tink/jwt/jwt_key_templates.h" +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/log/check.h" +#include "util/util.h" #include "tink/jwt/jwt_public_key_sign.h" #include "tink/jwt/jwt_public_key_verify.h" #include "tink/jwt/jwt_signature_config.h" #include "tink/jwt/jwt_validator.h" #include "tink/jwt/raw_jwt.h" -#include "tink/jwt/verified_jwt.h" -#include "jwt/util.h" +#include "tink/keyset_handle.h" +#include "tink/util/status.h" -// Prints usage info. -using crypto::tink::JwtPublicKeySign; -using crypto::tink::JwtPublicKeyVerify; -using crypto::tink::JwtValidator; -using crypto::tink::KeysetHandle; -using crypto::tink::RawJwt; -using crypto::tink::RawJwtBuilder; +ABSL_FLAG(std::string, keyset_filename, "", "Keyset file in JSON format"); +ABSL_FLAG(std::string, mode, "", "Mode of operation (sign|verify)"); +ABSL_FLAG(std::string, audience, "", "Expected audience in the token"); +ABSL_FLAG(std::string, token_filename, "", "Path to the token file"); -void PrintUsageInfo() { - std::clog << "Usage: operation arguments\n" - << "where operation is one of the following:\n" - << " gen-private-key get-public-key sign verify\n" - << "and, depending on the operation, arguments are:\n" - << " gen-private-key: output-file\n" - << " get-public-key: private-keyset-file output-file\n" - << " sign: private-keyset-file audience output-file\n" - << " verify: public-keyset-file audience token-file " - << "output-file" << std::endl; +namespace { + +using ::crypto::tink::JwtPublicKeySign; +using ::crypto::tink::JwtPublicKeyVerify; +using ::crypto::tink::JwtValidator; +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::RawJwt; +using ::crypto::tink::RawJwtBuilder; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; + +constexpr absl::string_view kSign = "sign"; +constexpr absl::string_view kVerify = "verify"; + +void ValidateParams() { + // [START_EXCLUDE] + CHECK(absl::GetFlag(FLAGS_mode) == kSign || + absl::GetFlag(FLAGS_mode) == kVerify) + << "Invalid mode; must be `" << kSign << "` or `" << kVerify << "`" + << std::endl; + CHECK(!absl::GetFlag(FLAGS_keyset_filename).empty()) + << "Keyset file must be specified"; + CHECK(!absl::GetFlag(FLAGS_audience).empty()) + << "Expected audience in the token must be specified"; + CHECK(!absl::GetFlag(FLAGS_token_filename).empty()) + << "Token file must be specified"; + // [END_EXCLUDE] } -// Generates a new private keyset using JwtRs256_2048_F4_Template and writes it -// to the output file. -void GeneratePrivateKey(absl::string_view output_filename) { - std::clog << "Generating a new private keyset.." << std::endl; +} // namespace - auto key_template = crypto::tink::JwtRs256_2048_F4_Template(); - crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = - crypto::tink::KeysetHandle::GenerateNew(key_template); - if (!keyset_handle.ok()) { - std::clog << "Generating new keyset failed: " - << keyset_handle.status().message() << std::endl; - exit(1); +namespace tink_cc_examples { + +// JWT example CLI implementation. +Status JwtCli(absl::string_view mode, const std::string& keyset_filename, + absl::string_view audience, const std::string& token_filename) { + Status result = crypto::tink::JwtSignatureRegister(); + if (!result.ok()) return result; + + // Read the keyset from file. + StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = + ReadJsonCleartextKeyset(keyset_filename); + if (!keyset_handle.ok()) return keyset_handle.status(); + + if (mode == kSign) { + StatusOr<RawJwt> raw_jwt = + RawJwtBuilder() + .AddAudience(audience) + .SetExpiration(absl::Now() + absl::Seconds(100)) + .Build(); + if (!raw_jwt.ok()) return raw_jwt.status(); + StatusOr<std::unique_ptr<JwtPublicKeySign>> jwt_signer = + (*keyset_handle)->GetPrimitive<JwtPublicKeySign>(); + if (!jwt_signer.ok()) return jwt_signer.status(); + + StatusOr<std::string> token = (*jwt_signer)->SignAndEncode(*raw_jwt); + if (!token.ok()) return token.status(); + + return WriteToFile(*token, token_filename); + } else { // mode == kVerify + // Read the token. + StatusOr<std::string> token = ReadFile(token_filename); + if (!token.ok()) return token.status(); + + StatusOr<JwtValidator> validator = + crypto::tink::JwtValidatorBuilder().ExpectAudience(audience).Build(); + if (!validator.ok()) return validator.status(); + + StatusOr<std::unique_ptr<JwtPublicKeyVerify>> jwt_verifier = + (*keyset_handle)->GetPrimitive<JwtPublicKeyVerify>(); + if (!jwt_verifier.ok()) return jwt_verifier.status(); + + return (*jwt_verifier)->VerifyAndDecode(*token, *validator).status(); } - std::clog << "Writing the keyset to file " << output_filename - << "..." << std::endl; - - WriteKeyset(**keyset_handle, output_filename); } -// Extracts a public keyset associated with the given private keyset -// and writes it to the output file. -void ExtractPublicKey(absl::string_view private_keyset_filename, - absl::string_view output_filename) { - std::clog << "Extracting a public keyset associated with the private " - << "keyset from file " << private_keyset_filename << "..." - << std::endl; - - std::unique_ptr<crypto::tink::KeysetHandle> private_keyset_handle = - ReadKeyset(private_keyset_filename); - - crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> - public_keyset_handle = private_keyset_handle->GetPublicKeysetHandle(); - if (!public_keyset_handle.ok()) { - std::clog << "Getting the keyset failed: " - << public_keyset_handle.status().message() << std::endl; - exit(1); - } - - std::clog << "Writing the keyset to file " << output_filename - << "..." << std::endl; - - WriteKeyset(**public_keyset_handle, output_filename); -} - -// Creates and signs a token with the given audience claim using the given -// private keyset and writes the signature to the output file. -void Sign(absl::string_view keyset_filename, absl::string_view audience, - absl::string_view output_filename) { - std::unique_ptr<crypto::tink::KeysetHandle> keyset_handle = - ReadKeyset(keyset_filename); - - crypto::tink::util::StatusOr<std::unique_ptr<JwtPublicKeySign>> - jwt_public_key_sign = keyset_handle->GetPrimitive<JwtPublicKeySign>(); - if (!jwt_public_key_sign.ok()) { - std::clog << "Getting JwtPublicKeySign-primitive from the factory failed: " - << jwt_public_key_sign.status().message() << std::endl; - exit(1); - } - - std::clog << "Generating a token with audience '" << audience - << "' using private keyset from file " << keyset_filename << "..." - << std::endl; - - crypto::tink::util::StatusOr<RawJwt> raw_jwt = - RawJwtBuilder() - .AddAudience(audience) - .SetExpiration(absl::Now() + absl::Seconds(100)) - .Build(); - if (!raw_jwt.ok()) { - std::clog << "Building RawJwt failed: " << raw_jwt.status().message() - << std::endl; - exit(1); - } - - crypto::tink::util::StatusOr<std::string> token = - (*jwt_public_key_sign)->SignAndEncode(*raw_jwt); - if (!token.ok()) { - std::clog << "Error while generating the token: " - << token.status().message() << std::endl; - exit(1); - } - - std::clog << "Writing the resulting token to file " << output_filename - << "..." << std::endl; - - WriteFile(*token, output_filename); -} - -// Verifies a token using the given public keyset and writes the result to the -// output file. -void Verify(absl::string_view keyset_filename, - absl::string_view expected_audience, - absl::string_view token_filename, - absl::string_view output_filename) { - std::unique_ptr<KeysetHandle> keyset_handle = ReadKeyset(keyset_filename); - - crypto::tink::util::StatusOr<std::unique_ptr<JwtPublicKeyVerify>> verifier = - keyset_handle->GetPrimitive<crypto::tink::JwtPublicKeyVerify>(); - if (!verifier.ok()) { - std::clog << "Getting JwtPublicKeyVerify-primitive from the factory " - << "failed: " << verifier.status().message() << std::endl; - exit(1); - } - - std::clog << "Verifying token from file " << token_filename - << " with expected audience '" << expected_audience - << "' using public keyset from file " << keyset_filename << "..." - << std::endl; - - std::string token = ReadFile(token_filename); - - crypto::tink::util::StatusOr<JwtValidator> validator = - crypto::tink::JwtValidatorBuilder() - .ExpectAudience(expected_audience) - .Build(); - if (!validator.ok()) { - std::clog << "Building validator failed: " << validator.status().message() - << std::endl; - exit(1); - } - - std::string result; - crypto::tink::util::StatusOr<crypto::tink::VerifiedJwt> verified_jwt = - (*verifier)->VerifyAndDecode(token, *validator); - if (!verified_jwt.ok()) { - std::clog << "Error while verifying the token: " - << verified_jwt.status().message() << std::endl; - result = "invalid"; - } else { - absl::Duration ttl = *verified_jwt->GetExpiration() - absl::Now(); - std::clog << "Token is valid for " << ttl << "." << std::endl; - result = "valid"; - } - - std::clog << "Writing the result to file " << output_filename - << "..." << std::endl; - - WriteFile(result, output_filename); -} +} // namespace tink_cc_examples int main(int argc, char** argv) { - if (argc == 1) { - PrintUsageInfo(); - exit(1); - } + absl::ParseCommandLine(argc, argv); - crypto::tink::util::Status status = crypto::tink::JwtSignatureRegister(); - if (!status.ok()) { - std::clog << "JwtSignatureRegister() failed: " << status.message() + ValidateParams(); + + std::string mode = absl::GetFlag(FLAGS_mode); + std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename); + std::string audience = absl::GetFlag(FLAGS_audience); + std::string token_filename = absl::GetFlag(FLAGS_token_filename); + + std::clog << "Using keyset in " << keyset_filename << " to "; + if (mode == kSign) { + std::clog << " generate and sign a token using audience '" << audience + << "'; the resulting signature is written to " << token_filename << std::endl; - exit(1); + } else { // mode == kVerify + std::clog << " verify a token with expected audience '" << audience + << std::endl; } - std::string operation = argv[1]; - - if (operation == "gen-private-key") { - if (argc != 3) { - PrintUsageInfo(); - exit(1); - } - - std::string output_filename = argv[2]; - - GeneratePrivateKey(output_filename); - } else if (operation == "get-public-key") { - if (argc != 4) { - PrintUsageInfo(); - exit(1); - } - - std::string private_keyset_filename = argv[2]; - std::string output_filename = argv[3]; - - ExtractPublicKey(private_keyset_filename, output_filename); - } else if (operation == "sign") { - if (argc != 5) { - PrintUsageInfo(); - exit(1); - } - - std::string keyset_filename = argv[2]; - std::string audience = argv[3]; - std::string output_filename = argv[4]; - - Sign(keyset_filename, audience, output_filename); - } else if (operation == "verify") { - if (argc != 6) { - PrintUsageInfo(); - exit(1); - } - - std::string keyset_filename = argv[2]; - std::string audience = argv[3]; - std::string token_filename = argv[4]; - std::string output_filename = argv[5]; - - Verify(keyset_filename, audience, token_filename, output_filename); - } else { - std::clog << "Unknown operation. Supported operations are: " - << "gen-private-key get-public-key sign verify" << std::endl; - exit(1); - } - - std::clog << "Done!" << std::endl; - + CHECK_OK(tink_cc_examples::JwtCli(mode, keyset_filename, audience, + token_filename)); return 0; } +// [END jwt-example]
diff --git a/cc/examples/jwt/jwt_signature_cli_test.sh b/cc/examples/jwt/jwt_signature_cli_test.sh index 5376edb..16999c6 100755 --- a/cc/examples/jwt/jwt_signature_cli_test.sh +++ b/cc/examples/jwt/jwt_signature_cli_test.sh
@@ -14,124 +14,166 @@ # limitations under the License. ################################################################################ +set -euo pipefail ############################################################################# -#### Tests for digital_signatures_cli binary. - -SIGNATURE_CLI="$1" - -PRIVATE_KEYSET_FILE="$TEST_TMPDIR/private_keyset.bin" -PUBLIC_KEYSET_FILE="$TEST_TMPDIR/public_keyset.bin" -TOKEN_FILE="$TEST_TMPDIR/token.bin" -RESULT_FILE="$TEST_TMPDIR/result.txt" - -OTHER_PRIVATE_KEYSET_FILE="$TEST_TMPDIR/other_private_keyset.bin" -OTHER_PUBLIC_KEYSET_FILE="$TEST_TMPDIR/other_public_keyset.bin" -INVALID_TOKEN_FILE="$TEST_TMPDIR/invalid_token.txt" - -echo "invalid.token.file" > $INVALID_TOKEN_FILE - +# Tests for Tink C++ JWT signature example. ############################################################################# -#### Helper function that checks if values are equal. -assert_equal() { - if [ "$1" == "$2" ]; then - echo "+++ Success: values are equal." - else - echo "--- Failure: values are different. Expected: [$1], actual: [$2]." +: "${TEST_TMPDIR:=$(mktemp -d)}" + +readonly CLI="$1" +readonly PRIVATE_KEYSET_FILE="$2" +readonly PUBLIC_KEYSET_FILE="$3" +readonly TOKEN_FILE="${TEST_TMPDIR}/token.json" +readonly TEST_NAME="TinkCcExamplesJwtSignatureTest" + +readonly AUDIENCE="JWT audience" + +####################################### +# 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 } -############################################################################# -#### All good, everything should work. -test_name="all_good" -echo "+++ Starting test $test_name..." +####################################### +# 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 +} -#### Generate a private key and get a public key. -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 +####################################### +# 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}" +} -#### Sign the message. -$SIGNATURE_CLI sign $PRIVATE_KEYSET_FILE audience $TOKEN_FILE || exit 1 - -#### Verify the signature. -$SIGNATURE_CLI verify $PUBLIC_KEYSET_FILE audience $TOKEN_FILE $RESULT_FILE || exit 1 - -#### Check that the signature is valid. -RESULT=$(<$RESULT_FILE) -assert_equal "valid" "$RESULT" +####################################### +# Ends a test case printing a success message. +# +# Globals: +# TEST_NAME +# TEST_CASE +####################################### +end_test_case() { + echo "[ OK ] ${TEST_NAME}.${TEST_CASE}" +} ############################################################################# -#### Bad private key when getting the public key. -test_name="get_public_key_with_bad_private_key" -echo "+++ Starting test $test_name..." -echo "abcd" >> $PRIVATE_KEYSET_FILE -$SIGNATURE_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE +start_test_case "sign_verify_all_good" -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE" +# Sign. +test_command "${CLI}" \ + --mode sign \ + --keyset_filename "${PRIVATE_KEYSET_FILE}" \ + --audience "${AUDIENCE}" \ + --token_filename "${TOKEN_FILE}" +assert_command_succeeded + +# Verify. +test_command "${CLI}" \ + --mode verify \ + --keyset_filename "${PUBLIC_KEYSET_FILE}" \ + --audience "${AUDIENCE}" \ + --token_filename "${TOKEN_FILE}" +assert_command_succeeded + +end_test_case ############################################################################# -#### Signature verification fails because the public key is wrong. -test_name="verify_with_different_public_key" -echo "+++ Starting test $test_name..." -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI gen-private-key $OTHER_PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI get-public-key $OTHER_PRIVATE_KEYSET_FILE $OTHER_PUBLIC_KEYSET_FILE || exit 1 -$SIGNATURE_CLI sign $PRIVATE_KEYSET_FILE audience $TOKEN_FILE || exit 1 -$SIGNATURE_CLI verify $OTHER_PUBLIC_KEYSET_FILE audience $TOKEN_FILE $RESULT_FILE || exit 1 +start_test_case "verify_fails_with_invalid_token" -RESULT=$(<$RESULT_FILE) -assert_equal "invalid" "$RESULT" +# Sign. +test_command "${CLI}" \ + --mode sign \ + --keyset_filename "${PRIVATE_KEYSET_FILE}" \ + --audience "${AUDIENCE}" \ + --token_filename "${TOKEN_FILE}" +assert_command_succeeded + +# Invalid token. +echo "modified" >> "${TOKEN_FILE}" + +# Verify. +test_command "${CLI}" \ + --mode verify \ + --keyset_filename "${PUBLIC_KEYSET_FILE}" \ + --audience "${AUDIENCE}" \ + --token_filename "${TOKEN_FILE}" +assert_command_failed + +end_test_case ############################################################################# -#### Verification fails because the audience is wrong. -test_name="verify_with_different_message" -echo "+++ Starting test $test_name..." -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 -$SIGNATURE_CLI sign $PRIVATE_KEYSET_FILE audience $TOKEN_FILE || exit 1 -$SIGNATURE_CLI verify $PUBLIC_KEYSET_FILE other_audience $TOKEN_FILE $RESULT_FILE || exit 1 +start_test_case "verify_fails_with_invalid_audience" -RESULT=$(<$RESULT_FILE) -assert_equal "invalid" "$RESULT" +# Sign. +test_command "${CLI}" \ + --mode sign \ + --keyset_filename "${PRIVATE_KEYSET_FILE}" \ + --audience "${AUDIENCE}" \ + --token_filename "${TOKEN_FILE}" +assert_command_succeeded -############################################################################# -#### Verification fails because the token ist invalid. -test_name="verify_with_different_message" -echo "+++ Starting test $test_name..." +# Modify audience. +readonly INVALID_AUDIENCE="invalid audience" -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 -$SIGNATURE_CLI verify $PUBLIC_KEYSET_FILE audience $INVALID_TOKEN_FILE $RESULT_FILE || exit 1 +# Verify. +test_command "${CLI}" \ + --mode verify \ + --keyset_filename "${PUBLIC_KEYSET_FILE}" \ + --audience "${INVALID_AUDIENCE}" \ + --token_filename "${TOKEN_FILE}" +assert_command_failed -RESULT=$(<$RESULT_FILE) -assert_equal "invalid" "$RESULT" +end_test_case -############################################################################# -#### Signing fails because we use the wrong key type. -test_name="sign_with_wrong_key" -echo "+++ Starting test $test_name..." - -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI get-public-key $PRIVATE_KEYSET_FILE $PUBLIC_KEYSET_FILE || exit 1 -$SIGNATURE_CLI sign $PUBLIC_KEYSET_FILE audience $TOKEN_FILE - -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE" - -############################################################################# -#### Verification fails because we use the wrong key type. -test_name="verify_with_wrong_key" -echo "+++ Starting test $test_name..." - -$SIGNATURE_CLI gen-private-key $PRIVATE_KEYSET_FILE || exit 1 -$SIGNATURE_CLI sign $PRIVATE_KEYSET_FILE audience $TOKEN_FILE || exit 1 -$SIGNATURE_CLI verify $PRIVATE_KEYSET_FILE audience $TOKEN_FILE $RESULT_FILE - -EXIT_VALUE="$?" -assert_equal 1 "$EXIT_VALUE"
diff --git a/cc/examples/jwt/jwt_signature_private_keyset.json b/cc/examples/jwt/jwt_signature_private_keyset.json new file mode 100644 index 0000000..be5f4be --- /dev/null +++ b/cc/examples/jwt/jwt_signature_private_keyset.json
@@ -0,0 +1,15 @@ +{ + "primaryKeyId": 185188009, + "key": [ + { + "keyData": { + "typeUrl": "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PrivateKey", + "value": "EosCEAEagQIAs9iifvWObNLbP+x7zupVIYTdHKba4VFgJEnnGtIII21R+KGddTdvNGAokd4GPrFk1GDPitHrAAoW1+NWrafsEUi2J9Sy3uwEyarsKDggewoBCNg2fcWAiZXplPjUyTlhrLvTuyrcL/mGPy+ib7bdmov+D2EP+rKUH6/ydtQGiyHRR3uurTUWfrMD1/6WaBVfngpy5Pxs2nuHXRmBHQKWmPfvErgr4abdjhKDaWIuxzSise1CSAbiWTNcxpIuFYZgPjgQzpqeh93LUXIX9YJds/bhHtXqRdxk6yTisloHOZETItK/rHCCE25dLkkaJ2Li7AtnJdBc6tEUNiuFj2JCjSIDAQABGoACT2lWxwySaQbp/N3lBUZ/dJ+AKsiaWWdfNmbTfwpCwbHhwhFKv5lMpynWgCIzS7d0uDpPKhLq20eZMpaVjXRaTn92vzuyB7DbpFiukkvGO839CvS9iueMjDP/weHlwzxtHqKJKVoRg7WAS6Iy7XUngLhT5GKNdbsooJ1GSKXyhbgWyMcspKSQe4lZXUntVMK5z4iLNmcQwsBp8yM55mZra13TXowob/E/wd+tGiABCn6CDt8G1gXzWDaoF2tt6WhSGZbXUVGagmoea/BWeAuJyKSSi5h+uPpc5SPhGvyKfSEVaCs2QeM7/UIXhzAcx2j/VqySb6y9EbSiJfy8vr49QSKBAQD+AbFCGHd9kZ5LIQrfe9caOxS9pQPdFkBJESw0C3x2uBIg8awiQsuVXMeEgyGLyWBZoi2x98OMSR9OzCuSLtb7Nv0Wqn0LUj4WPRdmg//uLeD3O2rcVRIR4db/B8WvXnK2uQsqwGDyh4BepGvprXQPYMX2uwnBGL2ccS2De53HJSqBAQC1QfOi4egjmlmXqJLpISUSN1NixkIi8EJHaZZ0YrbaRrEyiJczthcazNHFt6gzgOcosFaKaZeqps4Tet+5NgS7eh7RzLQ2+cfT4ewpT2ExJ4NsOy8XDqD6GRjliLxjGAoUf24s3B+3LLACPiQjeeZGJP0ivh384WabyXXxRgHFSTKBAQChl7gKIYCbHPHEQAAnzyQ4Js/6GinMFCTPlyI09f23lUDLPpRQs4fKvNydO8Myp+ko/NjvOH1qGPbW7WLmu+++n+wA6HNmqWqgQTtK170Q7JULE/zWsTQutitN0cb82yxFfJFTIFJM2NFc5GNWpSeJxPoMDk+VTcUK6qGW3SSyFTqBAQCeaPFA3SZAV1kNjio2zNzVOr0JijOqzUdfmgv/03Xy9e1POMjMTMuMhIygu42o1XMwwEwh037Vicp4g96aw3cHUgc1XC30DgByUPRQdit/BgV5xY+2GvbdHKoBkKrz/8Jvf58OXaLqN4frrdtvlc2GaDVC89zJcUR3ym3lW0WY4UKBAQD6MCruwXaxXJMxjtlH1YT5ow4R5neeiswNfGj4Ta/WbWyiVA60zpdNbGqH+etmiHY8+aBb/H4O9+JhOcBtlMLN4UlK1jg8wPSemZjsIPiUZXHkeIUa2RTUSz90wgz7aOqC0lYsLLFaJNWs54fC9LpZ0JzoqYDI8iDPnlE7xaag9g==", + "keyMaterialType": "ASYMMETRIC_PRIVATE" + }, + "status": "ENABLED", + "keyId": 185188009, + "outputPrefixType": "TINK" + } + ] +}
diff --git a/cc/examples/jwt/jwt_signature_public_keyset.json b/cc/examples/jwt/jwt_signature_public_keyset.json new file mode 100644 index 0000000..d6fa045 --- /dev/null +++ b/cc/examples/jwt/jwt_signature_public_keyset.json
@@ -0,0 +1,15 @@ +{ + "primaryKeyId": 185188009, + "key": [ + { + "keyData": { + "typeUrl": "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey", + "value": "EAEagQIAs9iifvWObNLbP+x7zupVIYTdHKba4VFgJEnnGtIII21R+KGddTdvNGAokd4GPrFk1GDPitHrAAoW1+NWrafsEUi2J9Sy3uwEyarsKDggewoBCNg2fcWAiZXplPjUyTlhrLvTuyrcL/mGPy+ib7bdmov+D2EP+rKUH6/ydtQGiyHRR3uurTUWfrMD1/6WaBVfngpy5Pxs2nuHXRmBHQKWmPfvErgr4abdjhKDaWIuxzSise1CSAbiWTNcxpIuFYZgPjgQzpqeh93LUXIX9YJds/bhHtXqRdxk6yTisloHOZETItK/rHCCE25dLkkaJ2Li7AtnJdBc6tEUNiuFj2JCjSIDAQAB", + "keyMaterialType": "ASYMMETRIC_PUBLIC" + }, + "status": "ENABLED", + "keyId": 185188009, + "outputPrefixType": "TINK" + } + ] +}
diff --git a/cc/examples/jwt/util.cc b/cc/examples/jwt/util.cc deleted file mode 100644 index a40266b..0000000 --- a/cc/examples/jwt/util.cc +++ /dev/null
@@ -1,125 +0,0 @@ -// Copyright 2021 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "jwt/util.h" - -#include <fstream> -#include <iostream> -#include <string> -#include <utility> - -#include "absl/strings/string_view.h" -#include "tink/binary_keyset_reader.h" -#include "tink/binary_keyset_writer.h" -#include "tink/cleartext_keyset_handle.h" -#include "tink/keyset_handle.h" -#include "tink/keyset_reader.h" -#include "tink/keyset_writer.h" -#include "tink/util/status.h" - -using crypto::tink::BinaryKeysetReader; -using crypto::tink::BinaryKeysetWriter; -using crypto::tink::CleartextKeysetHandle; -using crypto::tink::KeysetHandle; -using crypto::tink::KeysetReader; -using crypto::tink::KeysetWriter; - -namespace { - -// Returns a BinaryKeysetReader that reads from the specified file. -// In case of errors writes a log message and aborts. -std::unique_ptr<KeysetReader> GetBinaryKeysetReader( - absl::string_view filename) { - std::unique_ptr<std::ifstream> keyset_stream(new std::ifstream()); - keyset_stream->open(std::string(filename), std::ifstream::in); - crypto::tink::util::StatusOr<std::unique_ptr<KeysetReader>> keyset_reader = - BinaryKeysetReader::New(std::move(keyset_stream)); - if (!keyset_reader.ok()) { - std::clog << "Creation of the BinaryKeysetReader failed: " - << keyset_reader.status().message() << std::endl; - exit(1); - } - return std::move(*keyset_reader); -} - -// Returns a BinaryKeysetWriter that writes from the specified file. -// In case of errors writes a log message and aborts. -std::unique_ptr<KeysetWriter> GetBinaryKeysetWriter( - absl::string_view filename) { - std::unique_ptr<std::ofstream> keyset_stream(new std::ofstream()); - keyset_stream->open(std::string(filename), std::ofstream::out); - crypto::tink::util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> - keyset_writer = BinaryKeysetWriter::New(std::move(keyset_stream)); - if (!keyset_writer.ok()) { - std::clog << "Creation of the BinaryKeysetWriter failed: " - << keyset_writer.status().message() << std::endl; - exit(1); - } - return std::move(*keyset_writer); -} - -} // namespace - -std::unique_ptr<KeysetHandle> ReadKeyset(absl::string_view filename) { - std::unique_ptr<crypto::tink::KeysetReader> keyset_reader = - GetBinaryKeysetReader(filename); - crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = - CleartextKeysetHandle::Read(std::move(keyset_reader)); - if (!keyset_handle.ok()) { - std::clog << "Reading the keyset failed: " - << keyset_handle.status().message() << std::endl; - exit(1); - } - return std::move(*keyset_handle); -} - -void WriteKeyset(const crypto::tink::KeysetHandle& keyset_handle, - absl::string_view filename) { - std::unique_ptr<crypto::tink::KeysetWriter> keyset_writer = - GetBinaryKeysetWriter(filename); - auto status = - CleartextKeysetHandle::Write(keyset_writer.get(), keyset_handle); - if (!status.ok()) { - std::clog << "Writing the keyset failed: " << status.message() << std::endl; - exit(1); - } -} - -std::string ReadFile(absl::string_view filename) { - std::ifstream input_stream; - input_stream.open(std::string(filename), std::ifstream::in); - if (!input_stream.is_open()) { - std::clog << "Error opening input file " << std::string(filename) - << std::endl; - exit(1); - } - std::stringstream input; - input << input_stream.rdbuf(); - input_stream.close(); - return input.str(); -} - -void WriteFile(absl::string_view output, absl::string_view filename) { - std::ofstream output_stream(std::string(filename), - std::ofstream::out | std::ofstream::binary); - if (!output_stream.is_open()) { - std::clog << "Error opening output file " << std::string(filename) - << std::endl; - exit(1); - } - output_stream << std::string(output); - output_stream.close(); -}
diff --git a/cc/examples/jwt/util.h b/cc/examples/jwt/util.h deleted file mode 100644 index 9427faa..0000000 --- a/cc/examples/jwt/util.h +++ /dev/null
@@ -1,47 +0,0 @@ -// Copyright 2021 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef TINK_EXAMPLES_JWT_UTIL_H_ -#define TINK_EXAMPLES_JWT_UTIL_H_ - -#include <fstream> -#include <iostream> -#include <string> - -#include "absl/strings/string_view.h" -#include "tink/keyset_handle.h" - -// Helper functions for JWT Signature CLI - -// Reads a keyset from the specified file. -// In case of errors writes a log message and aborts. -std::unique_ptr<crypto::tink::KeysetHandle> ReadKeyset( - absl::string_view filename); - -// Writes the keyset to the specified file. -// In case of errors writes a log message and aborts. -void WriteKeyset(const crypto::tink::KeysetHandle& keyset_handle, - absl::string_view filename); - -// Reads the specified file and returns the contents as a string. -// In case of errors writes a log message and aborts. -std::string ReadFile(absl::string_view filename); - -// Writes the given 'output' to the specified file. -// In case of errors writes a log message and aborts. -void WriteFile(absl::string_view output, absl::string_view filename); - -#endif // TINK_EXAMPLES_JWT_UTIL_H_
diff --git a/cc/examples/key_derivation/BUILD.bazel b/cc/examples/key_derivation/BUILD.bazel new file mode 100644 index 0000000..9f0bcd6 --- /dev/null +++ b/cc/examples/key_derivation/BUILD.bazel
@@ -0,0 +1,34 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +cc_binary( + name = "key_derivation_cli", + srcs = ["key_derivation_cli.cc"], + deps = [ + "//util", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + "@com_google_absl//absl/log:check", + "@tink_cc//:aead", + "@tink_cc//:keyset_handle", + "@tink_cc//aead:aead_config", + "@tink_cc//keyderivation:key_derivation_config", + "@tink_cc//keyderivation:keyset_deriver", + "@tink_cc//util:status", + ], +) + +sh_test( + name = "key_derivation_cli_test", + size = "small", + srcs = ["key_derivation_cli_test.sh"], + args = [ + "$(rootpath :key_derivation_cli)", + "$(rootpaths :keyset.json)", + ], + data = [ + ":key_derivation_cli", + ":keyset.json", + ], +)
diff --git a/cc/examples/key_derivation/CMakeLists.txt b/cc/examples/key_derivation/CMakeLists.txt new file mode 100644 index 0000000..d81719b --- /dev/null +++ b/cc/examples/key_derivation/CMakeLists.txt
@@ -0,0 +1,16 @@ +add_executable(key_derivation_cli key_derivation_cli.cc) +target_include_directories(key_derivation_cli PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(key_derivation_cli + tink::static + absl::check + absl::flags_parse + util) + +add_test( + NAME key_derivation_cli_test + COMMAND "${BASH_PROGRAM}" + "${CMAKE_CURRENT_SOURCE_DIR}/key_derivation_cli_test.sh" + "${CMAKE_CURRENT_BINARY_DIR}/key_derivation_cli" + "${CMAKE_CURRENT_SOURCE_DIR}/keyset.json"
diff --git a/cc/examples/key_derivation/key_derivation_cli.cc b/cc/examples/key_derivation/key_derivation_cli.cc new file mode 100644 index 0000000..bca4ad1 --- /dev/null +++ b/cc/examples/key_derivation/key_derivation_cli.cc
@@ -0,0 +1,143 @@ +// Copyright 2023 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 key-derivation-example] +// A command-line utility for testing Tink Key Derivation. +#include <iostream> +#include <memory> +#include <string> +#include <utility> + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/log/check.h" +#include "tink/aead.h" +#include "tink/aead/aead_config.h" +#include "util/util.h" +#include "tink/keyderivation/key_derivation_config.h" +#include "tink/keyderivation/keyset_deriver.h" +#include "tink/keyset_handle.h" +#include "tink/util/status.h" + +ABSL_FLAG(std::string, keyset_filename, "", + "File in JSON format containing keyset that derives an AEAD keyset"); +ABSL_FLAG(std::string, salt_filename, "", "Salt file name"); +ABSL_FLAG(std::string, derived_keyset_filename, "", "Derived keyset file name"); + +namespace { + +using ::crypto::tink::Aead; +using ::crypto::tink::AeadConfig; +using ::crypto::tink::KeyDerivationConfig; +using ::crypto::tink::KeysetDeriver; +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::util::OkStatus; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; + +void ValidateParams() { + // [START_EXCLUDE] + CHECK(!absl::GetFlag(FLAGS_keyset_filename).empty()) + << "Keyset file must be specified"; + CHECK(!absl::GetFlag(FLAGS_salt_filename).empty()) + << "Input file must be specified"; + CHECK(!absl::GetFlag(FLAGS_derived_keyset_filename).empty()) + << "Output file must be specified"; + // [END_EXCLUDE] +} + +// Verifies `handle` contains a valid AEAD primitive. +Status VerifyDerivedAeadKeyset(const KeysetHandle& handle) { + // [START_EXCLUDE] + StatusOr<std::unique_ptr<Aead>> aead = handle.GetPrimitive<Aead>(); + if (!aead.ok()) return aead.status(); + + std::string plaintext = "plaintext"; + std::string ad = "ad"; + StatusOr<std::string> ciphertext = (*aead)->Encrypt(plaintext, ad); + if (!ciphertext.ok()) return ciphertext.status(); + + StatusOr<std::string> got = (*aead)->Decrypt(*ciphertext, ad); + if (!got.ok()) return got.status(); + + if (*got != plaintext) { + return Status( + absl::StatusCode::kInternal, + "AEAD obtained from derived keyset failed to decrypt correctly"); + } + return OkStatus(); + // [END_EXCLUDE] +} + +} // namespace + +namespace tink_cc_examples { + +Status KeyDerivationCli(const std::string& keyset_filename, + const std::string& salt_filename, + const std::string& derived_keyset_filename) { + Status result = KeyDerivationConfig::Register(); + if (!result.ok()) return result; + result = AeadConfig::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<KeysetDeriver>> deriver = + (*keyset_handle)->GetPrimitive<KeysetDeriver>(); + if (!deriver.ok()) return deriver.status(); + + // Read the salt. + StatusOr<std::string> salt_file_content = ReadFile(salt_filename); + if (!salt_file_content.ok()) return salt_file_content.status(); + + // Derive new keyset. + StatusOr<std::unique_ptr<KeysetHandle>> derived_handle = + (*deriver)->DeriveKeyset(*salt_file_content); + if (!derived_handle.ok()) return derived_handle.status(); + + Status status = VerifyDerivedAeadKeyset(**derived_handle); + if (!status.ok()) return status; + + return WriteJsonCleartextKeyset(derived_keyset_filename, **derived_handle); +} + +} // namespace tink_cc_examples + +int main(int argc, char** argv) { + absl::ParseCommandLine(argc, argv); + + ValidateParams(); + + std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename); + std::string salt_filename = absl::GetFlag(FLAGS_salt_filename); + std::string derived_keyset_filename = + absl::GetFlag(FLAGS_derived_keyset_filename); + + std::clog << "Using keyset from file " << keyset_filename + << " to derive a new AEAD keyset with the salt in file " + << salt_filename << "." << std::endl; + std::clog << "The resulting derived keyset will be written to " + << derived_keyset_filename << "." << std::endl; + + CHECK_OK(tink_cc_examples::KeyDerivationCli(keyset_filename, salt_filename, + derived_keyset_filename)); + return 0; +} +// [END key-derivation-example]
diff --git a/cc/examples/key_derivation/key_derivation_cli_test.sh b/cc/examples/key_derivation/key_derivation_cli_test.sh new file mode 100755 index 0000000..c59e7a7 --- /dev/null +++ b/cc/examples/key_derivation/key_derivation_cli_test.sh
@@ -0,0 +1,118 @@ +#!/bin/bash +# Copyright 2023 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 C++ Key Derivation example. +############################################################################# + +: "${TEST_TMPDIR:=$(mktemp -d)}" + +readonly CLI="$1" +readonly KEYSET_FILE="$2" +readonly SALT_FILE="${TEST_TMPDIR}/salt.txt" +readonly DERIVED_KEYSET_FILE="${TEST_TMPDIR}/derived_keyset.json" +readonly TEST_NAME="TinkCcExamplesKeyDerivationTest" + +echo "This is the salt used to derive keys." > "${SALT_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 "derive_key" + +test_command "${CLI}" \ + --keyset_filename "${KEYSET_FILE}" \ + --salt_filename "${SALT_FILE}" \ + --derived_keyset_filename "${DERIVED_KEYSET_FILE}" +assert_command_succeeded + +end_test_case
diff --git a/cc/examples/key_derivation/keyset.json b/cc/examples/key_derivation/keyset.json new file mode 100644 index 0000000..8703b1a --- /dev/null +++ b/cc/examples/key_derivation/keyset.json
@@ -0,0 +1,15 @@ +{ + "primaryKeyId": 1746379508, + "key": [ + { + "keyData": { + "typeUrl": "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey", + "value": "El0KMXR5cGUuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmNyeXB0by50aW5rLkhrZGZQcmZLZXkSJhICCAMaIHq3492RGOyzGsJTQh6Xi6noTDSrPQxULHuBqB10zMUCGAEaOgo4CjB0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5BZXNHY21LZXkSAhAQGAE=", + "keyMaterialType": "SYMMETRIC" + }, + "status": "ENABLED", + "keyId": 1746379508, + "outputPrefixType": "TINK" + } + ] +}
diff --git a/cc/examples/mac/BUILD.bazel b/cc/examples/mac/BUILD.bazel index 456fd7c..7d3b7c1 100644 --- a/cc/examples/mac/BUILD.bazel +++ b/cc/examples/mac/BUILD.bazel
@@ -11,17 +11,14 @@ name = "mac_cli", srcs = ["mac_cli.cc"], deps = [ + "//util", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/status", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/strings", "@tink_cc//:cleartext_keyset_handle", - "@tink_cc//:json_keyset_reader", "@tink_cc//:keyset_handle", - "@tink_cc//:keyset_reader", "@tink_cc//:mac", - "@tink_cc//config:tink_config", "@tink_cc//mac:mac_config", "@tink_cc//util:status", ],
diff --git a/cc/examples/mac/CMakeLists.txt b/cc/examples/mac/CMakeLists.txt new file mode 100644 index 0000000..194d7d6 --- /dev/null +++ b/cc/examples/mac/CMakeLists.txt
@@ -0,0 +1,16 @@ +add_executable(mac_cli mac_cli.cc) +target_include_directories(mac_cli PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(mac_cli + tink::static + absl::check + absl::flags_parse + util) + +add_test( + NAME mac_cli_test + COMMAND "${BASH_PROGRAM}" + "${CMAKE_CURRENT_SOURCE_DIR}/mac_cli_test.sh" + "${CMAKE_CURRENT_BINARY_DIR}/mac_cli" + "${CMAKE_CURRENT_SOURCE_DIR}/mac_test_keyset.json")
diff --git a/cc/examples/mac/mac_cli.cc b/cc/examples/mac/mac_cli.cc index 10cd06d..3a5407f 100644 --- a/cc/examples/mac/mac_cli.cc +++ b/cc/examples/mac/mac_cli.cc
@@ -14,25 +14,22 @@ // /////////////////////////////////////////////////////////////////////////////// // [START mac-example] -// A command-line utility for testing Tink MAC. +// A command-line utility for showcasing using the Tink MAC primitive. #include <fstream> #include <iostream> #include <memory> +#include <ostream> #include <sstream> #include <string> #include <utility> #include "absl/flags/flag.h" #include "absl/flags/parse.h" -#include "absl/memory/memory.h" -#include "absl/status/status.h" -#include "absl/strings/str_cat.h" +#include "absl/log/check.h" #include "absl/strings/string_view.h" -#include "tink/cleartext_keyset_handle.h" -#include "tink/json_keyset_reader.h" +#include "util/util.h" #include "tink/keyset_handle.h" -#include "tink/keyset_reader.h" #include "tink/mac.h" #include "tink/mac/mac_config.h" #include "tink/util/status.h" @@ -44,10 +41,7 @@ namespace { -using ::crypto::tink::CleartextKeysetHandle; -using ::crypto::tink::JsonKeysetReader; using ::crypto::tink::KeysetHandle; -using ::crypto::tink::KeysetReader; using ::crypto::tink::Mac; using ::crypto::tink::MacConfig; using ::crypto::tink::util::Status; @@ -56,151 +50,89 @@ constexpr absl::string_view kCompute = "compute"; constexpr absl::string_view kVerify = "verify"; -// Creates a KeysetReader that reads a JSON-formatted keyset -// from the given file. -StatusOr<std::unique_ptr<KeysetReader>> GetJsonKeysetReader( - const std::string& filename) { - std::clog << "Creating a JsonKeysetReader...\n"; - auto key_input_stream = absl::make_unique<std::ifstream>(); - key_input_stream->open(filename, std::ifstream::in); - return JsonKeysetReader::New(std::move(key_input_stream)); -} - -// Creates a KeysetHandle that for a keyset read from the given file, -// which is expected to contain a JSON-formatted keyset. -StatusOr<std::unique_ptr<KeysetHandle>> ReadKeyset( - const std::string& filename) { - StatusOr<std::unique_ptr<KeysetReader>> keyset_reader = - GetJsonKeysetReader(filename); - if (!keyset_reader.ok()) { - return keyset_reader.status(); - } - return CleartextKeysetHandle::Read(*std::move(keyset_reader)); -} - -// Reads `filename` and returns the read content as a string, or an error status -// if the file does not exist. -StatusOr<std::string> Read(const std::string& filename) { - std::clog << "Reading the input...\n"; - std::ifstream input_stream; - input_stream.open(filename, std::ifstream::in); - if (!input_stream.is_open()) { - return Status(absl::StatusCode::kInternal, - absl::StrCat("Error opening input file ", filename)); - } - std::stringstream input; - input << input_stream.rdbuf(); - return input.str(); -} - -// Writes the given `data_to_write` to the specified file `filename`. -Status Write(const std::string& data_to_write, const std::string& filename) { - std::clog << "Writing the output...\n"; - std::ofstream output_stream(filename, - std::ofstream::out | std::ofstream::binary); - if (!output_stream.is_open()) { - return Status(absl::StatusCode::kInternal, - absl::StrCat("Error opening output file ", filename)); - } - output_stream << data_to_write; - return crypto::tink::util::OkStatus(); +void ValidateParams() { + // [START_EXCLUDE] + CHECK(absl::GetFlag(FLAGS_mode) == kCompute || + absl::GetFlag(FLAGS_mode) == kVerify) + << "Invalid mode; must be `" << kCompute << "` or `" << kVerify << "`"; + CHECK(!absl::GetFlag(FLAGS_keyset_filename).empty()) + << "Keyset file must be specified"; + CHECK(!absl::GetFlag(FLAGS_data_filename).empty()) + << "Data file must be specified"; + CHECK(!absl::GetFlag(FLAGS_tag_filename).empty()) + << "Tag file must be specified"; + // [END_EXCLUDE] } } // namespace +namespace tink_cc_examples { + +// MAC example CLI implementation. +Status MacCli(absl::string_view mode, const std::string keyset_filename, + const std::string& data_filename, + const std::string& tag_filename) { + Status result = MacConfig::Register(); + if (!result.ok()) return result; + + // Read the 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<Mac>> mac_primitive = + (*keyset_handle)->GetPrimitive<Mac>(); + if (!mac_primitive.ok()) return mac_primitive.status(); + + // Read the input. + StatusOr<std::string> data_file_content = ReadFile(data_filename); + if (!data_file_content.ok()) return data_file_content.status(); + + std::string output; + if (mode == kCompute) { + // Compute authentication tag. + StatusOr<std::string> compute_result = + (*mac_primitive)->ComputeMac(*data_file_content); + if (!compute_result.ok()) return compute_result.status(); + // Write out the authentication tag to tag file. + return WriteToFile(*compute_result, tag_filename); + } else { // operation == kVerify. + // Read the authentication tag from tag file. + StatusOr<std::string> tag_result = ReadFile(tag_filename); + if (!tag_result.ok()) { + std::cerr << tag_result.status().message() << std::endl; + exit(1); + } + // Verify authentication tag. + Status verify_result = + (*mac_primitive)->VerifyMac(*tag_result, *data_file_content); + if (verify_result.ok()) std::clog << "Verification succeeded!" << std::endl; + return verify_result; + } +} + +} // 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 data_filename = absl::GetFlag(FLAGS_data_filename); std::string tag_filename = absl::GetFlag(FLAGS_tag_filename); - if (mode.empty()) { - std::cerr << "Mode must be specified with --mode=<" << kCompute << "|" - << kVerify << ">." << std::endl; - exit(1); - } - - if (mode != kCompute && mode != kVerify) { - std::cerr << "Unknown mode '" << mode << "'; " - << "Expected either " << kCompute << " or " << kVerify << "." - << std::endl; - exit(1); - } - - const std::string tag_file_action = - (mode == kCompute) ? "written to" : "read from"; std::clog << "Using keyset from file '" << keyset_filename << "' to " << mode << " authentication tag from file '" << tag_filename << "' for data file '" << data_filename << "'." << std::endl; - std::clog << "The tag will be " << tag_file_action << " file '" + std::clog << "The tag will be " + << ((mode == kCompute) ? "written to" : "read from") << " file '" << tag_filename << "'." << std::endl; - Status result = MacConfig::Register(); - if (!result.ok()) { - std::cerr << result.message() << std::endl; - exit(1); - } - - // Read the keyset from file. - StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = - ReadKeyset(keyset_filename); - if (!keyset_handle.ok()) { - std::cerr << keyset_handle.status().message() << std::endl; - exit(1); - } - - // Get the primitive. - StatusOr<std::unique_ptr<Mac>> mac_primitive = - (*keyset_handle)->GetPrimitive<Mac>(); - if (!mac_primitive.ok()) { - std::cerr << mac_primitive.status().message() << std::endl; - exit(1); - } - - // Read the input. - StatusOr<std::string> data_file_content = Read(data_filename); - if (!data_file_content.ok()) { - std::cerr << data_file_content.status().message() << std::endl; - exit(1); - } - - std::string output; - if (mode == kCompute) { - // Compute authentication tag. - std::clog << "Computing tag...\n"; - StatusOr<std::string> compute_result = - (*mac_primitive)->ComputeMac(*data_file_content); - if (!compute_result.ok()) { - std::cerr << compute_result.status().message() << std::endl; - exit(1); - } - // Write out the authentication tag to tag file. - Status write_result = Write(*compute_result, tag_filename); - if (!write_result.ok()) { - std::cerr << write_result.message() << std::endl; - exit(1); - } - } else { // operation == kVerify. - // Read the authentication tag from tag file. - StatusOr<std::string> tag_result = Read(tag_filename); - if (!tag_result.ok()) { - std::cerr << tag_result.status().message() << std::endl; - exit(1); - } - // Verify authentication tag. - std::clog << "Verifying tag...\n"; - Status verify_result = - (*mac_primitive)->VerifyMac(*tag_result, *data_file_content); - if (!verify_result.ok()) { - std::cerr << verify_result.message() << std::endl; - exit(1); - } - std::clog << "verification succeeded" << std::endl; - } - - std::clog << "All done." << std::endl; + CHECK_OK(tink_cc_examples::MacCli(mode, keyset_filename, data_filename, + tag_filename)); return 0; } // [END mac-example]
diff --git a/cc/examples/mac/mac_cli_test.sh b/cc/examples/mac/mac_cli_test.sh index c4600c5..34f88f3 100755 --- a/cc/examples/mac/mac_cli_test.sh +++ b/cc/examples/mac/mac_cli_test.sh
@@ -20,6 +20,8 @@ # Tests for Tink CC MAC. ############################################################################# +: "${TEST_TMPDIR:=$(mktemp -d)}" + readonly CLI="$1" readonly KEYSET_FILE="$2" readonly DATA_FILE="${TEST_TMPDIR}/example_data.txt" @@ -44,7 +46,7 @@ } ####################################### -# Asserts that the outcome of the latest test command was the expected one. +# Asserts that the outcome of the latest test command is 0. # # If not, it terminates the test execution. # @@ -52,23 +54,29 @@ # TEST_STATUS # TEST_NAME # TEST_CASE -# Arguments: -# expected_outcome: The expected outcome. ####################################### -_assert_test_command_outcome() { - expected_outcome="$1" - if (( TEST_STATUS != expected_outcome )); then - echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" - exit 1 +assert_command_succeeded() { + if (( TEST_STATUS != 0 )); then + echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" + exit 1 fi } -assert_command_succeeded() { - _assert_test_command_outcome 0 -} - +####################################### +# 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() { - _assert_test_command_outcome 1 + if (( TEST_STATUS == 0 )); then + echo "[ FAILED ] ${TEST_NAME}.${TEST_CASE}" + exit 1 + fi } #######################################
diff --git a/cc/examples/mac/mac_test_keyset.json b/cc/examples/mac/mac_test_keyset.json index d4ef7e1..b40e989 100644 --- a/cc/examples/mac/mac_test_keyset.json +++ b/cc/examples/mac/mac_test_keyset.json
@@ -1,13 +1,15 @@ { "primaryKeyId": 691856985, - "key": [{ + "key": [ + { "keyData": { - "typeUrl": "type.googleapis.com/google.crypto.tink.HmacKey", - "keyMaterialType": "SYMMETRIC", - "value": "EgQIAxAgGiDZsmkTufMG/XlKlk9m7bqxustjUPT2YULEVm8mOp2mSA\u003d\u003d" + "typeUrl": "type.googleapis.com/google.crypto.tink.HmacKey", + "keyMaterialType": "SYMMETRIC", + "value": "EgQIAxAgGiDZsmkTufMG/XlKlk9m7bqxustjUPT2YULEVm8mOp2mSA==" }, "outputPrefixType": "TINK", "keyId": 691856985, "status": "ENABLED" - }] -} + } + ] + }
diff --git a/cc/examples/util/BUILD.bazel b/cc/examples/util/BUILD.bazel new file mode 100644 index 0000000..b872c23 --- /dev/null +++ b/cc/examples/util/BUILD.bazel
@@ -0,0 +1,20 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +cc_library( + name = "util", + srcs = ["util.cc"], + hdrs = ["util.h"], + deps = [ + "@com_google_absl//absl/memory", + "@tink_cc//:cleartext_keyset_handle", + "@tink_cc//:json_keyset_reader", + "@tink_cc//:json_keyset_writer", + "@tink_cc//:keyset_handle", + "@tink_cc//:keyset_reader", + "@tink_cc//:keyset_writer", + "@tink_cc//util:status", + "@tink_cc//util:statusor", + ], +)
diff --git a/cc/examples/util/CMakeLists.txt b/cc/examples/util/CMakeLists.txt new file mode 100644 index 0000000..6e5a02c --- /dev/null +++ b/cc/examples/util/CMakeLists.txt
@@ -0,0 +1,5 @@ +add_library(util util.cc util.h) +target_include_directories(util PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(util tink::static)
diff --git a/cc/examples/util/util.cc b/cc/examples/util/util.cc new file mode 100644 index 0000000..ca4a3d5 --- /dev/null +++ b/cc/examples/util/util.cc
@@ -0,0 +1,105 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#include "util/util.h" + +#include <fstream> +#include <iostream> +#include <memory> +#include <ostream> +#include <sstream> +#include <string> +#include <utility> + +#include "absl/memory/memory.h" +#include "tink/cleartext_keyset_handle.h" +#include "tink/json_keyset_reader.h" +#include "tink/json_keyset_writer.h" +#include "tink/keyset_handle.h" +#include "tink/keyset_reader.h" +#include "tink/keyset_writer.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace tink_cc_examples { +namespace { + +using ::crypto::tink::JsonKeysetReader; +using ::crypto::tink::JsonKeysetWriter; +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::KeysetReader; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; + +// Creates a KeysetReader that reads a JSON-formatted keyset +// from the given file. +StatusOr<std::unique_ptr<KeysetReader>> GetJsonKeysetReader( + const std::string& filename) { + auto input_stream = absl::make_unique<std::ifstream>(); + input_stream->open(filename, std::ifstream::in); + return JsonKeysetReader::New(std::move(input_stream)); +} + +StatusOr<std::unique_ptr<JsonKeysetWriter>> GetJsonKeysetWriter( + const std::string& filename) { + auto output_stream = absl::make_unique<std::ofstream>(); + output_stream->open(filename, std::ofstream::out); + return JsonKeysetWriter::New(std::move(output_stream)); +} + +} // namespace + +StatusOr<std::unique_ptr<KeysetHandle>> ReadJsonCleartextKeyset( + const std::string& filename) { + StatusOr<std::unique_ptr<KeysetReader>> keyset_reader = + GetJsonKeysetReader(filename); + if (!keyset_reader.ok()) return keyset_reader.status(); + return crypto::tink::CleartextKeysetHandle::Read(*std::move(keyset_reader)); +} + +Status WriteJsonCleartextKeyset(const std::string& filename, + const KeysetHandle& keyset_handle) { + StatusOr<std::unique_ptr<JsonKeysetWriter>> keyset_writer = + GetJsonKeysetWriter(filename); + if (!keyset_writer.ok()) return keyset_writer.status(); + return crypto::tink::CleartextKeysetHandle::Write(keyset_writer->get(), + keyset_handle); +} + +StatusOr<std::string> ReadFile(const std::string& filename) { + std::ifstream input_stream; + input_stream.open(filename, std::ifstream::in); + if (!input_stream.is_open()) { + return Status(absl::StatusCode::kInternal, + absl::StrCat("Error opening input file ", filename)); + } + std::stringstream input; + input << input_stream.rdbuf(); + return input.str(); +} + +Status WriteToFile(const std::string& data_to_write, + const std::string& filename) { + std::ofstream output_stream(filename, + std::ofstream::out | std::ofstream::binary); + if (!output_stream.is_open()) { + return Status(absl::StatusCode::kInternal, + absl::StrCat("Error opening output file ", filename)); + } + output_stream << data_to_write; + return crypto::tink::util::OkStatus(); +} + +} // namespace tink_cc_examples
diff --git a/cc/examples/util/util.h b/cc/examples/util/util.h new file mode 100644 index 0000000..3f25a31 --- /dev/null +++ b/cc/examples/util/util.h
@@ -0,0 +1,49 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_EXAMPLES_UTIL_UTIL_H_ +#define TINK_EXAMPLES_UTIL_UTIL_H_ + +#include <memory> +#include <string> + +#include "tink/keyset_handle.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace tink_cc_examples { + +// Reads a keyset from the given file `filename` which is expected to contain a +// JSON-formatted keyset. +crypto::tink::util::StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> +ReadJsonCleartextKeyset(const std::string& filename); + +// Writes `keyset_handle` to the file `filename` formatted with JSON in +// cleartext. +crypto::tink::util::Status WriteJsonCleartextKeyset( + const std::string& filename, + const crypto::tink::KeysetHandle& keyset_handle); + +// Reads `filename` and returns the read content as a string, or an error status +// if the file does not exist. +crypto::tink::util::StatusOr<std::string> ReadFile(const std::string& filename); + +// Writes the given `data_to_write` to the specified file `filename`. +crypto::tink::util::Status WriteToFile(const std::string& data_to_write, + const std::string& filename); + +} // namespace tink_cc_examples + +#endif // TINK_EXAMPLES_UTIL_UTIL_H_
diff --git a/cc/examples/walkthrough/BUILD.bazel b/cc/examples/walkthrough/BUILD.bazel new file mode 100644 index 0000000..69d2010 --- /dev/null +++ b/cc/examples/walkthrough/BUILD.bazel
@@ -0,0 +1,165 @@ +"""Walkthrough examples for using Tink.""" + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +cc_library( + name = "test_util", + testonly = 1, + srcs = ["test_util.cc"], + hdrs = ["test_util.h"], + deps = [ + ":load_cleartext_keyset", + "@com_google_absl//absl/strings", + "@tink_cc", + ], +) + +cc_library( + name = "create_keyset", + srcs = ["create_keyset.cc"], + hdrs = ["create_keyset.h"], + deps = [ + "@tink_cc", + "@tink_cc//:keyset_handle", + "@tink_cc//util:statusor", + ], +) + +cc_test( + name = "create_keyset_test", + srcs = ["create_keyset_test.cc"], + deps = [ + ":create_keyset", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@tink_cc", + "@tink_cc//util:test_matchers", + ], +) + +cc_library( + name = "load_cleartext_keyset", + srcs = ["load_cleartext_keyset.cc"], + hdrs = ["load_cleartext_keyset.h"], + deps = [ + "@com_google_absl//absl/strings", + "@tink_cc", + "@tink_cc//:cleartext_keyset_handle", + ], +) + +cc_test( + name = "load_cleartext_keyset_test", + srcs = ["load_cleartext_keyset_test.cc"], + deps = [ + ":load_cleartext_keyset", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@tink_cc", + "@tink_cc//util:test_matchers", + ], +) + +cc_library( + name = "load_encrypted_keyset", + srcs = ["load_encrypted_keyset.cc"], + hdrs = ["load_encrypted_keyset.h"], + deps = [ + "@com_google_absl//absl/strings", + "@tink_cc", + ], +) + +cc_test( + name = "load_encrypted_keyset_test", + srcs = ["load_encrypted_keyset_test.cc"], + deps = [ + ":load_cleartext_keyset", + ":load_encrypted_keyset", + ":test_util", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@tink_cc", + "@tink_cc//util:test_matchers", + ], +) + +cc_library( + name = "write_keyset", + srcs = ["write_keyset.cc"], + hdrs = ["write_keyset.h"], + deps = [ + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@tink_cc", + ], +) + +cc_test( + name = "write_keyset_test", + srcs = ["write_keyset_test.cc"], + deps = [ + ":load_cleartext_keyset", + ":load_encrypted_keyset", + ":test_util", + ":write_keyset", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@tink_cc", + "@tink_cc//util:test_matchers", + ], +) + +cc_library( + name = "obtain_and_use_a_primitive", + srcs = ["obtain_and_use_a_primitive.cc"], + hdrs = ["obtain_and_use_a_primitive.h"], + deps = [ + "@com_google_absl//absl/strings", + "@tink_cc", + ], +) + +cc_test( + name = "obtain_and_use_a_primitive_test", + srcs = ["obtain_and_use_a_primitive_test.cc"], + deps = [ + ":load_cleartext_keyset", + ":obtain_and_use_a_primitive", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@tink_cc", + "@tink_cc//util:test_matchers", + ], +) + +cc_library( + name = "write_cleartext_keyset", + srcs = ["write_cleartext_keyset.cc"], + hdrs = ["write_cleartext_keyset.h"], + deps = [ + "@com_google_absl//absl/strings", + "@tink_cc", + "@tink_cc//:cleartext_keyset_handle", + ], +) + +cc_test( + name = "write_cleartext_keyset_test", + srcs = ["write_cleartext_keyset_test.cc"], + deps = [ + ":load_cleartext_keyset", + ":write_cleartext_keyset", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@tink_cc", + "@tink_cc//util:test_matchers", + ], +)
diff --git a/cc/examples/walkthrough/CMakeLists.txt b/cc/examples/walkthrough/CMakeLists.txt new file mode 100644 index 0000000..db128e5 --- /dev/null +++ b/cc/examples/walkthrough/CMakeLists.txt
@@ -0,0 +1,70 @@ +# Library targets. + +add_library(create_keyset create_keyset.cc create_keyset.h) +target_include_directories(create_keyset PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(create_keyset tink::static) + +add_library(load_cleartext_keyset load_cleartext_keyset.cc load_cleartext_keyset.h) +target_include_directories(load_cleartext_keyset PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(load_cleartext_keyset tink::static) + +add_library(test_util test_util.cc test_util.h) +target_include_directories(test_util PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(test_util load_cleartext_keyset tink::static) + +add_library(load_encrypted_keyset load_encrypted_keyset.cc load_encrypted_keyset.h) +target_include_directories(load_encrypted_keyset PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(load_encrypted_keyset tink::static) + +add_library(write_keyset write_keyset.cc write_keyset.h) +target_include_directories(write_keyset PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(write_keyset load_cleartext_keyset tink::static) + +add_library(obtain_and_use_a_primitive obtain_and_use_a_primitive.cc obtain_and_use_a_primitive.h) +target_include_directories(obtain_and_use_a_primitive PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(obtain_and_use_a_primitive tink::static) + +add_library(write_cleartext_keyset write_cleartext_keyset.cc write_cleartext_keyset.h) +target_include_directories(write_cleartext_keyset PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINK_EXAMPLES_INCLUDE_PATH}") +target_link_libraries(write_cleartext_keyset tink::static) + +# Test targets. +# NOTE: gmock and gtest_main are already exported by Tink. + +add_executable(create_keyset_test create_keyset_test.cc) +add_test(NAME create_keyset_test COMMAND create_keyset_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +target_link_libraries(create_keyset_test create_keyset gmock gtest_main) + +add_executable(load_cleartext_keyset_test load_cleartext_keyset_test.cc) +add_test(NAME load_cleartext_keyset_test COMMAND load_cleartext_keyset_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +target_link_libraries(load_cleartext_keyset_test load_cleartext_keyset gmock gtest_main) + +add_executable(load_encrypted_keyset_test load_encrypted_keyset_test.cc) +add_test(NAME load_encrypted_keyset_test COMMAND load_encrypted_keyset_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +target_link_libraries(load_encrypted_keyset_test test_util load_encrypted_keyset load_cleartext_keyset gmock gtest_main) + +add_executable(write_keyset_test write_keyset_test.cc) +add_test(NAME write_keyset_test COMMAND write_keyset_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +target_link_libraries(write_keyset_test test_util write_keyset load_cleartext_keyset load_encrypted_keyset gmock gtest_main) + +add_executable(obtain_and_use_a_primitive_test obtain_and_use_a_primitive_test.cc) +add_test(NAME obtain_and_use_a_primitive_test COMMAND obtain_and_use_a_primitive_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +target_link_libraries(obtain_and_use_a_primitive_test obtain_and_use_a_primitive load_cleartext_keyset gmock gtest_main) + +add_executable(write_cleartext_keyset_test write_cleartext_keyset_test.cc) +add_test(NAME write_cleartext_keyset_test COMMAND write_cleartext_keyset_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +target_link_libraries(write_cleartext_keyset_test write_cleartext_keyset load_cleartext_keyset gmock gtest_main)
diff --git a/cc/examples/walkthrough/create_keyset.cc b/cc/examples/walkthrough/create_keyset.cc new file mode 100644 index 0000000..d02f24b --- /dev/null +++ b/cc/examples/walkthrough/create_keyset.cc
@@ -0,0 +1,46 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/create_keyset.h" + +// [START tink_walkthrough_create_keyset] +#include <memory> + +#include "tink/aead/aead_key_templates.h" +#include "tink/keyset_handle.h" +#include "tink/util/statusor.h" + +namespace tink_walkthrough { + +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::util::StatusOr; +using ::google::crypto::tink::KeyTemplate; + +// Creates a keyset with a single AES128-GCM key and return a handle to it. +// +// Prerequisites for this example: +// - Register AEAD implementations of Tink. +StatusOr<std::unique_ptr<KeysetHandle>> CreateAead128GcmKeyset() { + // Tink provides pre-baked templates. For example, we generate a key template + // for AES128-GCM. + KeyTemplate key_template = crypto::tink::AeadKeyTemplates::Aes128Gcm(); + // This will generate a new keyset with only *one* key and return a keyset + // handle to it. + return KeysetHandle::GenerateNew(key_template); +} + +} // namespace tink_walkthrough +// [END tink_walkthrough_create_keyset]
diff --git a/cc/examples/walkthrough/create_keyset.h b/cc/examples/walkthrough/create_keyset.h new file mode 100644 index 0000000..96e2a41 --- /dev/null +++ b/cc/examples/walkthrough/create_keyset.h
@@ -0,0 +1,32 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_EXAMPLES_WALKTHROUGH_CREATE_KEYSET_H_ +#define TINK_EXAMPLES_WALKTHROUGH_CREATE_KEYSET_H_ + +#include <memory> + +#include "tink/keyset_handle.h" +#include "tink/util/statusor.h" + +namespace tink_walkthrough { + +// Creates a keyset with a single AES128-GCM key and return a handle to it. +crypto::tink::util::StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> +CreateAead128GcmKeyset(); + +} // namespace tink_walkthrough + +#endif // TINK_EXAMPLES_WALKTHROUGH_CREATE_KEYSET_H_
diff --git a/cc/examples/walkthrough/create_keyset_test.cc b/cc/examples/walkthrough/create_keyset_test.cc new file mode 100644 index 0000000..7970c13 --- /dev/null +++ b/cc/examples/walkthrough/create_keyset_test.cc
@@ -0,0 +1,67 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/create_keyset.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "absl/strings/string_view.h" +#include "tink/aead.h" +#include "tink/aead/aead_config.h" +#include "tink/registry.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace tink_walkthrough { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::util::StatusOr; +using ::testing::Not; +using ::testing::Test; + +class CreateAead128GcmKeysetTest : public Test { + public: + void TearDown() override { crypto::tink::Registry::Reset(); } +}; + +TEST_F(CreateAead128GcmKeysetTest, + CreateAead128GcmKeysetFailsIfAeadNotRegistered) { + EXPECT_THAT(CreateAead128GcmKeyset(), Not(IsOk())); +} + +TEST_F(CreateAead128GcmKeysetTest, CreateAead128GcmKeysetSucceeds) { + ASSERT_THAT(crypto::tink::AeadConfig::Register(), IsOk()); + StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> keyset_handle = + CreateAead128GcmKeyset(); + ASSERT_THAT(keyset_handle, IsOk()); + constexpr absl::string_view plaintext = "Some plaintext"; + constexpr absl::string_view associated_data = "Some associated_data"; + StatusOr<std::unique_ptr<crypto::tink::Aead>> aead = + (*keyset_handle)->GetPrimitive<crypto::tink::Aead>(); + ASSERT_THAT(aead, IsOk()); + StatusOr<std::string> ciphertext = + (*aead)->Encrypt(plaintext, associated_data); + ASSERT_THAT(ciphertext, IsOk()); + EXPECT_THAT((*aead)->Decrypt(*ciphertext, associated_data), + IsOkAndHolds(plaintext)); +} + +} // namespace +} // namespace tink_walkthrough
diff --git a/cc/examples/walkthrough/load_cleartext_keyset.cc b/cc/examples/walkthrough/load_cleartext_keyset.cc new file mode 100644 index 0000000..5ea603b --- /dev/null +++ b/cc/examples/walkthrough/load_cleartext_keyset.cc
@@ -0,0 +1,55 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/load_cleartext_keyset.h" + +// [START tink_walkthrough_load_cleartext_keyset] +#include <iostream> +#include <memory> +#include <utility> + +#include "absl/strings/string_view.h" +#include "tink/cleartext_keyset_handle.h" +#include "tink/json_keyset_reader.h" +#include "tink/keyset_handle.h" +#include "tink/keyset_reader.h" +#include "tink/util/statusor.h" + +namespace tink_walkthrough { + +using ::crypto::tink::util::StatusOr; + +// Loads a JSON-serialized unencrypted keyset `serialized_keyset` and returns a +// KeysetHandle. +// +// Prerequisites for this example: +// - Create an plaintext keyset in JSON, for example, using Tinkey: +// +// tinkey create-key --key-template AES256_GCM \ +// --out-format json --out keyset.json +// +StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> LoadKeyset( + absl::string_view serialized_keyset) { + // To load a serialized keyset we need a JSON keyset reader. + StatusOr<std::unique_ptr<crypto::tink::KeysetReader>> reader = + crypto::tink::JsonKeysetReader::New(serialized_keyset); + if (!reader.ok()) return reader.status(); + // Parse and obtain the keyset using the reader. + return crypto::tink::CleartextKeysetHandle::Read(*std::move(reader)); +} + +} // namespace tink_walkthrough +// [END tink_walkthrough_load_cleartext_keyset]
diff --git a/cc/examples/walkthrough/load_cleartext_keyset.h b/cc/examples/walkthrough/load_cleartext_keyset.h new file mode 100644 index 0000000..b4e1e42 --- /dev/null +++ b/cc/examples/walkthrough/load_cleartext_keyset.h
@@ -0,0 +1,34 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_EXAMPLES_WALKTHROUGH_LOAD_CLEARTEXT_KEYSET_H_ +#define TINK_EXAMPLES_WALKTHROUGH_LOAD_CLEARTEXT_KEYSET_H_ + +#include <memory> + +#include "absl/strings/string_view.h" +#include "tink/keyset_handle.h" +#include "tink/util/statusor.h" + +namespace tink_walkthrough { + +// Loads a JSON-serialized unencrypted keyset `serialized_keyset` and returns a +// KeysetHandle. +crypto::tink::util::StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> +LoadKeyset(absl::string_view serialized_keyset); + +} // namespace tink_walkthrough + +#endif // TINK_EXAMPLES_WALKTHROUGH_LOAD_CLEARTEXT_KEYSET_H_
diff --git a/cc/examples/walkthrough/load_cleartext_keyset_test.cc b/cc/examples/walkthrough/load_cleartext_keyset_test.cc new file mode 100644 index 0000000..ac8e5fa --- /dev/null +++ b/cc/examples/walkthrough/load_cleartext_keyset_test.cc
@@ -0,0 +1,77 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/load_cleartext_keyset.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "tink/aead/aead_config.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace tink_walkthrough { +namespace { + +constexpr absl::string_view kSerializedKeyset = R"json({ + "key": [ + { + "keyData": { + "keyMaterialType": "SYMMETRIC", + "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey", + "value": "GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg==" + }, + "keyId": 294406504, + "outputPrefixType": "TINK", + "status": "ENABLED" + } + ], + "primaryKeyId": 294406504 +})json"; + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::crypto::tink::util::StatusOr; + +TEST(LoadKeysetTest, LoadKeysetFailsWithInvalidKeyset) { + EXPECT_THAT(LoadKeyset("Invalid").status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(LoadKeysetTest, LoadKeysetSucceeds) { + StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> keyset_handle = + LoadKeyset(kSerializedKeyset); + ASSERT_THAT(keyset_handle, IsOk()); + ASSERT_THAT(crypto::tink::AeadConfig::Register(), IsOk()); + // Make sure we can extract the Aead primitive and encrypt/decrypt with it. + constexpr absl::string_view plaintext = "Some plaintext"; + constexpr absl::string_view associated_data = "Some associated_data"; + StatusOr<std::unique_ptr<crypto::tink::Aead>> aead = + (*keyset_handle)->GetPrimitive<crypto::tink::Aead>(); + ASSERT_THAT(aead, IsOk()); + StatusOr<std::string> ciphertext = + (*aead)->Encrypt(plaintext, associated_data); + ASSERT_THAT(ciphertext, IsOk()); + EXPECT_THAT((*aead)->Decrypt(*ciphertext, associated_data), + IsOkAndHolds(plaintext)); +} + +} // namespace +} // namespace tink_walkthrough
diff --git a/cc/examples/walkthrough/load_encrypted_keyset.cc b/cc/examples/walkthrough/load_encrypted_keyset.cc new file mode 100644 index 0000000..11679a7 --- /dev/null +++ b/cc/examples/walkthrough/load_encrypted_keyset.cc
@@ -0,0 +1,72 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/load_encrypted_keyset.h" + +// [START tink_walkthrough_load_encrypted_keyset] +#include <iostream> +#include <memory> +#include <utility> + +#include "absl/strings/string_view.h" +#include "tink/aead.h" +#include "tink/json_keyset_reader.h" +#include "tink/keyset_handle.h" +#include "tink/keyset_reader.h" +#include "tink/kms_client.h" +#include "tink/kms_clients.h" +#include "tink/util/statusor.h" + +namespace tink_walkthrough { + +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::util::StatusOr; + +// Loads a JSON-serialized keyset encrypted with a KSM +// `serialized_encrypted_keyset`. The decryption uses the KMS master key +// `master_key_uri`. +// +// Prerequisites for this example: +// - Register AEAD implementations of Tink. +// - Register a KMS client for the given URI prefix using KmsClients::Add. +// - Create a KMS encrypted keyset, for example using Tinkey with Cloud KMS: +// +// tinkey create-keyset --key-template AES128_GCM \ +// --out-format json --out encrypted_aead_keyset.json \ +// --master-key-uri gcp-kms://<KMS key uri> \ +// --credentials gcp_credentials.json +// +StatusOr<std::unique_ptr<KeysetHandle>> LoadKeyset( + absl::string_view serialized_encrypted_keyset, + absl::string_view master_key_uri) { + // Get a KMS client for the given key URI. + StatusOr<const crypto::tink::KmsClient*> kms_client = + crypto::tink::KmsClients::Get(master_key_uri); + if (!kms_client.ok()) return kms_client.status(); + // A KmsClient can return an Aead primitive. + StatusOr<std::unique_ptr<crypto::tink::Aead>> kms_aead = + (*kms_client)->GetAead(master_key_uri); + if (!kms_aead.ok()) return kms_aead.status(); + // Use a JSON reader to read the encrypted keyset. + StatusOr<std::unique_ptr<crypto::tink::KeysetReader>> reader = + crypto::tink::JsonKeysetReader::New(serialized_encrypted_keyset); + if (!reader.ok()) return reader.status(); + // Decrypt using the KMS, parse the keyset and retuns a handle to it. + return KeysetHandle::Read(*std::move(reader), **kms_aead); +} + +} // namespace tink_walkthrough +// [END tink_walkthrough_load_encrypted_keyset]
diff --git a/cc/examples/walkthrough/load_encrypted_keyset.h b/cc/examples/walkthrough/load_encrypted_keyset.h new file mode 100644 index 0000000..0c271fd --- /dev/null +++ b/cc/examples/walkthrough/load_encrypted_keyset.h
@@ -0,0 +1,36 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_EXAMPLES_WALKTHROUGH_LOAD_ENCRYPTED_KEYSET_H_ +#define TINK_EXAMPLES_WALKTHROUGH_LOAD_ENCRYPTED_KEYSET_H_ + +#include <memory> + +#include "absl/strings/string_view.h" +#include "tink/keyset_handle.h" +#include "tink/util/statusor.h" + +namespace tink_walkthrough { + +// Loads a JSON-serialized keyset encrypted with a KSM +// `serialized_encrypted_keyset`. The decryption uses the KMS master key +// `master_key_uri`. +crypto::tink::util::StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> +LoadKeyset(absl::string_view serialized_encrypted_keyset, + absl::string_view master_key_uri); + +} // namespace tink_walkthrough + +#endif // TINK_EXAMPLES_WALKTHROUGH_LOAD_ENCRYPTED_KEYSET_H_
diff --git a/cc/examples/walkthrough/load_encrypted_keyset_test.cc b/cc/examples/walkthrough/load_encrypted_keyset_test.cc new file mode 100644 index 0000000..4a1c42a --- /dev/null +++ b/cc/examples/walkthrough/load_encrypted_keyset_test.cc
@@ -0,0 +1,168 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/load_encrypted_keyset.h" + +#include <memory> +#include <ostream> +#include <sstream> +#include <string> +#include <utility> + +#include "gmock/gmock.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "tink/aead.h" +#include "tink/aead/aead_config.h" +#include "walkthrough/load_cleartext_keyset.h" +#include "walkthrough/test_util.h" +#include "tink/kms_clients.h" +#include "tink/registry.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace tink_walkthrough { +namespace { + +constexpr absl::string_view kSerializedMasterKeyKeyset = R"json({ + "key": [ + { + "keyData": { + "keyMaterialType": "SYMMETRIC", + "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey", + "value": "GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg==" + }, + "keyId": 294406504, + "outputPrefixType": "TINK", + "status": "ENABLED" + } + ], + "primaryKeyId": 294406504 +})json"; + +constexpr absl::string_view kSerializedKeysetToEncrypt = R"json({ + "key": [ + { + "keyData": { + "keyMaterialType": "SYMMETRIC", + "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey", + "value": "GhD+9l0RANZjzZEZ8PDp7LRW" + }, + "keyId": 1931667682, + "outputPrefixType": "TINK", + "status": "ENABLED" + } + ], + "primaryKeyId": 1931667682 +})json"; + +// Encryption of kSerializedKeysetToEncrypt using kSerializedMasterKeyKeyset. +constexpr absl::string_view kEncryptedKeyset = R"json({ + "encryptedKeyset": "ARGMSWi6YHyZ/Oqxl00XSq631a0q2UPmf+rCvCIAggSZrwCmxFF797MpY0dqgaXu1fz2eQ8zFNhlyTXv9kwg1kY6COpyhY/68zNBUkyKX4CharLYfpg1LgRl+6rMzIQa0XDHh7ZDmp1CevzecZIKnG83uDRHxxSv3h8c/Kc=" +})json"; + +constexpr absl::string_view kFakeKmsKeyUri = "fake://some_key"; + +using ::crypto::tink::Aead; +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::Registry; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; +using ::testing::Environment; + +// Test environment used to register KMS clients only once for the whole test. +class LoadKeysetTestEnvironment : public Environment { + public: + ~LoadKeysetTestEnvironment() override = default; + + // Register FakeKmsClient and AlwaysFailingFakeKmsClient. + void SetUp() override { + auto fake_kms = + absl::make_unique<FakeKmsClient>(kSerializedMasterKeyKeyset); + ASSERT_THAT(crypto::tink::KmsClients::Add(std::move(fake_kms)), IsOk()); + auto failing_kms = absl::make_unique<AlwaysFailingFakeKmsClient>(); + ASSERT_THAT(crypto::tink::KmsClients::Add(std::move(failing_kms)), IsOk()); + } +}; + +// Unused. +Environment *const test_env = + testing::AddGlobalTestEnvironment(new LoadKeysetTestEnvironment()); + +class LoadKeysetTest : public ::testing::Test { + public: + void TearDown() override { Registry::Reset(); } +}; + +TEST_F(LoadKeysetTest, LoadKeysetFailsWhenNoKmsRegistered) { + StatusOr<std::unique_ptr<KeysetHandle>> expected_keyset = + LoadKeyset(kEncryptedKeyset, /*master_key_uri=*/"other_kms://some_key"); + EXPECT_THAT(expected_keyset.status(), StatusIs(absl::StatusCode::kNotFound)); +} + +TEST_F(LoadKeysetTest, LoadKeysetFailsWhenKmsClientFails) { + StatusOr<std::unique_ptr<KeysetHandle>> expected_keyset = + LoadKeyset(kEncryptedKeyset, /*master_key_uri=*/"failing://some_key"); + EXPECT_THAT(expected_keyset.status(), + StatusIs(absl::StatusCode::kUnimplemented)); +} + +TEST_F(LoadKeysetTest, LoadKeysetFailsWhenAeadNotRegistered) { + StatusOr<std::unique_ptr<KeysetHandle>> expected_keyset = + LoadKeyset(kEncryptedKeyset, kFakeKmsKeyUri); + EXPECT_THAT(expected_keyset.status(), StatusIs(absl::StatusCode::kNotFound)); +} + +TEST_F(LoadKeysetTest, LoadKeysetFailsWhenInvalidKeyset) { + ASSERT_THAT(crypto::tink::AeadConfig::Register(), IsOk()); + StatusOr<std::unique_ptr<KeysetHandle>> expected_keyset = + LoadKeyset("invalid", kFakeKmsKeyUri); + EXPECT_THAT(expected_keyset.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + Registry::Reset(); +} + +TEST_F(LoadKeysetTest, LoadKeysetSucceeds) { + ASSERT_THAT(crypto::tink::AeadConfig::Register(), IsOk()); + StatusOr<std::unique_ptr<KeysetHandle>> handle = + LoadKeyset(kEncryptedKeyset, kFakeKmsKeyUri); + ASSERT_THAT(handle, IsOk()); + StatusOr<std::unique_ptr<Aead>> aead = (*handle)->GetPrimitive<Aead>(); + ASSERT_THAT(aead, IsOk()); + + StatusOr<std::unique_ptr<KeysetHandle>> expected_keyset = + LoadKeyset(kSerializedKeysetToEncrypt); + ASSERT_THAT(expected_keyset, IsOk()); + StatusOr<std::unique_ptr<Aead>> expected_aead = + (*expected_keyset)->GetPrimitive<Aead>(); + ASSERT_THAT(expected_aead, IsOk()); + + std::string associated_data = "Some associated data"; + std::string plaintext = "Some plaintext"; + + StatusOr<std::string> ciphertext = + (*aead)->Encrypt(plaintext, associated_data); + ASSERT_THAT(ciphertext, IsOk()); + EXPECT_THAT((*expected_aead)->Decrypt(*ciphertext, associated_data), + IsOkAndHolds(plaintext)); +} + +} // namespace +} // namespace tink_walkthrough
diff --git a/cc/examples/walkthrough/obtain_and_use_a_primitive.cc b/cc/examples/walkthrough/obtain_and_use_a_primitive.cc new file mode 100644 index 0000000..3f4ede4 --- /dev/null +++ b/cc/examples/walkthrough/obtain_and_use_a_primitive.cc
@@ -0,0 +1,71 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/obtain_and_use_a_primitive.h" + +// [START tink_walkthrough_obtain_and_use_a_primitive] +#include <iostream> +#include <memory> +#include <string> +#include <utility> + +#include "absl/strings/string_view.h" +#include "tink/aead.h" +#include "tink/keyset_handle.h" +#include "tink/util/statusor.h" + +namespace tink_walkthrough { + +using ::crypto::tink::Aead; +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::util::StatusOr; + +// AEAD encrypts `plaintext` with `associated_data` and the primary key in +// `keyset_handle`. +// +// Prerequisites for this example: +// - Register AEAD implementations of Tink. +// - Create a keyset and get a handle to it. +StatusOr<std::string> AeadEncrypt(const KeysetHandle& keyset_handle, + absl::string_view palintext, + absl::string_view associated_data) { + // To facilitate key rotation, GetPrimitive returns an Aead primitive that + // "wraps" multiple Aead primitives in the keyset. When encrypting it uses the + // primary key. + StatusOr<std::unique_ptr<Aead>> aead = keyset_handle.GetPrimitive<Aead>(); + if (!aead.ok()) return aead.status(); + return (*aead)->Encrypt(palintext, associated_data); +} + +// AEAD decrypts `ciphertext` with `associated_data` and the correct key in +// `keyset_handle`. +// +// Prerequisites for this example: +// - Register AEAD implementations of Tink. +// - Create a keyset and get a handle to it. +StatusOr<std::string> AeadDecrypt(const KeysetHandle& keyset_handle, + absl::string_view ciphertext, + absl::string_view associated_data) { + // To facilitate key rotation, GetPrimitive returns an Aead primitive that + // "wraps" multiple Aead primitives in the keyset. When decrypting it uses the + // key that was used to encrypt using the key ID contained in the ciphertext. + StatusOr<std::unique_ptr<Aead>> aead = keyset_handle.GetPrimitive<Aead>(); + if (!aead.ok()) return aead.status(); + return (*aead)->Decrypt(ciphertext, associated_data); +} + +} // namespace tink_walkthrough +// [END tink_walkthrough_obtain_and_use_a_primitive]
diff --git a/cc/examples/walkthrough/obtain_and_use_a_primitive.h b/cc/examples/walkthrough/obtain_and_use_a_primitive.h new file mode 100644 index 0000000..1f36018 --- /dev/null +++ b/cc/examples/walkthrough/obtain_and_use_a_primitive.h
@@ -0,0 +1,42 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_EXAMPLES_WALKTHROUGH_OBTAIN_AND_USE_A_PRIMITIVE_H_ +#define TINK_EXAMPLES_WALKTHROUGH_OBTAIN_AND_USE_A_PRIMITIVE_H_ + +#include <memory> +#include <string> + +#include "absl/strings/string_view.h" +#include "tink/keyset_handle.h" +#include "tink/util/statusor.h" + +namespace tink_walkthrough { + +// AEAD encrypts `plaintext` with `associated_data` and the primary key in +// `keyset_handle`. +crypto::tink::util::StatusOr<std::string> AeadEncrypt( + const crypto::tink::KeysetHandle& keyset_handle, + absl::string_view palintext, absl::string_view associated_data); + +// AEAD decrypts `ciphertext` with `associated_data` and the correct key in +// `keyset_handle`. +crypto::tink::util::StatusOr<std::string> AeadDecrypt( + const crypto::tink::KeysetHandle& keyset_handle, + absl::string_view ciphertext, absl::string_view associated_data); + +} // namespace tink_walkthrough + +#endif // TINK_EXAMPLES_WALKTHROUGH_OBTAIN_AND_USE_A_PRIMITIVE_H_
diff --git a/cc/examples/walkthrough/obtain_and_use_a_primitive_test.cc b/cc/examples/walkthrough/obtain_and_use_a_primitive_test.cc new file mode 100644 index 0000000..95b3894 --- /dev/null +++ b/cc/examples/walkthrough/obtain_and_use_a_primitive_test.cc
@@ -0,0 +1,70 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/obtain_and_use_a_primitive.h" + +#include <memory> +#include <ostream> +#include <string> +#include <utility> + +#include "gmock/gmock.h" +#include "absl/strings/string_view.h" +#include "tink/aead/aead_config.h" +#include "walkthrough/load_cleartext_keyset.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace tink_walkthrough { +namespace { + +constexpr absl::string_view kSerializedKeyset = R"string({ + "key": [ + { + "keyData": { + "keyMaterialType": "SYMMETRIC", + "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey", + "value": "GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg==" + }, + "keyId": 294406504, + "outputPrefixType": "TINK", + "status": "ENABLED" + } + ], + "primaryKeyId": 294406504 +})string"; + +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::util::StatusOr; + +TEST(LoadKeysetTest, EncryptDecrypt) { + ASSERT_THAT(crypto::tink::AeadConfig::Register(), IsOk()); + StatusOr<std::unique_ptr<KeysetHandle>> master_key_keyset = + LoadKeyset(kSerializedKeyset); + ASSERT_THAT(master_key_keyset, IsOk()); + constexpr absl::string_view kPlaintext = "Some data"; + constexpr absl::string_view kAssociatedData = "Some associated data"; + StatusOr<std::string> ciphertext = + AeadEncrypt(**master_key_keyset, kPlaintext, kAssociatedData); + ASSERT_THAT(ciphertext, IsOk()); + EXPECT_THAT(AeadDecrypt(**master_key_keyset, *ciphertext, kAssociatedData), + IsOkAndHolds(kPlaintext)); +} + +} // namespace +} // namespace tink_walkthrough
diff --git a/cc/examples/walkthrough/test_util.cc b/cc/examples/walkthrough/test_util.cc new file mode 100644 index 0000000..3cb7c49 --- /dev/null +++ b/cc/examples/walkthrough/test_util.cc
@@ -0,0 +1,53 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/test_util.h" + +#include <memory> + +#include "absl/strings/match.h" +#include "walkthrough/load_cleartext_keyset.h" +#include "tink/keyset_handle.h" + +namespace tink_walkthrough { + +using ::crypto::tink::Aead; +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; + +bool FakeKmsClient::DoesSupport(absl::string_view key_uri) const { + return absl::StartsWith(key_uri, "fake://"); +} + +StatusOr<std::unique_ptr<Aead>> FakeKmsClient::GetAead( + absl::string_view key_uri) const { + StatusOr<std::unique_ptr<KeysetHandle>> master_key_keyset = + LoadKeyset(serialized_master_key_keyset_); + if (!master_key_keyset.ok()) return master_key_keyset.status(); + return (*master_key_keyset)->GetPrimitive<Aead>(); +} + +bool AlwaysFailingFakeKmsClient::DoesSupport(absl::string_view key_uri) const { + return absl::StartsWith(key_uri, "failing://"); +} + +StatusOr<std::unique_ptr<Aead>> AlwaysFailingFakeKmsClient::GetAead( + absl::string_view key_uri) const { + return Status(absl::StatusCode::kUnimplemented, "Unimplemented"); +} + +} // namespace tink_walkthrough
diff --git a/cc/examples/walkthrough/test_util.h b/cc/examples/walkthrough/test_util.h new file mode 100644 index 0000000..f5f7312 --- /dev/null +++ b/cc/examples/walkthrough/test_util.h
@@ -0,0 +1,57 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_EXAMPLES_WALKTHROUGH_TEST_UTIL_H_ +#define TINK_EXAMPLES_WALKTHROUGH_TEST_UTIL_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "absl/strings/string_view.h" +#include "tink/aead.h" +#include "tink/kms_client.h" +#include "tink/util/statusor.h" + +namespace tink_walkthrough { + +// A fake KmsClient that for every key URI always returns an aead from +// kSerializedMasterKeyKeyset. +class FakeKmsClient : public crypto::tink::KmsClient { + public: + explicit FakeKmsClient(absl::string_view serialized_master_key_keyset) + : serialized_master_key_keyset_(serialized_master_key_keyset) {} + + bool DoesSupport(absl::string_view key_uri) const override; + + crypto::tink::util::StatusOr<std::unique_ptr<crypto::tink::Aead>> GetAead( + absl::string_view key_uri) const override; + + private: + std::string serialized_master_key_keyset_; +}; + +// A fake KmsClient that always fails to return an AEAD. +class AlwaysFailingFakeKmsClient : public crypto::tink::KmsClient { + public: + bool DoesSupport(absl::string_view key_uri) const override; + + crypto::tink::util::StatusOr<std::unique_ptr<crypto::tink::Aead>> GetAead( + absl::string_view key_uri) const override; +}; + +} // namespace tink_walkthrough + +#endif // TINK_EXAMPLES_WALKTHROUGH_TEST_UTIL_H_
diff --git a/cc/examples/walkthrough/write_cleartext_keyset.cc b/cc/examples/walkthrough/write_cleartext_keyset.cc new file mode 100644 index 0000000..0cdc856 --- /dev/null +++ b/cc/examples/walkthrough/write_cleartext_keyset.cc
@@ -0,0 +1,56 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/write_cleartext_keyset.h" + +// [START tink_walkthrough_write_keyset] +#include <memory> +#include <ostream> +#include <utility> + +#include "absl/status/status.h" +#include "tink/cleartext_keyset_handle.h" +#include "tink/json_keyset_writer.h" +#include "tink/keyset_handle.h" + +namespace tink_walkthrough { + +using ::crypto::tink::JsonKeysetWriter; +using ::crypto::tink::util::StatusOr; + +// Writes a `keyset` to `output_stream` as a plaintext JSON format. +// +// Warning: Storing keys in cleartext is not recommended. We recommend using a +// Key Management Service to protect your keys. See +// https://github.com/google/tink/blob/master/cc/examples/walkthrough/write_keyset.cc +// for an example, and +// https://developers.google.com/tink/key-management-overview for more info on +// how to use a KMS with Tink. +// +// Prerequisites for this example: +// - Create a keyset and obtain a KeysetHandle to it. +crypto::tink::util::Status WriteKeyset( + const crypto::tink::KeysetHandle& keyset, + std::unique_ptr<std::ostream> output_stream) { + StatusOr<std::unique_ptr<JsonKeysetWriter>> keyset_writer = + JsonKeysetWriter::New(std::move(output_stream)); + if (!keyset_writer.ok()) return keyset_writer.status(); + return crypto::tink::CleartextKeysetHandle::Write((keyset_writer)->get(), + keyset); +} + +} // namespace tink_walkthrough +// [END tink_walkthrough_write_keyset]
diff --git a/cc/examples/walkthrough/write_cleartext_keyset.h b/cc/examples/walkthrough/write_cleartext_keyset.h new file mode 100644 index 0000000..f0f2b8e --- /dev/null +++ b/cc/examples/walkthrough/write_cleartext_keyset.h
@@ -0,0 +1,37 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_EXAMPLES_WALKTHROUGH_WRITE_CLEARTEXT_KEYSET_H_ +#define TINK_EXAMPLES_WALKTHROUGH_WRITE_CLEARTEXT_KEYSET_H_ + +#include <memory> +#include <ostream> + +#include "tink/keyset_handle.h" + +namespace tink_walkthrough { + +// Writes a `keyset` to `output_stream` as a plaintext JSON format. +// +// Warning: Storing keys in cleartext is not recommended. We recommend using a +// Key Management Service to protect your keys. See +// https://developers.google.com/tink/key-management-overview. +crypto::tink::util::Status WriteKeyset( + const crypto::tink::KeysetHandle& keyset, + std::unique_ptr<std::ostream> output_stream); + +} // namespace tink_walkthrough + +#endif // TINK_EXAMPLES_WALKTHROUGH_WRITE_CLEARTEXT_KEYSET_H_
diff --git a/cc/examples/walkthrough/write_cleartext_keyset_test.cc b/cc/examples/walkthrough/write_cleartext_keyset_test.cc new file mode 100644 index 0000000..af229a2 --- /dev/null +++ b/cc/examples/walkthrough/write_cleartext_keyset_test.cc
@@ -0,0 +1,89 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#include "walkthrough/write_cleartext_keyset.h" + +#include <memory> +#include <ostream> +#include <sstream> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "tink/aead.h" +#include "tink/aead/aead_config.h" +#include "walkthrough/load_cleartext_keyset.h" +#include "tink/util/test_matchers.h" + +namespace tink_walkthrough { +namespace { + +using ::crypto::tink::Aead; +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::util::StatusOr; + +constexpr absl::string_view kSerializedKeyset = R"string({ + "key": [ + { + "keyData": { + "keyMaterialType": "SYMMETRIC", + "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey", + "value": "GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg==" + }, + "keyId": 294406504, + "outputPrefixType": "TINK", + "status": "ENABLED" + } + ], + "primaryKeyId": 294406504 +})string"; + +TEST(WriteCleartextKeysetTest, WriteKeysetSerializesCorrectly) { + ASSERT_THAT(crypto::tink::AeadConfig::Register(), IsOk()); + StatusOr<std::unique_ptr<KeysetHandle>> keyset = + LoadKeyset(kSerializedKeyset); + + std::stringbuf buffer; + auto output_stream = absl::make_unique<std::ostream>(&buffer); + ASSERT_THAT(WriteKeyset(**keyset, std::move(output_stream)), IsOk()); + + StatusOr<std::unique_ptr<Aead>> aead = (*keyset)->GetPrimitive<Aead>(); + + // Make sure the encrypted keyset was written correctly by loading it and + // trying to decrypt ciphertext. + StatusOr<std::unique_ptr<KeysetHandle>> loaded_keyset = + LoadKeyset(buffer.str()); + ASSERT_THAT(loaded_keyset, IsOk()); + StatusOr<std::unique_ptr<Aead>> loaded_keyset_aead = + (*loaded_keyset)->GetPrimitive<Aead>(); + ASSERT_THAT(loaded_keyset_aead, IsOk()); + + constexpr absl::string_view kPlaintext = "Some plaintext"; + constexpr absl::string_view kAssociatedData = "Some associated data"; + + StatusOr<std::string> ciphertext = + (*aead)->Encrypt(kPlaintext, kAssociatedData); + EXPECT_THAT((*loaded_keyset_aead)->Decrypt(*ciphertext, kAssociatedData), + IsOkAndHolds(kPlaintext)); + ciphertext = (*loaded_keyset_aead)->Encrypt(kPlaintext, kAssociatedData); + EXPECT_THAT((*aead)->Decrypt(*ciphertext, kAssociatedData), + IsOkAndHolds(kPlaintext)); +} + +} // namespace +} // namespace tink_walkthrough
diff --git a/cc/examples/walkthrough/write_keyset.cc b/cc/examples/walkthrough/write_keyset.cc new file mode 100644 index 0000000..ddaf807 --- /dev/null +++ b/cc/examples/walkthrough/write_keyset.cc
@@ -0,0 +1,65 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/write_keyset.h" + +// [START tink_walkthrough_write_keyset] +#include <fstream> +#include <memory> +#include <ostream> +#include <utility> + +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "tink/aead.h" +#include "tink/json_keyset_writer.h" +#include "tink/keyset_handle.h" +#include "tink/kms_client.h" +#include "tink/kms_clients.h" + +namespace tink_walkthrough { + +using ::crypto::tink::JsonKeysetWriter; +using ::crypto::tink::util::StatusOr; + +// Writes a `keyset` to `output_stream` in JSON format; the keyset is encrypted +// through a KMS service using the KMS key `master_kms_key_uri`. +// +// Prerequisites for this example: +// - Register AEAD implementations of Tink. +// - Register a KMS client that can use `master_kms_key_uri`. +// - Create a keyset and obtain a KeysetHandle to it. +crypto::tink::util::Status WriteEncryptedKeyset( + const crypto::tink::KeysetHandle& keyset, + std::unique_ptr<std::ostream> output_stream, + absl::string_view master_kms_key_uri) { + // Create a writer that will write the keyset to output_stream as JSON. + StatusOr<std::unique_ptr<JsonKeysetWriter>> writer = + JsonKeysetWriter::New(std::move(output_stream)); + if (!writer.ok()) return writer.status(); + // Get a KMS client for the given key URI. + StatusOr<const crypto::tink::KmsClient*> kms_client = + crypto::tink::KmsClients::Get(master_kms_key_uri); + if (!kms_client.ok()) return kms_client.status(); + // Get an Aead primitive that uses the KMS service to encrypt/decrypt. + StatusOr<std::unique_ptr<crypto::tink::Aead>> kms_aead = + (*kms_client)->GetAead(master_kms_key_uri); + if (!kms_aead.ok()) return kms_aead.status(); + return keyset.Write(writer->get(), **kms_aead); +} + +} // namespace tink_walkthrough +// [END tink_walkthrough_write_keyset]
diff --git a/cc/examples/walkthrough/write_keyset.h b/cc/examples/walkthrough/write_keyset.h new file mode 100644 index 0000000..323328c --- /dev/null +++ b/cc/examples/walkthrough/write_keyset.h
@@ -0,0 +1,35 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_EXAMPLES_WALKTHROUGH_WRITE_KEYSET_H_ +#define TINK_EXAMPLES_WALKTHROUGH_WRITE_KEYSET_H_ + +#include <memory> +#include <ostream> + +#include "tink/keyset_handle.h" + +namespace tink_walkthrough { + +// Writes a `keyset` to `output_stream` in JSON format; the keyset is encrypted +// through a KMS service using the KMS key `master_kms_key_uri`. +crypto::tink::util::Status WriteEncryptedKeyset( + const crypto::tink::KeysetHandle& keyset, + std::unique_ptr<std::ostream> output_stream, + absl::string_view master_kms_key_uri); + +} // namespace tink_walkthrough + +#endif // TINK_EXAMPLES_WALKTHROUGH_WRITE_KEYSET_H_
diff --git a/cc/examples/walkthrough/write_keyset_test.cc b/cc/examples/walkthrough/write_keyset_test.cc new file mode 100644 index 0000000..4b82df4 --- /dev/null +++ b/cc/examples/walkthrough/write_keyset_test.cc
@@ -0,0 +1,162 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "walkthrough/write_keyset.h" + +#include <memory> +#include <ostream> +#include <sstream> +#include <string> +#include <utility> + +#include "gmock/gmock.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "tink/aead.h" +#include "tink/aead/aead_config.h" +#include "walkthrough/load_cleartext_keyset.h" +#include "walkthrough/load_encrypted_keyset.h" +#include "walkthrough/test_util.h" +#include "tink/kms_clients.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace tink_walkthrough { +namespace { + +constexpr absl::string_view kSerializedMasterKeyKeyset = R"json({ + "key": [ + { + "keyData": { + "keyMaterialType": "SYMMETRIC", + "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey", + "value": "GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg==" + }, + "keyId": 294406504, + "outputPrefixType": "TINK", + "status": "ENABLED" + } + ], + "primaryKeyId": 294406504 +})json"; + +constexpr absl::string_view kSerializedKeysetToEncrypt = R"json({ + "key": [ + { + "keyData": { + "keyMaterialType": "SYMMETRIC", + "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey", + "value": "GhD+9l0RANZjzZEZ8PDp7LRW" + }, + "keyId": 1931667682, + "outputPrefixType": "TINK", + "status": "ENABLED" + } + ], + "primaryKeyId": 1931667682 +})json"; + +using ::crypto::tink::Aead; +using ::crypto::tink::KeysetHandle; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::crypto::tink::util::Status; +using ::crypto::tink::util::StatusOr; +using ::testing::Not; + +Status InitFakeKms() { + static Status* status = new Status([]() { + Status status = crypto::tink::AeadConfig::Register(); + if (!status.ok()) { + return status; + } + return crypto::tink::KmsClients::Add( + absl::make_unique<FakeKmsClient>(kSerializedMasterKeyKeyset)); + }()); + return *status; +} + +class WriteKeysetTest : public testing::Test { + protected: + void SetUp() override { + ASSERT_THAT(InitFakeKms(), IsOk()); + StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle_to_encrypt = + LoadKeyset(kSerializedKeysetToEncrypt); + ASSERT_THAT(keyset_handle_to_encrypt, IsOk()); + keyset_handle_to_encrypt_ = std::move(*keyset_handle_to_encrypt); + } + + std::unique_ptr<KeysetHandle> keyset_handle_to_encrypt_; +}; + +TEST_F(WriteKeysetTest, WriteEncryptedKeysetFailsWithNullOutputStream) { + EXPECT_THAT(WriteEncryptedKeyset(*keyset_handle_to_encrypt_, nullptr, + /*master_kms_key_uri=*/"fake://some_key"), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(WriteKeysetTest, WriteEncryptedKeysetFailsWhenStreamFails) { + auto output_stream = absl::make_unique<std::ostream>(nullptr); + EXPECT_THAT( + WriteEncryptedKeyset(*keyset_handle_to_encrypt_, std::move(output_stream), + /*master_kms_key_uri=*/"fake://some_key"), + Not(IsOk())); +} + +TEST_F(WriteKeysetTest, WriteEncryptedKeysetFailsNoKmsAvailable) { + std::stringbuf buffer; + auto output_stream = absl::make_unique<std::ostream>(&buffer); + EXPECT_THAT(WriteEncryptedKeyset( + *keyset_handle_to_encrypt_, std::move(output_stream), + /*master_kms_key_uri=*/"does_not_exist://does_not_exist"), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST_F(WriteKeysetTest, WriteEncryptedKeysetWithValidInputs) { + std::stringbuf buffer; + auto output_stream = absl::make_unique<std::ostream>(&buffer); + constexpr absl::string_view master_kms_key_uri = "fake://some_key"; + ASSERT_THAT( + WriteEncryptedKeyset(*keyset_handle_to_encrypt_, std::move(output_stream), + master_kms_key_uri), + IsOk()); + StatusOr<std::unique_ptr<Aead>> expected_aead = + keyset_handle_to_encrypt_->GetPrimitive<Aead>(); + ASSERT_THAT(expected_aead, IsOk()); + constexpr absl::string_view associated_data = "Some associated data"; + constexpr absl::string_view plaintext = "Some plaintext"; + + StatusOr<std::string> ciphertext = + (*expected_aead)->Encrypt(plaintext, associated_data); + ASSERT_THAT(ciphertext, IsOk()); + + // Make sure the encrypted keyset was written correctly by loading it and + // trying to decrypt ciphertext. + StatusOr<std::unique_ptr<KeysetHandle>> loaded_keyset = + LoadKeyset(buffer.str(), master_kms_key_uri); + ASSERT_THAT(loaded_keyset, IsOk()); + StatusOr<std::unique_ptr<Aead>> loaded_keyset_aead = + (*loaded_keyset)->GetPrimitive<Aead>(); + ASSERT_THAT(loaded_keyset_aead, IsOk()); + EXPECT_THAT((*loaded_keyset_aead)->Decrypt(*ciphertext, associated_data), + IsOkAndHolds(plaintext)); +} + +} // namespace +} // namespace tink_walkthrough
diff --git a/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.cc b/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.cc index badab1d..3f6139d 100644 --- a/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.cc +++ b/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.cc
@@ -16,6 +16,8 @@ #include "tink/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.h" +#include <memory> +#include <sstream> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.h b/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.h index 202df39..f48aba9 100644 --- a/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.h +++ b/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.h
@@ -37,7 +37,7 @@ std::unique_ptr<const Cecpq2AeadHkdfDemHelper>> New(const google::crypto::tink::KeyTemplate& dem_key_template); - virtual ~Cecpq2AeadHkdfDemHelper() {} + virtual ~Cecpq2AeadHkdfDemHelper() = default; // Creates and returns a new AeadOrDaead object that uses // a 32-bytes or greater high-entropy seed to generate a key.
diff --git a/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper_test.cc b/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper_test.cc index d9c9c33..b6ca7bd 100644 --- a/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper_test.cc +++ b/cc/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/kem/cecpq2_aead_hkdf_dem_helper.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/kem/cecpq2_hybrid_key_templates.cc b/cc/experimental/pqcrypto/kem/cecpq2_hybrid_key_templates.cc index c51a426..f6a24f7 100644 --- a/cc/experimental/pqcrypto/kem/cecpq2_hybrid_key_templates.cc +++ b/cc/experimental/pqcrypto/kem/cecpq2_hybrid_key_templates.cc
@@ -29,7 +29,6 @@ namespace tink { namespace { -using google::crypto::tink::Cecpq2AeadHkdfKeyFormat; using google::crypto::tink::EcPointFormat; using google::crypto::tink::EllipticCurveType; using google::crypto::tink::HashType;
diff --git a/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt.cc b/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt.cc index 933eb4a..2ed06d8 100644 --- a/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt.cc +++ b/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt_test.cc b/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt_test.cc index d141836..ffa659a 100644 --- a/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt_test.cc +++ b/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt_test.cc
@@ -16,8 +16,10 @@ #include "tink/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_decrypt.h" +#include <memory> #include <string> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt.cc b/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt.cc index eb0c665..9d405b9 100644 --- a/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt.cc +++ b/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt_test.cc b/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt_test.cc index 42d5b03..299e2c2 100644 --- a/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt_test.cc +++ b/cc/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/kem/subtle/cecpq2_aead_hkdf_hybrid_encrypt.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_recipient_kem_boringssl.cc b/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_recipient_kem_boringssl.cc index f8e7baa..5eca870 100644 --- a/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_recipient_kem_boringssl.cc +++ b/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_recipient_kem_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_recipient_kem_boringssl.h" +#include <memory> #include <utility> #include "absl/memory/memory.h"
diff --git a/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.cc b/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.cc index a67c8b5..7f21162 100644 --- a/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.cc +++ b/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.h b/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.h index e24e8b8..be57c87 100644 --- a/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.h +++ b/cc/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.h
@@ -89,9 +89,8 @@ // curves is trivial. static crypto::tink::util::StatusOr< std::unique_ptr<const Cecpq2HkdfSenderKemBoringSsl>> - New(EllipticCurveType curve, const absl::string_view ec_pubx, - const absl::string_view ec_puby, - const absl::string_view marshalled_hrss_pub); + New(EllipticCurveType curve, absl::string_view ec_pubx, + absl::string_view ec_puby, absl::string_view marshalled_hrss_pub); // Generates ephemeral key pairs, computes ECC's shared secret based on // generated ephemeral key and recipient's public key, generate a random @@ -114,9 +113,8 @@ // must be a big-endian byte array, and recipient's HRSS public key. static crypto::tink::util::StatusOr< std::unique_ptr<const Cecpq2HkdfSenderKemBoringSsl>> - New(EllipticCurveType curve, const absl::string_view pubx, - const absl::string_view puby, - const absl::string_view marshalled_hrss_pub); + New(EllipticCurveType curve, absl::string_view pubx, absl::string_view puby, + absl::string_view marshalled_hrss_pub); // Generates an ephemeral X25519 key pair, computes the X25519's shared secret // based on the ephemeral key and recipient's public key, generates a random @@ -136,8 +134,7 @@ // curve is not provided as a parameter here because the curve validation has // already been made in the New() method defined above. explicit Cecpq2HkdfX25519SenderKemBoringSsl( - const absl::string_view peer_ec_pubx, - const absl::string_view marshalled_hrss_pub); + absl::string_view peer_ec_pubx, absl::string_view marshalled_hrss_pub); // X25519 and HRSS public key containers. We note that the BoringSSL // implementation of HRSS requires that the HRSS public key is stored in the
diff --git a/cc/experimental/pqcrypto/kem/util/test_util.cc b/cc/experimental/pqcrypto/kem/util/test_util.cc index ff4e43b..d019189 100644 --- a/cc/experimental/pqcrypto/kem/util/test_util.cc +++ b/cc/experimental/pqcrypto/kem/util/test_util.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/kem/util/test_util.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/kem/util/test_util_test.cc b/cc/experimental/pqcrypto/kem/util/test_util_test.cc index ee7c3ca..98c48d6 100644 --- a/cc/experimental/pqcrypto/kem/util/test_util_test.cc +++ b/cc/experimental/pqcrypto/kem/util/test_util_test.cc
@@ -16,6 +16,8 @@ #include "tink/experimental/pqcrypto/kem/util/test_util.h" +#include <vector> + #include "gtest/gtest.h" #include "tink/aead/aes_gcm_key_manager.h" #include "tink/hybrid_encrypt.h"
diff --git a/cc/experimental/pqcrypto/signature/dilithium_key_template.cc b/cc/experimental/pqcrypto/signature/dilithium_key_template.cc index adbaf94..14f52a3 100644 --- a/cc/experimental/pqcrypto/signature/dilithium_key_template.cc +++ b/cc/experimental/pqcrypto/signature/dilithium_key_template.cc
@@ -40,7 +40,7 @@ using google::crypto::tink::KeyTemplate; using google::crypto::tink::OutputPrefixType; -KeyTemplate* NewDilithiumKeyTemplate(int32 key_size, +KeyTemplate* NewDilithiumKeyTemplate(int32_t key_size, DilithiumSeedExpansion seed_expansion) { KeyTemplate* key_template = new KeyTemplate; key_template->set_type_url(
diff --git a/cc/experimental/pqcrypto/signature/dilithium_key_template.h b/cc/experimental/pqcrypto/signature/dilithium_key_template.h index cce59e2..c3acff2 100644 --- a/cc/experimental/pqcrypto/signature/dilithium_key_template.h +++ b/cc/experimental/pqcrypto/signature/dilithium_key_template.h
@@ -14,8 +14,8 @@ // /////////////////////////////////////////////////////////////////////////////// -#ifndef TINK_EXPERIMENTAL_SIGNATURE_DILITHIUM_KEY_TEMPLATE_H_ -#define TINK_EXPERIMENTAL_SIGNATURE_DILITHIUM_KEY_TEMPLATE_H_ +#ifndef TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_DILITHIUM_KEY_TEMPLATE_H_ +#define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_DILITHIUM_KEY_TEMPLATE_H_ #include "proto/tink.pb.h" @@ -38,4 +38,4 @@ } // namespace tink } // namespace crypto -#endif // TINK_EXPERIMENTAL_SIGNATURE_DILITHIUM_KEY_TEMPLATE_H_ +#endif // TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_DILITHIUM_KEY_TEMPLATE_H_
diff --git a/cc/experimental/pqcrypto/signature/dilithium_key_template_test.cc b/cc/experimental/pqcrypto/signature/dilithium_key_template_test.cc index f4b2858..ac8c9c8 100644 --- a/cc/experimental/pqcrypto/signature/dilithium_key_template_test.cc +++ b/cc/experimental/pqcrypto/signature/dilithium_key_template_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/dilithium_key_template.h" +#include <memory> #include <string> #include "gmock/gmock.h" @@ -50,7 +51,7 @@ struct DilithiumKeyTemplateTestCase { std::string test_name; - int32 key_size; + int32_t key_size; DilithiumSeedExpansion seed_expansion; KeyTemplate key_template; };
diff --git a/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager.cc b/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager.cc index 3acb09b..2ff7e1b 100644 --- a/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager.cc +++ b/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager.cc
@@ -16,6 +16,9 @@ #include "tink/experimental/pqcrypto/signature/dilithium_sign_key_manager.h" +#include <memory> +#include <utility> + #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h"
diff --git a/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager.h b/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager.h index 6e83592..4f5abb0 100644 --- a/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager.h +++ b/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager.h
@@ -17,6 +17,7 @@ #ifndef TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_DILITHIUM_SIGN_KEY_MANAGER_H_ #define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_DILITHIUM_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager_test.cc b/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager_test.cc index 92f5423..bca0d26 100644 --- a/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager_test.cc +++ b/cc/experimental/pqcrypto/signature/dilithium_sign_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/dilithium_sign_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h" @@ -70,7 +71,7 @@ // Helper function that returns a valid dilithium key format. StatusOr<DilithiumKeyFormat> CreateValidKeyFormat( - int32 private_key_size, DilithiumSeedExpansion seed_expansion) { + int32_t private_key_size, DilithiumSeedExpansion seed_expansion) { DilithiumKeyFormat key_format; DilithiumParams* params = key_format.mutable_params(); params->set_key_size(private_key_size);
diff --git a/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager.cc b/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager.cc index 740e6f5..66d3c75 100644 --- a/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager.cc +++ b/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager.cc
@@ -16,6 +16,8 @@ #include "tink/experimental/pqcrypto/signature/dilithium_verify_key_manager.h" +#include <memory> + #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h"
diff --git a/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager.h b/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager.h index b139ab8..4893220 100644 --- a/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager.h +++ b/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager.h
@@ -14,9 +14,10 @@ // /////////////////////////////////////////////////////////////////////////////// -#ifndef TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SUBTLE_DILITHIUM_VERIFY_KEY_MANAGER_H_ -#define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SUBTLE_DILITHIUM_VERIFY_KEY_MANAGER_H_ +#ifndef TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_DILITHIUM_VERIFY_KEY_MANAGER_H_ +#define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_DILITHIUM_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h" @@ -71,4 +72,4 @@ } // namespace tink } // namespace crypto -#endif // TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SUBTLE_DILITHIUM_VERIFY_KEY_MANAGER_H_ +#endif // TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_DILITHIUM_VERIFY_KEY_MANAGER_H_
diff --git a/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager_test.cc b/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager_test.cc index e9c1695..a093fea 100644 --- a/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager_test.cc +++ b/cc/experimental/pqcrypto/signature/dilithium_verify_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/dilithium_verify_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h" @@ -70,7 +71,7 @@ // Helper function that returns a valid dilithium private key. StatusOr<DilithiumPrivateKey> CreateValidPrivateKey( - int32 private_key_size, DilithiumSeedExpansion seed_expansion) { + int32_t private_key_size, DilithiumSeedExpansion seed_expansion) { DilithiumKeyFormat key_format; DilithiumParams* params = key_format.mutable_params(); params->set_key_size(private_key_size); @@ -81,7 +82,7 @@ // Helper function that returns a valid dilithium public key. StatusOr<DilithiumPublicKey> CreateValidPublicKey( - int32 private_key_size, DilithiumSeedExpansion seed_expansion) { + int32_t private_key_size, DilithiumSeedExpansion seed_expansion) { StatusOr<DilithiumPrivateKey> private_key = CreateValidPrivateKey(private_key_size, seed_expansion);
diff --git a/cc/experimental/pqcrypto/signature/falcon_key_template.cc b/cc/experimental/pqcrypto/signature/falcon_key_template.cc index 668259b..bd33a70 100644 --- a/cc/experimental/pqcrypto/signature/falcon_key_template.cc +++ b/cc/experimental/pqcrypto/signature/falcon_key_template.cc
@@ -16,6 +16,8 @@ #include "tink/experimental/pqcrypto/signature/falcon_key_template.h" +#include <memory> + #include "tink/experimental/pqcrypto/signature/subtle/falcon_subtle_utils.h" #include "tink/util/constants.h" #include "proto/experimental/pqcrypto/falcon.pb.h"
diff --git a/cc/experimental/pqcrypto/signature/falcon_key_template_test.cc b/cc/experimental/pqcrypto/signature/falcon_key_template_test.cc index 8c9c167..2af3a5c 100644 --- a/cc/experimental/pqcrypto/signature/falcon_key_template_test.cc +++ b/cc/experimental/pqcrypto/signature/falcon_key_template_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/falcon_key_template.h" +#include <memory> #include <string> #include "gmock/gmock.h"
diff --git a/cc/experimental/pqcrypto/signature/falcon_sign_key_manager.cc b/cc/experimental/pqcrypto/signature/falcon_sign_key_manager.cc index 9d1d3ed..0b431c5 100644 --- a/cc/experimental/pqcrypto/signature/falcon_sign_key_manager.cc +++ b/cc/experimental/pqcrypto/signature/falcon_sign_key_manager.cc
@@ -16,6 +16,8 @@ #include "tink/experimental/pqcrypto/signature/falcon_sign_key_manager.h" +#include <memory> + #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h"
diff --git a/cc/experimental/pqcrypto/signature/falcon_sign_key_manager.h b/cc/experimental/pqcrypto/signature/falcon_sign_key_manager.h index 0ce6cc2..32a6f14 100644 --- a/cc/experimental/pqcrypto/signature/falcon_sign_key_manager.h +++ b/cc/experimental/pqcrypto/signature/falcon_sign_key_manager.h
@@ -17,7 +17,7 @@ #ifndef TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_FALCON_SIGN_KEY_MANAGER_H_ #define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_FALCON_SIGN_KEY_MANAGER_H_ - +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/experimental/pqcrypto/signature/falcon_sign_key_manager_test.cc b/cc/experimental/pqcrypto/signature/falcon_sign_key_manager_test.cc index eccd0bb..06ff16a 100644 --- a/cc/experimental/pqcrypto/signature/falcon_sign_key_manager_test.cc +++ b/cc/experimental/pqcrypto/signature/falcon_sign_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/falcon_sign_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h" @@ -54,7 +55,7 @@ using FalconSignKeyManagerTest = testing::TestWithParam<FalconTestCase>; // Helper function that returns a valid falcon key format. -StatusOr<FalconKeyFormat> CreateValidKeyFormat(int32 private_key_size) { +StatusOr<FalconKeyFormat> CreateValidKeyFormat(int32_t private_key_size) { FalconKeyFormat key_format; key_format.set_key_size(private_key_size);
diff --git a/cc/experimental/pqcrypto/signature/falcon_verify_key_manager.cc b/cc/experimental/pqcrypto/signature/falcon_verify_key_manager.cc index 26dab4b..125c802 100644 --- a/cc/experimental/pqcrypto/signature/falcon_verify_key_manager.cc +++ b/cc/experimental/pqcrypto/signature/falcon_verify_key_manager.cc
@@ -16,6 +16,8 @@ #include "tink/experimental/pqcrypto/signature/falcon_verify_key_manager.h" +#include <memory> + #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h"
diff --git a/cc/experimental/pqcrypto/signature/falcon_verify_key_manager.h b/cc/experimental/pqcrypto/signature/falcon_verify_key_manager.h index 9956700..dabe237 100644 --- a/cc/experimental/pqcrypto/signature/falcon_verify_key_manager.h +++ b/cc/experimental/pqcrypto/signature/falcon_verify_key_manager.h
@@ -17,6 +17,7 @@ #ifndef TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_FALCON_VERIFY_KEY_MANAGER_H_ #define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_FALCON_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/experimental/pqcrypto/signature/falcon_verify_key_manager_test.cc b/cc/experimental/pqcrypto/signature/falcon_verify_key_manager_test.cc index c486288..3f89de5 100644 --- a/cc/experimental/pqcrypto/signature/falcon_verify_key_manager_test.cc +++ b/cc/experimental/pqcrypto/signature/falcon_verify_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/falcon_verify_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h" @@ -54,7 +55,7 @@ using FalconVerifyKeyManagerTest = testing::TestWithParam<FalconTestCase>; // Helper function that returns a valid falcon private key. -StatusOr<FalconPrivateKey> CreateValidPrivateKey(int32 private_key_size) { +StatusOr<FalconPrivateKey> CreateValidPrivateKey(int32_t private_key_size) { FalconKeyFormat key_format; key_format.set_key_size(private_key_size); @@ -62,7 +63,7 @@ } // Helper function that returns a valid falcon public key. -StatusOr<FalconPublicKey> CreateValidPublicKey(int32 private_key_size) { +StatusOr<FalconPublicKey> CreateValidPublicKey(int32_t private_key_size) { StatusOr<FalconPrivateKey> private_key = CreateValidPrivateKey(private_key_size);
diff --git a/cc/experimental/pqcrypto/signature/signature_config_test.cc b/cc/experimental/pqcrypto/signature/signature_config_test.cc index 2526126..11605e1 100644 --- a/cc/experimental/pqcrypto/signature/signature_config_test.cc +++ b/cc/experimental/pqcrypto/signature/signature_config_test.cc
@@ -19,13 +19,13 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/status/status.h" -#include "tink/config/tink_fips.h" #include "tink/experimental/pqcrypto/signature/dilithium_sign_key_manager.h" #include "tink/experimental/pqcrypto/signature/dilithium_verify_key_manager.h" -#include "tink/experimental/pqcrypto/signature/sphincs_sign_key_manager.h" -#include "tink/experimental/pqcrypto/signature/sphincs_verify_key_manager.h" #include "tink/experimental/pqcrypto/signature/falcon_sign_key_manager.h" #include "tink/experimental/pqcrypto/signature/falcon_verify_key_manager.h" +#include "tink/experimental/pqcrypto/signature/sphincs_sign_key_manager.h" +#include "tink/experimental/pqcrypto/signature/sphincs_verify_key_manager.h" +#include "tink/internal/fips_utils.h" #include "tink/public_key_sign.h" #include "tink/public_key_verify.h" #include "tink/registry.h" @@ -45,7 +45,7 @@ }; TEST_F(PcqSignatureConfigTest, CheckDilithium) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used"; } @@ -69,7 +69,7 @@ } TEST_F(PcqSignatureConfigTest, CheckSphincs) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used"; } @@ -93,7 +93,7 @@ } TEST_F(PcqSignatureConfigTest, CheckFalcon) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used"; }
diff --git a/cc/experimental/pqcrypto/signature/signature_config_util_test.cc b/cc/experimental/pqcrypto/signature/signature_config_util_test.cc index 13bc155..f72e470 100644 --- a/cc/experimental/pqcrypto/signature/signature_config_util_test.cc +++ b/cc/experimental/pqcrypto/signature/signature_config_util_test.cc
@@ -14,16 +14,16 @@ // /////////////////////////////////////////////////////////////////////////////// -#include "tink/experimental/pqcrypto/signature/signature_config.h" - +#include <memory> #include <string> #include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "tink/config/tink_fips.h" #include "tink/experimental/pqcrypto/signature/dilithium_sign_key_manager.h" #include "tink/experimental/pqcrypto/signature/dilithium_verify_key_manager.h" +#include "tink/experimental/pqcrypto/signature/signature_config.h" +#include "tink/internal/fips_utils.h" #include "tink/public_key_sign.h" #include "tink/public_key_verify.h" #include "tink/registry.h" @@ -45,7 +45,7 @@ }; TEST_F(PcqSignatureConfigTest, CheckStatus) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used"; } @@ -55,7 +55,7 @@ // Tests that the PublicKeySignWrapper has been properly registered and we // can wrap primitives. TEST_F(PcqSignatureConfigTest, PublicKeySignWrapperRegistered) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used"; } @@ -91,7 +91,7 @@ // Tests that the PublicKeyVerifyWrapper has been properly registered and we // can wrap primitives. TEST_F(PcqSignatureConfigTest, PublicKeyVerifyWrapperRegistered) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used"; }
diff --git a/cc/experimental/pqcrypto/signature/sphincs_key_template.cc b/cc/experimental/pqcrypto/signature/sphincs_key_template.cc index 038b9c2..fb07ad9 100644 --- a/cc/experimental/pqcrypto/signature/sphincs_key_template.cc +++ b/cc/experimental/pqcrypto/signature/sphincs_key_template.cc
@@ -72,7 +72,7 @@ using ::google::crypto::tink::SphincsSignatureType; using ::google::crypto::tink::SphincsVariant; -KeyTemplate* NewSphincsKeyTemplate(int32 private_key_size, +KeyTemplate* NewSphincsKeyTemplate(int32_t private_key_size, SphincsHashType hash_type, SphincsVariant variant, SphincsSignatureType type) {
diff --git a/cc/experimental/pqcrypto/signature/sphincs_key_template_test.cc b/cc/experimental/pqcrypto/signature/sphincs_key_template_test.cc index 071feb4..90cfdd4 100644 --- a/cc/experimental/pqcrypto/signature/sphincs_key_template_test.cc +++ b/cc/experimental/pqcrypto/signature/sphincs_key_template_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/sphincs_key_template.h" +#include <memory> #include <string> #include "gmock/gmock.h"
diff --git a/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager.cc b/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager.cc index c481ae4..b5087ad 100644 --- a/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager.cc +++ b/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager.cc
@@ -16,6 +16,8 @@ #include "tink/experimental/pqcrypto/signature/sphincs_sign_key_manager.h" +#include <memory> + #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h"
diff --git a/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager.h b/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager.h index 03655e7..9c9d096 100644 --- a/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager.h +++ b/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager.h
@@ -17,6 +17,7 @@ #ifndef TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SPHINCS_SIGN_KEY_MANAGER_H_ #define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SPHINCS_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager_test.cc b/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager_test.cc index 7005f48..4b15c60 100644 --- a/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager_test.cc +++ b/cc/experimental/pqcrypto/signature/sphincs_sign_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/sphincs_sign_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h" @@ -104,7 +105,7 @@ using SphincsSignKeyManagerTest = testing::TestWithParam<SphincsTestCase>; // Helper function that returns a valid sphincs key format. -StatusOr<SphincsKeyFormat> CreateValidKeyFormat(int32 private_key_size, +StatusOr<SphincsKeyFormat> CreateValidKeyFormat(int32_t private_key_size, SphincsHashType hash_type, SphincsVariant variant, SphincsSignatureType type) {
diff --git a/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager.cc b/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager.cc index 438ccf3..0acf19c 100644 --- a/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager.cc +++ b/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager.cc
@@ -16,6 +16,8 @@ #include "tink/experimental/pqcrypto/signature/sphincs_verify_key_manager.h" +#include <memory> + #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h"
diff --git a/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager.h b/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager.h index d0d2283..70fe0b3 100644 --- a/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager.h +++ b/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager.h
@@ -17,6 +17,7 @@ #ifndef TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SPHINCS_VERIFY_KEY_MANAGER_H_ #define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SPHINCS_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager_test.cc b/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager_test.cc index b17f57c..f8b8d56 100644 --- a/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager_test.cc +++ b/cc/experimental/pqcrypto/signature/sphincs_verify_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/sphincs_verify_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h" @@ -102,7 +103,7 @@ using SphincsVerifyKeyManagerTest = testing::TestWithParam<SphincsTestCase>; // Helper function that returns a valid sphincs private key. -StatusOr<SphincsPrivateKey> CreateValidPrivateKey(int32 private_key_size, +StatusOr<SphincsPrivateKey> CreateValidPrivateKey(int32_t private_key_size, SphincsHashType hash_type, SphincsVariant variant, SphincsSignatureType type) { @@ -117,7 +118,7 @@ } // Helper function that returns a valid sphincs public key. -StatusOr<SphincsPublicKey> CreateValidPublicKey(int32 private_key_size, +StatusOr<SphincsPublicKey> CreateValidPublicKey(int32_t private_key_size, SphincsHashType hash_type, SphincsVariant variant, SphincsSignatureType type) {
diff --git a/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign.cc b/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign.cc index 6fd6d82..3e7e66a 100644 --- a/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign.cc +++ b/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign.cc
@@ -20,6 +20,7 @@ #include <cstddef> #include <cstdint> #include <iterator> +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign_test.cc b/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign_test.cc index a0dcba5..fb45802 100644 --- a/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign_test.cc +++ b/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify.cc b/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify.cc index b12c44f..d984a75 100644 --- a/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify.cc +++ b/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify.cc
@@ -19,6 +19,7 @@ #include <algorithm> #include <cstddef> #include <iterator> +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify_test.cc b/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify_test.cc index 6d90fc3..9caf4d1 100644 --- a/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify_test.cc +++ b/cc/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/signature/subtle/falcon_sign.cc b/cc/experimental/pqcrypto/signature/subtle/falcon_sign.cc index 63196a2..af83e92 100644 --- a/cc/experimental/pqcrypto/signature/subtle/falcon_sign.cc +++ b/cc/experimental/pqcrypto/signature/subtle/falcon_sign.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/falcon_sign.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/signature/subtle/falcon_sign_test.cc b/cc/experimental/pqcrypto/signature/subtle/falcon_sign_test.cc index 639a69a..a23a1f6 100644 --- a/cc/experimental/pqcrypto/signature/subtle/falcon_sign_test.cc +++ b/cc/experimental/pqcrypto/signature/subtle/falcon_sign_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/falcon_sign.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/signature/subtle/falcon_subtle_utils.h b/cc/experimental/pqcrypto/signature/subtle/falcon_subtle_utils.h index 302803a..81b1e08 100644 --- a/cc/experimental/pqcrypto/signature/subtle/falcon_subtle_utils.h +++ b/cc/experimental/pqcrypto/signature/subtle/falcon_subtle_utils.h
@@ -102,7 +102,7 @@ // This is an utility function that generates a new Falcon key pair. // This function is expected to be called from a key manager class. crypto::tink::util::StatusOr<FalconKeyPair> GenerateFalconKeyPair( - int32 private_key_size); + int32_t private_key_size); // Validates whether the private key size is safe to use for falcon signature. crypto::tink::util::Status ValidateFalconPrivateKeySize(int32_t key_size);
diff --git a/cc/experimental/pqcrypto/signature/subtle/falcon_subtle_utils_test.cc b/cc/experimental/pqcrypto/signature/subtle/falcon_subtle_utils_test.cc index 0b05363..552a463 100644 --- a/cc/experimental/pqcrypto/signature/subtle/falcon_subtle_utils_test.cc +++ b/cc/experimental/pqcrypto/signature/subtle/falcon_subtle_utils_test.cc
@@ -19,6 +19,7 @@ #include <climits> #include <string> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/experimental/pqcrypto/signature/subtle/falcon_verify.cc b/cc/experimental/pqcrypto/signature/subtle/falcon_verify.cc index 1284c30..6ad208f 100644 --- a/cc/experimental/pqcrypto/signature/subtle/falcon_verify.cc +++ b/cc/experimental/pqcrypto/signature/subtle/falcon_verify.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/falcon_verify.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/signature/subtle/falcon_verify_test.cc b/cc/experimental/pqcrypto/signature/subtle/falcon_verify_test.cc index c1cad8d..0372e2d 100644 --- a/cc/experimental/pqcrypto/signature/subtle/falcon_verify_test.cc +++ b/cc/experimental/pqcrypto/signature/subtle/falcon_verify_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/falcon_verify.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/signature/subtle/sphincs_helper_pqclean.h b/cc/experimental/pqcrypto/signature/subtle/sphincs_helper_pqclean.h index 4e8c99e..12d71ea 100644 --- a/cc/experimental/pqcrypto/signature/subtle/sphincs_helper_pqclean.h +++ b/cc/experimental/pqcrypto/signature/subtle/sphincs_helper_pqclean.h
@@ -34,7 +34,7 @@ SphincsHelperPqclean(const SphincsHelperPqclean &other) = delete; SphincsHelperPqclean &operator=(const SphincsHelperPqclean &other) = delete; - virtual ~SphincsHelperPqclean() {} + virtual ~SphincsHelperPqclean() = default; // Arguments: // sig - output signature (allocated buffer of size at least
diff --git a/cc/experimental/pqcrypto/signature/subtle/sphincs_sign.cc b/cc/experimental/pqcrypto/signature/subtle/sphincs_sign.cc index ee8257d..48d96f1 100644 --- a/cc/experimental/pqcrypto/signature/subtle/sphincs_sign.cc +++ b/cc/experimental/pqcrypto/signature/subtle/sphincs_sign.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/sphincs_sign.h" +#include <memory> #include <string> #include <utility> @@ -51,7 +52,7 @@ } util::StatusOr<std::string> SphincsSign::Sign(absl::string_view data) const { - util::StatusOr<int32> key_size_index = + util::StatusOr<int32_t> key_size_index = SphincsKeySizeToIndex(key_.GetKey().size()); if (!key_size_index.ok()) { return key_size_index.status();
diff --git a/cc/experimental/pqcrypto/signature/subtle/sphincs_sign_test.cc b/cc/experimental/pqcrypto/signature/subtle/sphincs_sign_test.cc index 58df17c..070190b 100644 --- a/cc/experimental/pqcrypto/signature/subtle/sphincs_sign_test.cc +++ b/cc/experimental/pqcrypto/signature/subtle/sphincs_sign_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/sphincs_sign.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/experimental/pqcrypto/signature/subtle/sphincs_subtle_utils.h b/cc/experimental/pqcrypto/signature/subtle/sphincs_subtle_utils.h index 95fa9e9..964638c 100644 --- a/cc/experimental/pqcrypto/signature/subtle/sphincs_subtle_utils.h +++ b/cc/experimental/pqcrypto/signature/subtle/sphincs_subtle_utils.h
@@ -62,7 +62,7 @@ SphincsHashType hash_type; SphincsVariant variant; SphincsSignatureType sig_length_type; - int32 private_key_size; + int32_t private_key_size; }; // Representation of the Sphincs private key. @@ -127,10 +127,10 @@ SphincsParamsPqclean params); // Validates whether the private key size is safe to use for sphincs signature. -crypto::tink::util::Status ValidatePrivateKeySize(int32 key_size); +crypto::tink::util::Status ValidatePrivateKeySize(int32_t key_size); // Validates whether the public key size is safe to use for sphincs signature. -crypto::tink::util::Status ValidatePublicKeySize(int32 key_size); +crypto::tink::util::Status ValidatePublicKeySize(int32_t key_size); // Validates whether the parameters are safe to use for sphincs signature. crypto::tink::util::Status ValidateParams(SphincsParamsPqclean params); @@ -138,7 +138,7 @@ // Convert the sphincs private key size to the appropiate index in the // pqclean functions array. -crypto::tink::util::StatusOr<int32> SphincsKeySizeToIndex(int32 key_size); +crypto::tink::util::StatusOr<int32_t> SphincsKeySizeToIndex(int32_t key_size); } // namespace subtle } // namespace tink
diff --git a/cc/experimental/pqcrypto/signature/subtle/sphincs_verify.cc b/cc/experimental/pqcrypto/signature/subtle/sphincs_verify.cc index 88379fa..6582b86 100644 --- a/cc/experimental/pqcrypto/signature/subtle/sphincs_verify.cc +++ b/cc/experimental/pqcrypto/signature/subtle/sphincs_verify.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/sphincs_verify.h" +#include <memory> #include <string> #include <utility> @@ -54,7 +55,7 @@ util::Status SphincsVerify::Verify(absl::string_view signature, absl::string_view data) const { SphincsParamsPqclean params = key_.GetParams(); - util::StatusOr<int32> key_size_index = + util::StatusOr<int32_t> key_size_index = SphincsKeySizeToIndex(params.private_key_size); if (!key_size_index.ok()) { return key_size_index.status();
diff --git a/cc/experimental/pqcrypto/signature/subtle/sphincs_verify_test.cc b/cc/experimental/pqcrypto/signature/subtle/sphincs_verify_test.cc index 544db98..f0a48c5 100644 --- a/cc/experimental/pqcrypto/signature/subtle/sphincs_verify_test.cc +++ b/cc/experimental/pqcrypto/signature/subtle/sphincs_verify_test.cc
@@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/subtle/sphincs_verify.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/extensions.bzl b/cc/extensions.bzl new file mode 100644 index 0000000..6ab6fc7 --- /dev/null +++ b/cc/extensions.bzl
@@ -0,0 +1,30 @@ +# Copyright 2023 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. + +"""Tink C++ Bazel Module extensions.""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +def _wycheproof_impl(_ctx): + # Commit from 2019-12-17. + http_archive( + name = "wycheproof", + strip_prefix = "wycheproof-d8ed1ba95ac4c551db67f410c06131c3bc00a97c", + url = "https://github.com/google/wycheproof/archive/d8ed1ba95ac4c551db67f410c06131c3bc00a97c.zip", + sha256 = "eb1d558071acf1aa6d677d7f1cabec2328d1cf8381496c17185bd92b52ce7545", + ) + +wycheproof_extension = module_extension( + implementation = _wycheproof_impl, +)
diff --git a/cc/hybrid/BUILD.bazel b/cc/hybrid/BUILD.bazel index 27c119e..209de7b 100644 --- a/cc/hybrid/BUILD.bazel +++ b/cc/hybrid/BUILD.bazel
@@ -149,22 +149,27 @@ deps = [ "//:aead", "//:deterministic_aead", - "//:key_manager", - "//:registry", + "//aead:aes_ctr_hmac_aead_key_manager", "//daead/subtle:aead_or_daead", "//proto:aes_ctr_cc_proto", "//proto:aes_ctr_hmac_aead_cc_proto", "//proto:aes_gcm_cc_proto", "//proto:aes_siv_cc_proto", + "//proto:common_cc_proto", "//proto:hmac_cc_proto", "//proto:tink_cc_proto", "//proto:xchacha20_poly1305_cc_proto", + "//subtle:aes_gcm_boringssl", + "//subtle:aes_siv_boringssl", + "//subtle:xchacha20_poly1305_boringssl", "//util:errors", "//util:protobuf_helper", "//util:secret_data", "//util:statusor", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", ], ) @@ -284,15 +289,15 @@ ":ecies_aead_hkdf_public_key_manager", ":hybrid_config", ":hybrid_key_templates", - "//:config", "//:hybrid_decrypt", "//:hybrid_encrypt", "//:keyset_handle", "//:registry", - "//config:tink_fips", + "//internal:fips_utils", "//util:status", "//util:test_matchers", "//util:test_util", + "@boringssl//:crypto", "@com_google_absl//absl/status", "@com_google_googletest//:gtest_main", ], @@ -372,7 +377,6 @@ ":ecies_aead_hkdf_public_key_manager", ":hybrid_config", ":hybrid_decrypt_factory", - "//:config", "//:crypto_format", "//:hybrid_decrypt", "//:hybrid_encrypt", @@ -394,7 +398,6 @@ deps = [ ":hybrid_config", ":hybrid_encrypt_factory", - "//:config", "//:crypto_format", "//:hybrid_encrypt", "//:keyset_handle", @@ -433,7 +436,6 @@ srcs = ["ecies_aead_hkdf_dem_helper_test.cc"], deps = [ ":ecies_aead_hkdf_dem_helper", - "//:registry", "//aead:aes_gcm_key_manager", "//daead:aes_siv_key_manager", "//util:secret_data", @@ -452,7 +454,6 @@ ":ecies_aead_hkdf_hybrid_decrypt", ":ecies_aead_hkdf_hybrid_encrypt", "//:hybrid_decrypt", - "//:registry", "//aead:aes_ctr_hmac_aead_key_manager", "//aead:aes_gcm_key_manager", "//aead:xchacha20_poly1305_key_manager", @@ -479,7 +480,6 @@ deps = [ ":ecies_aead_hkdf_hybrid_encrypt", "//:hybrid_encrypt", - "//:registry", "//aead:aes_gcm_key_manager", "//internal:ec_util", "//proto:common_cc_proto",
diff --git a/cc/hybrid/BUILD.gn b/cc/hybrid/BUILD.gn index f796359..9a507bc 100644 --- a/cc/hybrid/BUILD.gn +++ b/cc/hybrid/BUILD.gn
@@ -176,18 +176,23 @@ public_deps = [ "//third_party/abseil-cpp/absl/memory:memory", "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/status:statusor", + "//third_party/abseil-cpp/absl/strings:strings", "//third_party/tink/cc:aead", "//third_party/tink/cc:deterministic_aead", - "//third_party/tink/cc:key_manager", - "//third_party/tink/cc:registry", + "//third_party/tink/cc/aead:aes_ctr_hmac_aead_key_manager", "//third_party/tink/cc/daead/subtle:aead_or_daead", "//third_party/tink/cc/proto:aes_ctr_hmac_aead_proto", "//third_party/tink/cc/proto:aes_ctr_proto", "//third_party/tink/cc/proto:aes_gcm_proto", "//third_party/tink/cc/proto:aes_siv_proto", + "//third_party/tink/cc/proto:common_proto", "//third_party/tink/cc/proto:hmac_proto", "//third_party/tink/cc/proto:tink_proto", "//third_party/tink/cc/proto:xchacha20_poly1305_proto", + "//third_party/tink/cc/subtle:aes_gcm_boringssl", + "//third_party/tink/cc/subtle:aes_siv_boringssl", + "//third_party/tink/cc/subtle:xchacha20_poly1305_boringssl", "//third_party/tink/cc/util:errors", "//third_party/tink/cc/util:protobuf_helper", "//third_party/tink/cc/util:secret_data",
diff --git a/cc/hybrid/CMakeLists.txt b/cc/hybrid/CMakeLists.txt index 67e17b7..cd213c8 100644 --- a/cc/hybrid/CMakeLists.txt +++ b/cc/hybrid/CMakeLists.txt
@@ -137,11 +137,15 @@ DEPS absl::memory absl::status + absl::statusor + absl::strings tink::core::aead tink::core::deterministic_aead - tink::core::key_manager - tink::core::registry + tink::aead::aes_ctr_hmac_aead_key_manager tink::daead::subtle::aead_or_daead + tink::subtle::aes_gcm_boringssl + tink::subtle::aes_siv_boringssl + tink::subtle::xchacha20_poly1305_boringssl tink::util::errors tink::util::protobuf_helper tink::util::secret_data @@ -150,6 +154,7 @@ tink::proto::aes_ctr_hmac_aead_cc_proto tink::proto::aes_gcm_cc_proto tink::proto::aes_siv_cc_proto + tink::proto::common_cc_proto tink::proto::hmac_cc_proto tink::proto::tink_cc_proto tink::proto::xchacha20_poly1305_cc_proto @@ -258,12 +263,12 @@ tink::hybrid::hybrid_key_templates gmock absl::status - tink::core::config + crypto tink::core::hybrid_decrypt tink::core::hybrid_encrypt tink::core::keyset_handle tink::core::registry - tink::config::tink_fips + tink::internal::fips_utils tink::util::status tink::util::test_matchers tink::util::test_util @@ -343,7 +348,6 @@ tink::hybrid::hybrid_decrypt_factory gmock absl::memory - tink::core::config tink::core::crypto_format tink::core::hybrid_decrypt tink::core::hybrid_encrypt @@ -363,7 +367,6 @@ tink::hybrid::hybrid_config tink::hybrid::hybrid_encrypt_factory gmock - tink::core::config tink::core::crypto_format tink::core::hybrid_encrypt tink::core::keyset_handle @@ -403,7 +406,6 @@ tink::hybrid::ecies_aead_hkdf_dem_helper gmock absl::status - tink::core::registry tink::aead::aes_gcm_key_manager tink::daead::aes_siv_key_manager tink::util::secret_data @@ -421,7 +423,6 @@ gmock absl::memory tink::core::hybrid_decrypt - tink::core::registry tink::aead::aes_ctr_hmac_aead_key_manager tink::aead::aes_gcm_key_manager tink::aead::xchacha20_poly1305_key_manager @@ -447,7 +448,6 @@ gmock absl::memory tink::core::hybrid_encrypt - tink::core::registry tink::aead::aes_gcm_key_manager tink::internal::ec_util tink::util::enums
diff --git a/cc/hybrid/ecies_aead_hkdf_dem_helper.cc b/cc/hybrid/ecies_aead_hkdf_dem_helper.cc index 9c169e1..6e46f4a 100644 --- a/cc/hybrid/ecies_aead_hkdf_dem_helper.cc +++ b/cc/hybrid/ecies_aead_hkdf_dem_helper.cc
@@ -16,15 +16,22 @@ #include "tink/hybrid/ecies_aead_hkdf_dem_helper.h" +#include <stdint.h> + +#include <memory> #include <string> #include <utility> #include "absl/memory/memory.h" #include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" #include "tink/aead.h" +#include "tink/aead/aes_ctr_hmac_aead_key_manager.h" #include "tink/deterministic_aead.h" -#include "tink/key_manager.h" -#include "tink/registry.h" +#include "tink/subtle/aes_gcm_boringssl.h" +#include "tink/subtle/aes_siv_boringssl.h" +#include "tink/subtle/xchacha20_poly1305_boringssl.h" #include "tink/util/errors.h" #include "tink/util/protobuf_helper.h" #include "tink/util/statusor.h" @@ -43,69 +50,28 @@ using ::crypto::tink::subtle::AeadOrDaead; using ::google::crypto::tink::AesCtrHmacAeadKey; using ::google::crypto::tink::AesCtrHmacAeadKeyFormat; -using ::google::crypto::tink::AesGcmKey; using ::google::crypto::tink::AesGcmKeyFormat; -using ::google::crypto::tink::AesSivKey; using ::google::crypto::tink::AesSivKeyFormat; using ::google::crypto::tink::KeyTemplate; -using ::google::crypto::tink::XChaCha20Poly1305Key; using ::google::crypto::tink::XChaCha20Poly1305KeyFormat; -// Internal implementaton of the EciesAeadHkdfDemHelper class, paremetrized by -// the Primitive used for data encapsulation (i.e Aead or DeterministicAead). -template <class EncryptionPrimitive> -class EciesAeadHkdfDemHelperImpl : public EciesAeadHkdfDemHelper { - public: - static util::StatusOr<std::unique_ptr<const EciesAeadHkdfDemHelper>> New( - const google::crypto::tink::KeyTemplate& dem_key_template, - const DemKeyParams& key_params, const std::string& dem_type_url) { - auto key_manager_or = - Registry::get_key_manager<EncryptionPrimitive>(dem_type_url); - if (!key_manager_or.ok()) { - return ToStatusF( - absl::StatusCode::kFailedPrecondition, - "No manager for DEM key type '%s' found in the registry.", - dem_type_url); - } - const KeyManager<EncryptionPrimitive>* key_manager = key_manager_or.value(); - return {absl::make_unique<EciesAeadHkdfDemHelperImpl<EncryptionPrimitive>>( - key_manager, dem_key_template, key_params)}; +crypto::tink::util::StatusOr<std::unique_ptr<AeadOrDaead>> Wrap( + crypto::tink::util::StatusOr<std::unique_ptr<crypto::tink::Aead>> aead_or) { + if (!aead_or.ok()) { + return aead_or.status(); } + return std::make_unique<AeadOrDaead>(std::move(aead_or.value())); +} - EciesAeadHkdfDemHelperImpl( - const KeyManager<EncryptionPrimitive>* key_manager, - const google::crypto::tink::KeyTemplate& key_template, - DemKeyParams key_params) - : EciesAeadHkdfDemHelper(key_template, key_params), - key_manager_(key_manager) {} - - protected: - crypto::tink::util::StatusOr< - std::unique_ptr<crypto::tink::subtle::AeadOrDaead>> - GetAeadOrDaead(const util::SecretData& symmetric_key_value) const override { - if (symmetric_key_value.size() != key_params_.key_size_in_bytes) { - return util::Status(absl::StatusCode::kInternal, - "Wrong length of symmetric key."); - } - auto key_or = key_manager_->get_key_factory().NewKey(key_template_.value()); - if (!key_or.ok()) return key_or.status(); - auto key = std::move(key_or).value(); - if (!ReplaceKeyBytes(symmetric_key_value, key.get())) { - return util::Status(absl::StatusCode::kInternal, - "Generation of DEM-key failed."); - } - - util::StatusOr<std::unique_ptr<EncryptionPrimitive>> primitive_or = - key_manager_->GetPrimitive(*key); - ZeroKeyBytes(key.get()); - - if (!primitive_or.ok()) return primitive_or.status(); - return absl::make_unique<AeadOrDaead>(std::move(primitive_or.value())); +crypto::tink::util::StatusOr<std::unique_ptr<AeadOrDaead>> Wrap( + crypto::tink::util::StatusOr< + std::unique_ptr<crypto::tink::DeterministicAead>> + daead_or) { + if (!daead_or.ok()) { + return daead_or.status(); } - - private: - const KeyManager<EncryptionPrimitive>* key_manager_; // not owned -}; + return std::make_unique<AeadOrDaead>(std::move(daead_or.value())); +} } // namespace @@ -129,7 +95,10 @@ uint32_t dem_key_size = key_format.aes_ctr_key_format().key_size() + key_format.hmac_key_format().key_size(); return {{AES_CTR_HMAC_AEAD_KEY, dem_key_size, - key_format.aes_ctr_key_format().key_size()}}; + key_format.aes_ctr_key_format().key_size(), + key_format.aes_ctr_key_format().params().iv_size(), + key_format.hmac_key_format().params().hash(), + key_format.hmac_key_format().params().tag_size()}}; } if (type_url == "type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key") { @@ -158,86 +127,41 @@ auto key_params_or = GetKeyParams(dem_key_template); if (!key_params_or.ok()) return key_params_or.status(); DemKeyParams key_params = key_params_or.value(); - const std::string& dem_type_url = dem_key_template.type_url(); - - if (key_params.key_type == AES_SIV_KEY) { - return EciesAeadHkdfDemHelperImpl<DeterministicAead>::New( - dem_key_template, key_params, dem_type_url); - } else { - return EciesAeadHkdfDemHelperImpl<Aead>::New(dem_key_template, key_params, - dem_type_url); - } + return absl::WrapUnique<const EciesAeadHkdfDemHelper>( + new EciesAeadHkdfDemHelper(dem_key_template, key_params)); } -bool EciesAeadHkdfDemHelper::ReplaceKeyBytes( - const util::SecretData& key_bytes, - portable_proto::MessageLite* proto) const { +crypto::tink::util::StatusOr<std::unique_ptr<AeadOrDaead>> +EciesAeadHkdfDemHelper::GetAeadOrDaead( + const util::SecretData& symmetric_key_value) const { + if (symmetric_key_value.size() != key_params_.key_size_in_bytes) { + return util::Status(absl::StatusCode::kInternal, + "Wrong length of symmetric key."); + } switch (key_params_.key_type) { - case AES_GCM_KEY: { - AesGcmKey* key = static_cast<AesGcmKey*>(proto); - key->set_key_value(std::string(util::SecretDataAsStringView(key_bytes))); - return true; - } + case AES_GCM_KEY: + return Wrap(subtle::AesGcmBoringSsl::New(symmetric_key_value)); case AES_CTR_HMAC_AEAD_KEY: { - AesCtrHmacAeadKey* key = static_cast<AesCtrHmacAeadKey*>(proto); - auto aes_ctr_key = key->mutable_aes_ctr_key(); + AesCtrHmacAeadKey key; + auto aes_ctr_key = key.mutable_aes_ctr_key(); + aes_ctr_key->mutable_params()->set_iv_size( + key_params_.aes_ctr_key_iv_size_in_bytes); aes_ctr_key->set_key_value( - std::string(util::SecretDataAsStringView(key_bytes).substr( - 0, key_params_.aes_ctr_key_size_in_bytes))); - auto hmac_key = key->mutable_hmac_key(); + std::string(util::SecretDataAsStringView(symmetric_key_value) + .substr(0, key_params_.aes_ctr_key_size_in_bytes))); + auto hmac_key = key.mutable_hmac_key(); + hmac_key->mutable_params()->set_tag_size( + key_params_.hmac_key_tag_size_in_bytes); + hmac_key->mutable_params()->set_hash(key_params_.hmac_key_hash); hmac_key->set_key_value( - std::string(util::SecretDataAsStringView(key_bytes).substr( - key_params_.aes_ctr_key_size_in_bytes))); - return true; + std::string(util::SecretDataAsStringView(symmetric_key_value) + .substr(key_params_.aes_ctr_key_size_in_bytes))); + return Wrap(AesCtrHmacAeadKeyManager().GetPrimitive<Aead>(key)); } - case XCHACHA20_POLY1305_KEY: { - XChaCha20Poly1305Key* key = static_cast<XChaCha20Poly1305Key*>(proto); - key->set_key_value(std::string(util::SecretDataAsStringView(key_bytes))); - return true; - } - case AES_SIV_KEY: { - AesSivKey* key = static_cast<AesSivKey*>(proto); - key->set_key_value(std::string(util::SecretDataAsStringView(key_bytes))); - return true; - } - } - return false; -} - -void EciesAeadHkdfDemHelper::ZeroKeyBytes( - portable_proto::MessageLite* proto) const { - switch (key_params_.key_type) { - case AES_GCM_KEY: { - AesGcmKey* key = static_cast<AesGcmKey*>(proto); - std::unique_ptr<std::string> key_value = - absl::WrapUnique(key->release_key_value()); - util::SafeZeroString(key_value.get()); - break; - } - case AES_CTR_HMAC_AEAD_KEY: { - AesCtrHmacAeadKey* key = static_cast<AesCtrHmacAeadKey*>(proto); - std::unique_ptr<std::string> aes_ctr_key_value = - absl::WrapUnique(key->mutable_aes_ctr_key()->release_key_value()); - util::SafeZeroString(aes_ctr_key_value.get()); - std::unique_ptr<std::string> hmac_key_value = - absl::WrapUnique(key->mutable_hmac_key()->release_key_value()); - util::SafeZeroString(hmac_key_value.get()); - break; - } - case XCHACHA20_POLY1305_KEY: { - XChaCha20Poly1305Key* key = static_cast<XChaCha20Poly1305Key*>(proto); - std::unique_ptr<std::string> key_value = - absl::WrapUnique(key->release_key_value()); - util::SafeZeroString(key_value.get()); - break; - } - case AES_SIV_KEY: { - AesSivKey* key = static_cast<AesSivKey*>(proto); - std::unique_ptr<std::string> key_value = - absl::WrapUnique(key->release_key_value()); - util::SafeZeroString(key_value.get()); - break; - } + case XCHACHA20_POLY1305_KEY: + return Wrap(subtle::XChacha20Poly1305BoringSsl::New(symmetric_key_value)); + case AES_SIV_KEY: + return Wrap(subtle::AesSivBoringSsl::New(symmetric_key_value)); } }
diff --git a/cc/hybrid/ecies_aead_hkdf_dem_helper.h b/cc/hybrid/ecies_aead_hkdf_dem_helper.h index 0f6d160..c00970b 100644 --- a/cc/hybrid/ecies_aead_hkdf_dem_helper.h +++ b/cc/hybrid/ecies_aead_hkdf_dem_helper.h
@@ -17,14 +17,16 @@ #ifndef TINK_HYBRID_ECIES_AEAD_HKDF_DEM_HELPER_H_ #define TINK_HYBRID_ECIES_AEAD_HKDF_DEM_HELPER_H_ +#include <stdint.h> + #include <memory> #include "tink/aead.h" #include "tink/daead/subtle/aead_or_daead.h" -#include "tink/key_manager.h" #include "tink/util/protobuf_helper.h" #include "tink/util/secret_data.h" #include "tink/util/statusor.h" +#include "proto/common.pb.h" #include "proto/tink.pb.h" namespace crypto { @@ -38,7 +40,7 @@ crypto::tink::util::StatusOr<std::unique_ptr<const EciesAeadHkdfDemHelper>> New(const google::crypto::tink::KeyTemplate& dem_key_template); - virtual ~EciesAeadHkdfDemHelper() {} + virtual ~EciesAeadHkdfDemHelper() = default; // Returns the size of the DEM-key in bytes. uint32_t dem_key_size_in_bytes() const { @@ -50,7 +52,7 @@ // be of length dem_key_size_in_bytes(). virtual crypto::tink::util::StatusOr< std::unique_ptr<crypto::tink::subtle::AeadOrDaead>> - GetAeadOrDaead(const util::SecretData& symmetric_key_value) const = 0; + GetAeadOrDaead(const util::SecretData& symmetric_key_value) const; protected: enum DemKeyType { @@ -64,6 +66,9 @@ DemKeyType key_type; uint32_t key_size_in_bytes; uint32_t aes_ctr_key_size_in_bytes; + uint32_t aes_ctr_key_iv_size_in_bytes; + google::crypto::tink::HashType hmac_key_hash; + uint32_t hmac_key_tag_size_in_bytes; }; EciesAeadHkdfDemHelper(const google::crypto::tink::KeyTemplate& key_template, @@ -73,11 +78,6 @@ static util::StatusOr<DemKeyParams> GetKeyParams( const ::google::crypto::tink::KeyTemplate& key_template); - bool ReplaceKeyBytes(const util::SecretData& key_bytes, - portable_proto::MessageLite* proto) const; - - void ZeroKeyBytes(portable_proto::MessageLite* proto) const; - const google::crypto::tink::KeyTemplate key_template_; const DemKeyParams key_params_; };
diff --git a/cc/hybrid/ecies_aead_hkdf_dem_helper_test.cc b/cc/hybrid/ecies_aead_hkdf_dem_helper_test.cc index 64393ad..a2bbd3a 100644 --- a/cc/hybrid/ecies_aead_hkdf_dem_helper_test.cc +++ b/cc/hybrid/ecies_aead_hkdf_dem_helper_test.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/ecies_aead_hkdf_dem_helper.h" +#include <memory> #include <string> #include <utility> @@ -24,7 +25,6 @@ #include "absl/status/status.h" #include "tink/aead/aes_gcm_key_manager.h" #include "tink/daead/aes_siv_key_manager.h" -#include "tink/registry.h" #include "tink/util/secret_data.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" @@ -69,8 +69,6 @@ key_format.set_key_size(16); std::unique_ptr<AesGcmKeyManager> key_manager(new AesGcmKeyManager()); std::string dem_key_type = key_manager->get_key_type(); - ASSERT_THAT(Registry::RegisterKeyTypeManager(std::move(key_manager), true), - IsOk()); google::crypto::tink::KeyTemplate dem_key_template; dem_key_template.set_type_url(dem_key_type); @@ -96,8 +94,6 @@ key_format.set_key_size(64); std::unique_ptr<AesSivKeyManager> key_manager(new AesSivKeyManager()); std::string dem_key_type = key_manager->get_key_type(); - ASSERT_THAT(Registry::RegisterKeyTypeManager(std::move(key_manager), true), - IsOk()); google::crypto::tink::KeyTemplate dem_key_template; dem_key_template.set_type_url(dem_key_type);
diff --git a/cc/hybrid/ecies_aead_hkdf_hybrid_decrypt.cc b/cc/hybrid/ecies_aead_hkdf_hybrid_decrypt.cc index 497b95f..d0c1377 100644 --- a/cc/hybrid/ecies_aead_hkdf_hybrid_decrypt.cc +++ b/cc/hybrid/ecies_aead_hkdf_hybrid_decrypt.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/ecies_aead_hkdf_hybrid_decrypt.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/hybrid/ecies_aead_hkdf_hybrid_decrypt_test.cc b/cc/hybrid/ecies_aead_hkdf_hybrid_decrypt_test.cc index 44b79c2..6bfc624 100644 --- a/cc/hybrid/ecies_aead_hkdf_hybrid_decrypt_test.cc +++ b/cc/hybrid/ecies_aead_hkdf_hybrid_decrypt_test.cc
@@ -16,8 +16,10 @@ #include "tink/hybrid/ecies_aead_hkdf_hybrid_decrypt.h" +#include <memory> #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/memory/memory.h" @@ -29,7 +31,6 @@ #include "tink/hybrid_decrypt.h" #include "tink/internal/ec_util.h" #include "tink/internal/ssl_util.h" -#include "tink/registry.h" #include "tink/subtle/random.h" #include "tink/util/enums.h" #include "tink/util/statusor.h" @@ -215,21 +216,6 @@ } } -TEST_F(EciesAeadHkdfHybridDecryptTest, testGettingHybridEncryptWithoutManager) { - // Prepare an ECIES key. - Registry::Reset(); - auto ecies_key = test::GetEciesAesGcmHkdfTestKey(EllipticCurveType::NIST_P256, - EcPointFormat::UNCOMPRESSED, - HashType::SHA256, 32); - - // Try to get a HybridEncrypt primitive without DEM key manager. - auto bad_result(EciesAeadHkdfHybridDecrypt::New(ecies_key)); - EXPECT_FALSE(bad_result.ok()); - EXPECT_EQ(absl::StatusCode::kFailedPrecondition, bad_result.status().code()); - EXPECT_PRED_FORMAT2(testing::IsSubstring, "No manager for DEM", - std::string(bad_result.status().message())); -} - TEST_F(EciesAeadHkdfHybridDecryptTest, testAesGcmHybridDecryption) { // Register DEM key manager. std::string dem_key_type = AesGcmKeyManager().get_key_type();
diff --git a/cc/hybrid/ecies_aead_hkdf_hybrid_encrypt.cc b/cc/hybrid/ecies_aead_hkdf_hybrid_encrypt.cc index 2f70a7c..a8af6c6 100644 --- a/cc/hybrid/ecies_aead_hkdf_hybrid_encrypt.cc +++ b/cc/hybrid/ecies_aead_hkdf_hybrid_encrypt.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/ecies_aead_hkdf_hybrid_encrypt.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/hybrid/ecies_aead_hkdf_hybrid_encrypt_test.cc b/cc/hybrid/ecies_aead_hkdf_hybrid_encrypt_test.cc index f2c4277..610fa5a 100644 --- a/cc/hybrid/ecies_aead_hkdf_hybrid_encrypt_test.cc +++ b/cc/hybrid/ecies_aead_hkdf_hybrid_encrypt_test.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/ecies_aead_hkdf_hybrid_encrypt.h" +#include <memory> #include <string> #include <utility> @@ -24,7 +25,6 @@ #include "tink/aead/aes_gcm_key_manager.h" #include "tink/hybrid_encrypt.h" #include "tink/internal/ec_util.h" -#include "tink/registry.h" #include "tink/util/enums.h" #include "tink/util/statusor.h" #include "tink/util/test_util.h" @@ -113,19 +113,6 @@ HashType::SHA256, 32); - // Try to get a HybridEncrypt primitive without DEM key manager. - auto bad_result(EciesAeadHkdfHybridEncrypt::New(ecies_key.public_key())); - EXPECT_FALSE(bad_result.ok()); - EXPECT_EQ(absl::StatusCode::kFailedPrecondition, bad_result.status().code()); - EXPECT_PRED_FORMAT2(testing::IsSubstring, "No manager for DEM", - std::string(bad_result.status().message())); - - // Register DEM key manager. - ASSERT_TRUE(Registry::RegisterKeyTypeManager( - absl::make_unique<AesGcmKeyManager>(), true) - .ok()); - std::string dem_key_type = AesGcmKeyManager().get_key_type(); - // Generate and test many keys with various parameters. std::string plaintext = "some plaintext"; std::string context_info = "some context info";
diff --git a/cc/hybrid/ecies_aead_hkdf_private_key_manager.h b/cc/hybrid/ecies_aead_hkdf_private_key_manager.h index 810f37c..ec82996 100644 --- a/cc/hybrid/ecies_aead_hkdf_private_key_manager.h +++ b/cc/hybrid/ecies_aead_hkdf_private_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_HYBRID_ECIES_AEAD_HKDF_PRIVATE_KEY_MANAGER_H_ #define TINK_HYBRID_ECIES_AEAD_HKDF_PRIVATE_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/hybrid/ecies_aead_hkdf_public_key_manager.h b/cc/hybrid/ecies_aead_hkdf_public_key_manager.h index 10ecedf..9c6d671 100644 --- a/cc/hybrid/ecies_aead_hkdf_public_key_manager.h +++ b/cc/hybrid/ecies_aead_hkdf_public_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_HYBRID_ECIES_AEAD_HKDF_PUBLIC_KEY_MANAGER_H_ #define TINK_HYBRID_ECIES_AEAD_HKDF_PUBLIC_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/hybrid/failing_hybrid.cc b/cc/hybrid/failing_hybrid.cc index 0496d17..6cda606 100644 --- a/cc/hybrid/failing_hybrid.cc +++ b/cc/hybrid/failing_hybrid.cc
@@ -15,11 +15,12 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/hybrid/failing_hybrid.h" +#include <memory> #include <string> #include <utility> -#include "tink/hybrid_encrypt.h" #include "absl/strings/string_view.h" +#include "tink/hybrid_encrypt.h" namespace crypto { namespace tink {
diff --git a/cc/hybrid/failing_hybrid.h b/cc/hybrid/failing_hybrid.h index 89e35ff..9009d2c 100644 --- a/cc/hybrid/failing_hybrid.h +++ b/cc/hybrid/failing_hybrid.h
@@ -16,6 +16,7 @@ #ifndef TINK_HYBRID_FAILING_HYBRID_H_ #define TINK_HYBRID_FAILING_HYBRID_H_ +#include <memory> #include <string> #include "absl/strings/string_view.h"
diff --git a/cc/hybrid/hybrid_config.cc b/cc/hybrid/hybrid_config.cc index 8a3bb0e..bb7dcac 100644 --- a/cc/hybrid/hybrid_config.cc +++ b/cc/hybrid/hybrid_config.cc
@@ -28,20 +28,13 @@ #include "tink/util/status.h" #include "proto/config.pb.h" -using google::crypto::tink::RegistryConfig; - namespace crypto { namespace tink { // static -const RegistryConfig& HybridConfig::Latest() { - static const RegistryConfig* config = new RegistryConfig(); - return *config; -} - -// static util::Status HybridConfig::Register() { auto status = AeadConfig::Register(); + if (!status.ok()) return status; // Register primitive wrappers. status = Registry::RegisterPrimitiveWrapper(
diff --git a/cc/hybrid/hybrid_config.h b/cc/hybrid/hybrid_config.h index fdfbc37..adea356 100644 --- a/cc/hybrid/hybrid_config.h +++ b/cc/hybrid/hybrid_config.h
@@ -37,16 +37,6 @@ // class HybridConfig { public: - static constexpr char kHybridDecryptCatalogueName[] = "TinkHybridDecrypt"; - static constexpr char kHybridDecryptPrimitiveName[] = "HybridDecrypt"; - static constexpr char kHybridEncryptCatalogueName[] = "TinkHybridEncrypt"; - static constexpr char kHybridEncryptPrimitiveName[] = "HybridEncrypt"; - - // Returns config with implementations of HybridEncrypt and HybridDecrypt - // supported in the current Tink release. - ABSL_DEPRECATED("This is not supported anymore.") - static const google::crypto::tink::RegistryConfig& Latest(); - // Registers HybridEncrypt and HybridDecrypt primitive wrappers, and key // managers for all implementations of HybridEncrypt and HybridDecrypt from // the current Tink release.
diff --git a/cc/hybrid/hybrid_config_test.cc b/cc/hybrid/hybrid_config_test.cc index d09a281..bb5467c 100644 --- a/cc/hybrid/hybrid_config_test.cc +++ b/cc/hybrid/hybrid_config_test.cc
@@ -23,13 +23,13 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/status/status.h" -#include "tink/config.h" -#include "tink/config/tink_fips.h" +#include "openssl/crypto.h" #include "tink/hybrid/ecies_aead_hkdf_private_key_manager.h" #include "tink/hybrid/ecies_aead_hkdf_public_key_manager.h" #include "tink/hybrid/hybrid_key_templates.h" #include "tink/hybrid_decrypt.h" #include "tink/hybrid_encrypt.h" +#include "tink/internal/fips_utils.h" #include "tink/keyset_handle.h" #include "tink/registry.h" #include "tink/util/status.h" @@ -51,7 +51,7 @@ }; TEST_F(HybridConfigTest, Basic) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Not supported in FIPS-only mode"; } @@ -77,7 +77,7 @@ // Tests that the HybridEncryptWrapper has been properly registered and we // can wrap primitives. TEST_F(HybridConfigTest, EncryptWrapperRegistered) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Not supported in FIPS-only mode"; } @@ -112,7 +112,7 @@ // Tests that the HybridDecryptWrapper has been properly registered and we // can wrap primitives. TEST_F(HybridConfigTest, DecryptWrapperRegistered) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Not supported in FIPS-only mode"; } @@ -146,8 +146,8 @@ // FIPS-only mode tests TEST_F(HybridConfigTest, RegisterNonFipsTemplates) { - if (!IsFipsModeEnabled()) { - GTEST_SKIP() << "Only supported in FIPS-only mode"; + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() << "Only supported in FIPS-only mode with BoringCrypto"; } EXPECT_THAT(HybridConfig::Register(), IsOk());
diff --git a/cc/hybrid/hybrid_decrypt_factory.cc b/cc/hybrid/hybrid_decrypt_factory.cc index 401d257..0cb9d26 100644 --- a/cc/hybrid/hybrid_decrypt_factory.cc +++ b/cc/hybrid/hybrid_decrypt_factory.cc
@@ -16,15 +16,16 @@ #include "tink/hybrid/hybrid_decrypt_factory.h" +#include <memory> + +#include "tink/hybrid/hybrid_decrypt_wrapper.h" #include "tink/hybrid_decrypt.h" #include "tink/key_manager.h" #include "tink/keyset_handle.h" #include "tink/registry.h" -#include "tink/hybrid/hybrid_decrypt_wrapper.h" #include "tink/util/status.h" #include "tink/util/statusor.h" - namespace crypto { namespace tink {
diff --git a/cc/hybrid/hybrid_decrypt_factory.h b/cc/hybrid/hybrid_decrypt_factory.h index 5218697..d2e3e43 100644 --- a/cc/hybrid/hybrid_decrypt_factory.h +++ b/cc/hybrid/hybrid_decrypt_factory.h
@@ -17,6 +17,8 @@ #ifndef TINK_HYBRID_HYBRID_DECRYPT_FACTORY_H_ #define TINK_HYBRID_HYBRID_DECRYPT_FACTORY_H_ +#include <memory> + #include "absl/base/macros.h" #include "tink/hybrid_decrypt.h" #include "tink/key_manager.h"
diff --git a/cc/hybrid/hybrid_decrypt_factory_test.cc b/cc/hybrid/hybrid_decrypt_factory_test.cc index 2dce36a..c2b405a 100644 --- a/cc/hybrid/hybrid_decrypt_factory_test.cc +++ b/cc/hybrid/hybrid_decrypt_factory_test.cc
@@ -16,12 +16,12 @@ #include "tink/hybrid/hybrid_decrypt_factory.h" +#include <memory> #include <string> #include <utility> #include "gtest/gtest.h" #include "absl/memory/memory.h" -#include "tink/config.h" #include "tink/crypto_format.h" #include "tink/hybrid/ecies_aead_hkdf_public_key_manager.h" #include "tink/hybrid/hybrid_config.h" @@ -34,7 +34,6 @@ #include "proto/ecies_aead_hkdf.pb.h" #include "proto/tink.pb.h" -using crypto::tink::TestKeysetHandle; using crypto::tink::test::AddRawKey; using crypto::tink::test::AddTinkKey; using google::crypto::tink::EciesAeadHkdfPrivateKey;
diff --git a/cc/hybrid/hybrid_decrypt_wrapper.cc b/cc/hybrid/hybrid_decrypt_wrapper.cc index 295f9e6..f07440b 100644 --- a/cc/hybrid/hybrid_decrypt_wrapper.cc +++ b/cc/hybrid/hybrid_decrypt_wrapper.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/hybrid_decrypt_wrapper.h" +#include <memory> #include <string> #include <utility> @@ -51,7 +52,7 @@ absl::string_view ciphertext, absl::string_view context_info) const override; - ~HybridDecryptSetWrapper() override {} + ~HybridDecryptSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<HybridDecrypt>> hybrid_decrypt_set_;
diff --git a/cc/hybrid/hybrid_decrypt_wrapper.h b/cc/hybrid/hybrid_decrypt_wrapper.h index 0d2ea55..14ea1d7 100644 --- a/cc/hybrid/hybrid_decrypt_wrapper.h +++ b/cc/hybrid/hybrid_decrypt_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_HYBRID_HYBRID_DECRYPT_WRAPPER_H_ #define TINK_HYBRID_HYBRID_DECRYPT_WRAPPER_H_ +#include <memory> + #include "absl/strings/string_view.h" #include "tink/hybrid_decrypt.h" #include "tink/primitive_set.h"
diff --git a/cc/hybrid/hybrid_encrypt_factory.cc b/cc/hybrid/hybrid_encrypt_factory.cc index 9e2d92f..57e8c06 100644 --- a/cc/hybrid/hybrid_encrypt_factory.cc +++ b/cc/hybrid/hybrid_encrypt_factory.cc
@@ -16,15 +16,16 @@ #include "tink/hybrid/hybrid_encrypt_factory.h" +#include <memory> + +#include "tink/hybrid/hybrid_encrypt_wrapper.h" #include "tink/hybrid_encrypt.h" #include "tink/key_manager.h" #include "tink/keyset_handle.h" #include "tink/registry.h" -#include "tink/hybrid/hybrid_encrypt_wrapper.h" #include "tink/util/status.h" #include "tink/util/statusor.h" - namespace crypto { namespace tink {
diff --git a/cc/hybrid/hybrid_encrypt_factory.h b/cc/hybrid/hybrid_encrypt_factory.h index 05f776c..23b6f9d 100644 --- a/cc/hybrid/hybrid_encrypt_factory.h +++ b/cc/hybrid/hybrid_encrypt_factory.h
@@ -17,6 +17,8 @@ #ifndef TINK_HYBRID_HYBRID_ENCRYPT_FACTORY_H_ #define TINK_HYBRID_HYBRID_ENCRYPT_FACTORY_H_ +#include <memory> + #include "absl/base/macros.h" #include "tink/hybrid_encrypt.h" #include "tink/key_manager.h"
diff --git a/cc/hybrid/hybrid_encrypt_factory_test.cc b/cc/hybrid/hybrid_encrypt_factory_test.cc index 415c786..14bd2d4 100644 --- a/cc/hybrid/hybrid_encrypt_factory_test.cc +++ b/cc/hybrid/hybrid_encrypt_factory_test.cc
@@ -20,7 +20,6 @@ #include <utility> #include "gtest/gtest.h" -#include "tink/config.h" #include "tink/crypto_format.h" #include "tink/hybrid/hybrid_config.h" #include "tink/hybrid_encrypt.h" @@ -31,7 +30,6 @@ #include "proto/ecies_aead_hkdf.pb.h" #include "proto/tink.pb.h" -using crypto::tink::TestKeysetHandle; using crypto::tink::test::AddRawKey; using crypto::tink::test::AddTinkKey; using google::crypto::tink::EciesAeadHkdfPublicKey;
diff --git a/cc/hybrid/hybrid_encrypt_wrapper.cc b/cc/hybrid/hybrid_encrypt_wrapper.cc index d941084..72f3b78 100644 --- a/cc/hybrid/hybrid_encrypt_wrapper.cc +++ b/cc/hybrid/hybrid_encrypt_wrapper.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/hybrid_encrypt_wrapper.h" +#include <memory> #include <string> #include <utility> @@ -25,8 +26,8 @@ #include "tink/internal/monitoring_util.h" #include "tink/internal/registry_impl.h" #include "tink/internal/util.h" -#include "tink/primitive_set.h" #include "tink/monitoring/monitoring.h" +#include "tink/primitive_set.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -66,7 +67,7 @@ absl::string_view plaintext, absl::string_view context_info) const override; - ~HybridEncryptSetWrapper() override {} + ~HybridEncryptSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<HybridEncrypt>> hybrid_encrypt_set_;
diff --git a/cc/hybrid/hybrid_encrypt_wrapper.h b/cc/hybrid/hybrid_encrypt_wrapper.h index ad78604..4d100da 100644 --- a/cc/hybrid/hybrid_encrypt_wrapper.h +++ b/cc/hybrid/hybrid_encrypt_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_HYBRID_HYBRID_ENCRYPT_WRAPPER_H_ #define TINK_HYBRID_HYBRID_ENCRYPT_WRAPPER_H_ +#include <memory> + #include "absl/strings/string_view.h" #include "tink/hybrid_encrypt.h" #include "tink/primitive_set.h"
diff --git a/cc/hybrid/internal/CMakeLists.txt b/cc/hybrid/internal/CMakeLists.txt index 6dc37fa..182b12c 100644 --- a/cc/hybrid/internal/CMakeLists.txt +++ b/cc/hybrid/internal/CMakeLists.txt
@@ -201,6 +201,7 @@ tink::util::status tink::util::statusor tink::proto::hpke_cc_proto + TESTONLY TAGS exclude_if_openssl ) @@ -253,6 +254,7 @@ tink::util::secret_data tink::util::status tink::util::statusor + TESTONLY TAGS exclude_if_openssl )
diff --git a/cc/hybrid/internal/hpke_context_boringssl.cc b/cc/hybrid/internal/hpke_context_boringssl.cc index 47a51ed..42374a1 100644 --- a/cc/hybrid/internal/hpke_context_boringssl.cc +++ b/cc/hybrid/internal/hpke_context_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/internal/hpke_context_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/hybrid/internal/hpke_context_boringssl_test.cc b/cc/hybrid/internal/hpke_context_boringssl_test.cc index 54f5f20..cded6bc 100644 --- a/cc/hybrid/internal/hpke_context_boringssl_test.cc +++ b/cc/hybrid/internal/hpke_context_boringssl_test.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/internal/hpke_context_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/hybrid/internal/hpke_context_test.cc b/cc/hybrid/internal/hpke_context_test.cc index 1338712..8592558 100644 --- a/cc/hybrid/internal/hpke_context_test.cc +++ b/cc/hybrid/internal/hpke_context_test.cc
@@ -16,7 +16,9 @@ #include "tink/hybrid/internal/hpke_context.h" +#include <memory> #include <string> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/hybrid/internal/hpke_decrypt_boringssl.cc b/cc/hybrid/internal/hpke_decrypt_boringssl.cc index ec529d9..46f0796 100644 --- a/cc/hybrid/internal/hpke_decrypt_boringssl.cc +++ b/cc/hybrid/internal/hpke_decrypt_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/internal/hpke_decrypt_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/hybrid/internal/hpke_decrypt_boringssl_test.cc b/cc/hybrid/internal/hpke_decrypt_boringssl_test.cc index afdf7e3..05106fa 100644 --- a/cc/hybrid/internal/hpke_decrypt_boringssl_test.cc +++ b/cc/hybrid/internal/hpke_decrypt_boringssl_test.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/internal/hpke_decrypt_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/hybrid/internal/hpke_encrypt_boringssl.cc b/cc/hybrid/internal/hpke_encrypt_boringssl.cc index ec7a010..198ce16 100644 --- a/cc/hybrid/internal/hpke_encrypt_boringssl.cc +++ b/cc/hybrid/internal/hpke_encrypt_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/internal/hpke_encrypt_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/hybrid/internal/hpke_encrypt_boringssl_test.cc b/cc/hybrid/internal/hpke_encrypt_boringssl_test.cc index 9f6ad14..7f668a3 100644 --- a/cc/hybrid/internal/hpke_encrypt_boringssl_test.cc +++ b/cc/hybrid/internal/hpke_encrypt_boringssl_test.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/internal/hpke_encrypt_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/hybrid/internal/hpke_key_boringssl.cc b/cc/hybrid/internal/hpke_key_boringssl.cc index 0448bee..5c8b198 100644 --- a/cc/hybrid/internal/hpke_key_boringssl.cc +++ b/cc/hybrid/internal/hpke_key_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/internal/hpke_key_boringssl.h" +#include <memory> #include <utility> #include "absl/status/status.h"
diff --git a/cc/hybrid/internal/hpke_key_boringssl_test.cc b/cc/hybrid/internal/hpke_key_boringssl_test.cc index 802058d..83840d6 100644 --- a/cc/hybrid/internal/hpke_key_boringssl_test.cc +++ b/cc/hybrid/internal/hpke_key_boringssl_test.cc
@@ -16,6 +16,7 @@ #include "tink/hybrid/internal/hpke_key_boringssl.h" +#include <memory> #include <string> #include "gtest/gtest.h"
diff --git a/cc/hybrid/internal/hpke_private_key_manager.cc b/cc/hybrid/internal/hpke_private_key_manager.cc index da0d663..5fc4047 100644 --- a/cc/hybrid/internal/hpke_private_key_manager.cc +++ b/cc/hybrid/internal/hpke_private_key_manager.cc
@@ -16,6 +16,8 @@ #include "tink/hybrid/internal/hpke_private_key_manager.h" +#include <memory> + #include "absl/status/status.h" #include "tink/hybrid/internal/hpke_key_manager_util.h" #include "tink/internal/ec_util.h"
diff --git a/cc/hybrid/internal/hpke_private_key_manager.h b/cc/hybrid/internal/hpke_private_key_manager.h index 745f6f3..ef2e6b9 100644 --- a/cc/hybrid/internal/hpke_private_key_manager.h +++ b/cc/hybrid/internal/hpke_private_key_manager.h
@@ -17,6 +17,7 @@ #ifndef TINK_HYBRID_INTERNAL_HPKE_PRIVATE_KEY_MANAGER_H_ #define TINK_HYBRID_INTERNAL_HPKE_PRIVATE_KEY_MANAGER_H_ +#include <memory> #include <string> #include "tink/core/key_type_manager.h"
diff --git a/cc/hybrid/internal/hpke_private_key_manager_test.cc b/cc/hybrid/internal/hpke_private_key_manager_test.cc index 9076409..116cf04 100644 --- a/cc/hybrid/internal/hpke_private_key_manager_test.cc +++ b/cc/hybrid/internal/hpke_private_key_manager_test.cc
@@ -16,6 +16,8 @@ #include "tink/hybrid/internal/hpke_private_key_manager.h" +#include <memory> + #include "gtest/gtest.h" #include "absl/status/status.h" #include "tink/hybrid/internal/hpke_encrypt.h"
diff --git a/cc/hybrid/internal/hpke_public_key_manager.h b/cc/hybrid/internal/hpke_public_key_manager.h index 1d43df8..ebb7cd5 100644 --- a/cc/hybrid/internal/hpke_public_key_manager.h +++ b/cc/hybrid/internal/hpke_public_key_manager.h
@@ -17,6 +17,7 @@ #ifndef TINK_HYBRID_INTERNAL_HPKE_PUBLIC_KEY_MANAGER_H_ #define TINK_HYBRID_INTERNAL_HPKE_PUBLIC_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/hybrid/internal/hpke_test_util.h b/cc/hybrid/internal/hpke_test_util.h index 5a7ea1a..10532c2 100644 --- a/cc/hybrid/internal/hpke_test_util.h +++ b/cc/hybrid/internal/hpke_test_util.h
@@ -18,6 +18,7 @@ #define TINK_HYBRID_INTERNAL_HPKE_TEST_UTIL_H_ #include <string> +#include <vector> #include "absl/strings/escaping.h" #include "tink/hybrid/internal/hpke_util.h"
diff --git a/cc/hybrid_decrypt.h b/cc/hybrid_decrypt.h index f685d64..6d553c3 100644 --- a/cc/hybrid_decrypt.h +++ b/cc/hybrid_decrypt.h
@@ -61,7 +61,7 @@ virtual crypto::tink::util::StatusOr<std::string> Decrypt( absl::string_view ciphertext, absl::string_view context_info) const = 0; - virtual ~HybridDecrypt() {} + virtual ~HybridDecrypt() = default; }; } // namespace tink
diff --git a/cc/hybrid_encrypt.h b/cc/hybrid_encrypt.h index 67b18dd..20f54dd 100644 --- a/cc/hybrid_encrypt.h +++ b/cc/hybrid_encrypt.h
@@ -61,7 +61,7 @@ virtual crypto::tink::util::StatusOr<std::string> Encrypt( absl::string_view plaintext, absl::string_view context_info) const = 0; - virtual ~HybridEncrypt() {} + virtual ~HybridEncrypt() = default; }; } // namespace tink
diff --git a/cc/input_stream.h b/cc/input_stream.h index f1ac7f1..cd832b0 100644 --- a/cc/input_stream.h +++ b/cc/input_stream.h
@@ -27,8 +27,8 @@ // Protocol Buffers' google::protobuf::io::ZeroCopyInputStream. class InputStream { public: - InputStream() {} - virtual ~InputStream() {} + InputStream() = default; + virtual ~InputStream() = default; // Obtains a chunk of data from the stream. //
diff --git a/cc/insecure_secret_key_access.h b/cc/insecure_secret_key_access.h index 90067f2..915b4f3 100644 --- a/cc/insecure_secret_key_access.h +++ b/cc/insecure_secret_key_access.h
@@ -14,8 +14,8 @@ // //////////////////////////////////////////////////////////////////////////////// -#ifndef TINK_INSECURE_KEY_ACCESS_H_ -#define TINK_INSECURE_KEY_ACCESS_H_ +#ifndef TINK_INSECURE_SECRET_KEY_ACCESS_H_ +#define TINK_INSECURE_SECRET_KEY_ACCESS_H_ #include "tink/secret_key_access_token.h" @@ -39,4 +39,4 @@ } // namespace tink } // namespace crypto -#endif // TINK_INSECURE_KEY_ACCESS_H_ +#endif // TINK_INSECURE_SECRET_KEY_ACCESS_H_
diff --git a/cc/integration/awskms/.bazelrc b/cc/integration/awskms/.bazelrc new file mode 100644 index 0000000..0fe2c3f --- /dev/null +++ b/cc/integration/awskms/.bazelrc
@@ -0,0 +1,4 @@ +# Minumum C++ version. Override it building this project with +# `bazel build --cxxopt='-std=c++<XY>' --host_cxxopt='c++<XY>' ...` +# (Both -std and --host_cxxopt must be set to force the desired version.) +build --cxxopt='-std=c++14' --host_cxxopt='-std=c++14'
diff --git a/cc/integration/awskms/.bazelversion b/cc/integration/awskms/.bazelversion new file mode 100644 index 0000000..09b254e --- /dev/null +++ b/cc/integration/awskms/.bazelversion
@@ -0,0 +1 @@ +6.0.0
diff --git a/cc/integration/awskms/BUILD.bazel b/cc/integration/awskms/BUILD.bazel index 0367300..1c81f7e 100644 --- a/cc/integration/awskms/BUILD.bazel +++ b/cc/integration/awskms/BUILD.bazel
@@ -3,24 +3,6 @@ licenses(["notice"]) cc_library( - name = "aws_crypto", - srcs = [ - "aws_crypto.cc", - ], - hdrs = [ - "aws_crypto.h", - ], - include_prefix = "tink/integration/awskms", - visibility = ["//visibility:public"], - deps = [ - "@aws_cpp_sdk//:aws_sdk_core", - "@boringssl//:crypto", - "@com_google_absl//absl/base", - ], - alwayslink = 1, -) - -cc_library( name = "aws_kms_aead", srcs = ["aws_kms_aead.cc"], hdrs = ["aws_kms_aead.h"], @@ -44,9 +26,9 @@ include_prefix = "tink/integration/awskms", visibility = ["//visibility:public"], deps = [ - ":aws_crypto", ":aws_kms_aead", "@aws_cpp_sdk//:aws_sdk_core", + "@com_google_absl//absl/base", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", @@ -58,34 +40,34 @@ alwayslink = 1, ) -# tests - cc_test( - name = "aws_kms_aead_test", - size = "medium", - srcs = ["aws_kms_aead_test.cc"], - copts = ["-Iexternal/gtest/include"], + name = "aws_kms_aead_integration_test", + size = "small", + srcs = ["aws_kms_aead_integration_test.cc"], + data = ["//testdata/aws:credentials"], + # This target requires valid credentials to interact with the AWS KMS. + tags = ["manual"], deps = [ ":aws_kms_aead", - "@aws_cpp_sdk//:aws_sdk_core", + ":aws_kms_client", + "//tink/integration/awskms/internal:test_file_util", + "@bazel_tools//tools/cpp/runfiles", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", - "@tink_cc//util:status", "@tink_cc//util:statusor", + "@tink_cc//util:test_matchers", ], ) cc_test( name = "aws_kms_client_test", - size = "medium", + size = "small", srcs = ["aws_kms_client_test.cc"], - copts = ["-Iexternal/gtest/include"], - data = [ - "//testdata/aws:credentials", - "//testdata/gcp:credentials", - ], + data = ["//testdata/aws:credentials"], deps = [ ":aws_kms_client", + "//tink/integration/awskms/internal:test_file_util", "@aws_cpp_sdk//:aws_sdk_core", "@com_google_absl//absl/status", "@com_google_googletest//:gtest_main",
diff --git a/cc/integration/awskms/aws_crypto.cc b/cc/integration/awskms/aws_crypto.cc deleted file mode 100644 index c73eac4..0000000 --- a/cc/integration/awskms/aws_crypto.cc +++ /dev/null
@@ -1,128 +0,0 @@ -// Copyright 2018 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "tink/integration/awskms/aws_crypto.h" - -#include "aws/core/Aws.h" -#include "aws/core/utils/Outcome.h" -#include "aws/core/utils/crypto/Factories.h" -#include "aws/core/utils/crypto/HashResult.h" -#include "aws/core/utils/crypto/HMAC.h" -#include "aws/core/utils/crypto/Hash.h" - -#include "absl/base/attributes.h" -#include "openssl/hmac.h" -#include "openssl/sha.h" - -namespace crypto { -namespace tink { -namespace integration { -namespace awskms { - -ABSL_CONST_INIT const char* kAwsCryptoAllocationTag = "AwsCryptoAllocation"; - -class AwsSha256HmacOpenSslImpl : public Aws::Utils::Crypto::HMAC { - public: - AwsSha256HmacOpenSslImpl() {} - - virtual ~AwsSha256HmacOpenSslImpl() = default; - - Aws::Utils::Crypto::HashResult Calculate( - const Aws::Utils::ByteBuffer& toSign, - const Aws::Utils::ByteBuffer& secret) override { - unsigned int length = SHA256_DIGEST_LENGTH; - Aws::Utils::ByteBuffer digest(length); - memset(digest.GetUnderlyingData(), 0, length); - - HMAC_CTX ctx; - HMAC_CTX_init(&ctx); - - HMAC_Init_ex(&ctx, secret.GetUnderlyingData(), - static_cast<int>(secret.GetLength()), EVP_sha256(), NULL); - HMAC_Update(&ctx, toSign.GetUnderlyingData(), toSign.GetLength()); - HMAC_Final(&ctx, digest.GetUnderlyingData(), &length); - HMAC_CTX_cleanup(&ctx); - - return Aws::Utils::Crypto::HashResult(std::move(digest)); - } -}; - -class AwsSha256OpenSslImpl : public Aws::Utils::Crypto::Hash { - public: - AwsSha256OpenSslImpl() {} - - virtual ~AwsSha256OpenSslImpl() = default; - - Aws::Utils::Crypto::HashResult Calculate(const Aws::String& str) override { - SHA256_CTX sha256; - SHA256_Init(&sha256); - SHA256_Update(&sha256, str.data(), str.size()); - - Aws::Utils::ByteBuffer hash(SHA256_DIGEST_LENGTH); - SHA256_Final(hash.GetUnderlyingData(), &sha256); - - return Aws::Utils::Crypto::HashResult(std::move(hash)); - } - - Aws::Utils::Crypto::HashResult Calculate(Aws::IStream& stream) override { - SHA256_CTX sha256; - SHA256_Init(&sha256); - - auto currentPos = stream.tellg(); - if (currentPos == std::streampos(std::streamoff(-1))) { - currentPos = 0; - stream.clear(); - } - - stream.seekg(0, stream.beg); - - char streamBuffer - [Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE]; - while (stream.good()) { - stream.read(streamBuffer, - Aws::Utils::Crypto::Hash::INTERNAL_HASH_STREAM_BUFFER_SIZE); - auto bytesRead = stream.gcount(); - - if (bytesRead > 0) { - SHA256_Update(&sha256, streamBuffer, static_cast<size_t>(bytesRead)); - } - } - - stream.clear(); - stream.seekg(currentPos, stream.beg); - - Aws::Utils::ByteBuffer hash(SHA256_DIGEST_LENGTH); - SHA256_Final(hash.GetUnderlyingData(), &sha256); - - return Aws::Utils::Crypto::HashResult(std::move(hash)); - } -}; - -std::shared_ptr<Aws::Utils::Crypto::Hash> -AwsSha256Factory::CreateImplementation() const { - return Aws::MakeShared<AwsSha256OpenSslImpl>(kAwsCryptoAllocationTag); -} - -std::shared_ptr<Aws::Utils::Crypto::HMAC> -AwsSha256HmacFactory::CreateImplementation() const { - return Aws::MakeShared<AwsSha256HmacOpenSslImpl>(kAwsCryptoAllocationTag); -} - - -} // namespace awskms -} // namespace integration -} // namespace tink -} // namespace crypto
diff --git a/cc/integration/awskms/aws_crypto.h b/cc/integration/awskms/aws_crypto.h deleted file mode 100644 index 01f8c92..0000000 --- a/cc/integration/awskms/aws_crypto.h +++ /dev/null
@@ -1,66 +0,0 @@ -// Copyright 2018 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef TINK_INTEGRATION_AWSKMS_AWS_CRYPTO_H_ -#define TINK_INTEGRATION_AWSKMS_AWS_CRYPTO_H_ - -#include "aws/core/Aws.h" -#include "aws/core/utils/crypto/Factories.h" -#include "aws/core/utils/crypto/HMAC.h" -#include "aws/core/utils/crypto/Hash.h" - -namespace crypto { -namespace tink { -namespace integration { -namespace awskms { - -// Helpers for building AWS C++ Client without OpenSSL, to avoid -// collisions with BoringSSL used by Tink. -// These were "borrowed" from TensorFlow (https://github.com/tensorflow/). -// -// With these helpers the initialization of AWS API looks as follows: -// -// Aws::SDKOptions options; -// options.cryptoOptions.sha256Factory_create_fn = []() { -// return Aws::MakeShared<AwsSha256Factory>(kAwsCryptoAllocationTag); -// }; -// options.cryptoOptions.sha256HMACFactory_create_fn = []() { -// return Aws::MakeShared<AwsSha256HmacFactory>(kAwsCryptoAllocationTag); -// }; -// Aws::InitAPI(options); -// -/////////////////////////////////////////////////////////////////////////////// - -extern const char* kAwsCryptoAllocationTag; - -class AwsSha256Factory : public Aws::Utils::Crypto::HashFactory { - public: - std::shared_ptr<Aws::Utils::Crypto::Hash> CreateImplementation() - const override; -}; - -class AwsSha256HmacFactory : public Aws::Utils::Crypto::HMACFactory { - public: - std::shared_ptr<Aws::Utils::Crypto::HMAC> CreateImplementation() - const override; -}; - -} // namespace awskms -} // namespace integration -} // namespace tink -} // namespace crypto - -#endif // TINK_INTEGRATION_AWSKMS_AWS_CRYPTO_H_
diff --git a/cc/integration/awskms/aws_kms_aead.cc b/cc/integration/awskms/aws_kms_aead.cc index e231b9a..a34b2e2 100644 --- a/cc/integration/awskms/aws_kms_aead.cc +++ b/cc/integration/awskms/aws_kms_aead.cc
@@ -16,11 +16,6 @@ #include "tink/integration/awskms/aws_kms_aead.h" -#include "absl/status/status.h" -#include "absl/strings/escaping.h" -#include "absl/strings/match.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" #include "aws/core/auth/AWSCredentialsProvider.h" #include "aws/core/client/AWSClient.h" #include "aws/core/utils/Outcome.h" @@ -31,6 +26,11 @@ #include "aws/kms/model/DecryptResult.h" #include "aws/kms/model/EncryptRequest.h" #include "aws/kms/model/EncryptResult.h" +#include "absl/status/status.h" +#include "absl/strings/escaping.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" #include "tink/aead.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -39,24 +39,8 @@ namespace tink { namespace integration { namespace awskms { - -using crypto::tink::util::Status; -using crypto::tink::util::StatusOr; - namespace { -// TODO: pull out hex-helpers from test_util and remove the copy below. -std::string HexEncode(absl::string_view bytes) { - std::string hexchars = "0123456789abcdef"; - std::string res(bytes.size() * 2, static_cast<char>(255)); - for (size_t i = 0; i < bytes.size(); ++i) { - uint8_t c = static_cast<uint8_t>(bytes[i]); - res[2 * i] = hexchars[c / 16]; - res[2 * i + 1] = hexchars[c % 16]; - } - return res; -} - std::string AwsErrorToString(Aws::Client::AWSError<Aws::KMS::KMSErrors> err) { return absl::StrCat("AWS error code: ", err.GetErrorType(), ", ", err.GetExceptionName(), ": ", err.GetMessage()); @@ -64,28 +48,21 @@ } // namespace -AwsKmsAead::AwsKmsAead(absl::string_view key_arn, - std::shared_ptr<Aws::KMS::KMSClient> aws_client) : - key_arn_(key_arn), aws_client_(aws_client) { -} - -// static -StatusOr<std::unique_ptr<Aead>> -AwsKmsAead::New(absl::string_view key_arn, - std::shared_ptr<Aws::KMS::KMSClient> aws_client) { +util::StatusOr<std::unique_ptr<Aead>> AwsKmsAead::New( + absl::string_view key_arn, + std::shared_ptr<Aws::KMS::KMSClient> aws_client) { if (key_arn.empty()) { - return Status(absl::StatusCode::kInvalidArgument, - "Key ARN cannot be empty."); + return util::Status(absl::StatusCode::kInvalidArgument, + "Key ARN cannot be empty."); } if (aws_client == nullptr) { - return Status(absl::StatusCode::kInvalidArgument, - "AWS KMS client cannot be null."); + return util::Status(absl::StatusCode::kInvalidArgument, + "AWS KMS client cannot be null."); } - std::unique_ptr<Aead> aead(new AwsKmsAead(key_arn, aws_client)); - return std::move(aead); + return {absl::WrapUnique(new AwsKmsAead(key_arn, aws_client))}; } -StatusOr<std::string> AwsKmsAead::Encrypt( +util::StatusOr<std::string> AwsKmsAead::Encrypt( absl::string_view plaintext, absl::string_view associated_data) const { Aws::KMS::Model::EncryptRequest req; req.SetKeyId(key_arn_.c_str()); @@ -95,23 +72,23 @@ req.SetPlaintext(plaintext_buffer); if (!associated_data.empty()) { req.AddEncryptionContext("associatedData", - HexEncode(associated_data).c_str()); + absl::BytesToHexString(associated_data).c_str()); } auto outcome = aws_client_->Encrypt(req); - if (outcome.IsSuccess()) { - auto& blob = outcome.GetResult().GetCiphertextBlob(); - std::string ciphertext( - reinterpret_cast<const char*>(blob.GetUnderlyingData()), - blob.GetLength()); - return ciphertext; + if (!outcome.IsSuccess()) { + auto& err = outcome.GetError(); + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("AWS KMS encryption failed with error: ", + AwsErrorToString(err))); } - auto& err = outcome.GetError(); - return util::Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("AWS KMS encryption failed with error: ", - AwsErrorToString(err))); + auto& blob = outcome.GetResult().GetCiphertextBlob(); + std::string ciphertext( + reinterpret_cast<const char*>(blob.GetUnderlyingData()), + blob.GetLength()); + return ciphertext; } -StatusOr<std::string> AwsKmsAead::Decrypt( +util::StatusOr<std::string> AwsKmsAead::Decrypt( absl::string_view ciphertext, absl::string_view associated_data) const { Aws::KMS::Model::DecryptRequest req; req.SetKeyId(key_arn_.c_str()); @@ -121,24 +98,20 @@ req.SetCiphertextBlob(ciphertext_buffer); if (!associated_data.empty()) { req.AddEncryptionContext("associatedData", - HexEncode(associated_data).c_str()); + absl::BytesToHexString(associated_data).c_str()); } auto outcome = aws_client_->Decrypt(req); - if (outcome.IsSuccess()) { - if (outcome.GetResult().GetKeyId() != Aws::String(key_arn_.c_str())) { - return util::Status(absl::StatusCode::kInvalidArgument, - "AWS KMS decryption failed: wrong key ARN."); - } - auto& buffer = outcome.GetResult().GetPlaintext(); - std::string plaintext( - reinterpret_cast<const char*>(buffer.GetUnderlyingData()), - buffer.GetLength()); - return plaintext; + if (!outcome.IsSuccess()) { + auto& err = outcome.GetError(); + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("AWS KMS decryption failed with error: ", + AwsErrorToString(err))); } - auto& err = outcome.GetError(); - return util::Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("AWS KMS decryption failed with error: ", - AwsErrorToString(err))); + auto& buffer = outcome.GetResult().GetPlaintext(); + std::string plaintext( + reinterpret_cast<const char*>(buffer.GetUnderlyingData()), + buffer.GetLength()); + return plaintext; } } // namespace awskms
diff --git a/cc/integration/awskms/aws_kms_aead.h b/cc/integration/awskms/aws_kms_aead.h index be400ab..31c9157 100644 --- a/cc/integration/awskms/aws_kms_aead.h +++ b/cc/integration/awskms/aws_kms_aead.h
@@ -17,8 +17,8 @@ #ifndef TINK_INTEGRATION_AWSKMS_AWS_KMS_AEAD_H_ #define TINK_INTEGRATION_AWSKMS_AWS_KMS_AEAD_H_ -#include "absl/strings/string_view.h" #include "aws/kms/KMSClient.h" +#include "absl/strings/string_view.h" #include "tink/aead.h" #include "tink/util/statusor.h" @@ -27,15 +27,20 @@ namespace integration { namespace awskms { -// AwsKmsAead is an implementation of AEAD that forwards -// encryption/decryption requests to a key managed by -// <a href="https://aws.amazon.com/kms/">AWS KMS</a>. +// AwsKmsAead is an implementation of AEAD that forwards encryption/decryption +// requests to a key managed by the AWS KMS (https://aws.amazon.com/kms). class AwsKmsAead : public Aead { public: - // Creates a new AwsKmsAead that is bound to the key specified in 'key_arn', + // Move only. + AwsKmsAead(AwsKmsAead&& other) = default; + AwsKmsAead& operator=(AwsKmsAead&& other) = default; + AwsKmsAead(const AwsKmsAead&) = delete; + AwsKmsAead& operator=(const AwsKmsAead&) = delete; + + // Creates a new AwsKmsAead that is bound to the key specified in `key_arn`, // and that uses the given client when communicating with the KMS. - static crypto::tink::util::StatusOr<std::unique_ptr<Aead>> - New(absl::string_view key_arn, + static crypto::tink::util::StatusOr<std::unique_ptr<Aead>> New( + absl::string_view key_arn, std::shared_ptr<Aws::KMS::KMSClient> aws_client); crypto::tink::util::StatusOr<std::string> Encrypt( @@ -46,16 +51,15 @@ absl::string_view ciphertext, absl::string_view associated_data) const override; - virtual ~AwsKmsAead() {} - private: AwsKmsAead(absl::string_view key_arn, - std::shared_ptr<Aws::KMS::KMSClient> aws_client); + std::shared_ptr<Aws::KMS::KMSClient> aws_client) + : key_arn_(key_arn), aws_client_(aws_client) {} + std::string key_arn_; // The location of a crypto key in AWS KMS. std::shared_ptr<Aws::KMS::KMSClient> aws_client_; }; - } // namespace awskms } // namespace integration } // namespace tink
diff --git a/cc/integration/awskms/aws_kms_aead_integration_test.cc b/cc/integration/awskms/aws_kms_aead_integration_test.cc new file mode 100644 index 0000000..a549cff --- /dev/null +++ b/cc/integration/awskms/aws_kms_aead_integration_test.cc
@@ -0,0 +1,91 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include <string> +#include <vector> + +#include "gtest/gtest.h" +#include "tink/integration/awskms/aws_kms_aead.h" +#include "tink/integration/awskms/aws_kms_client.h" +#include "tink/integration/awskms/internal/test_file_util.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace integration { +namespace awskms { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; + +constexpr absl::string_view kAwsKmsKeyUri = + "aws-kms://arn:aws:kms:us-east-2:235739564943:key/" + "3ee50705-5a82-4f5b-9753-05c4f473922f"; + +constexpr absl::string_view kAwsKmsKeyAliasUri = + "aws-kms://arn:aws:kms:us-east-2:235739564943:alias/" + "unit-and-integration-testing"; + + +TEST(AwsKmsAeadTest, EncryptDecrypt) { + std::string credentials = + internal::RunfilesPath("testdata/aws/credentials.ini"); + util::StatusOr<std::unique_ptr<AwsKmsClient>> client = + AwsKmsClient::New(/*key_uri=*/"", credentials); + ASSERT_THAT(client, IsOk()); + + util::StatusOr<std::unique_ptr<Aead>> aead = + (*client)->GetAead(kAwsKmsKeyUri); + ASSERT_THAT(aead, IsOk()); + + constexpr absl::string_view kPlaintext = "plaintext"; + constexpr absl::string_view kAssociatedData = "aad"; + + util::StatusOr<std::string> ciphertext = + (*aead)->Encrypt(kPlaintext, kAssociatedData); + ASSERT_THAT(ciphertext, IsOk()); + EXPECT_THAT((*aead)->Decrypt(*ciphertext, kAssociatedData), + IsOkAndHolds(kPlaintext)); +} + +TEST(AwsKmsAeadTest, EncryptDecryptWithKeyAlias) { + std::string credentials = + internal::RunfilesPath("testdata/aws/credentials.ini"); + util::StatusOr<std::unique_ptr<AwsKmsClient>> client = + AwsKmsClient::New(/*key_uri=*/"", credentials); + ASSERT_THAT(client, IsOk()); + + util::StatusOr<std::unique_ptr<Aead>> aead = + (*client)->GetAead(kAwsKmsKeyAliasUri); + ASSERT_THAT(aead, IsOk()); + + constexpr absl::string_view kPlaintext = "plaintext"; + constexpr absl::string_view kAssociatedData = "aad"; + + util::StatusOr<std::string> ciphertext = + (*aead)->Encrypt(kPlaintext, kAssociatedData); + ASSERT_THAT(ciphertext, IsOk()); + EXPECT_THAT((*aead)->Decrypt(*ciphertext, kAssociatedData), + IsOkAndHolds(kPlaintext)); +} + +} // namespace +} // namespace awskms +} // namespace integration +} // namespace tink +} // namespace crypto
diff --git a/cc/integration/awskms/aws_kms_aead_test.cc b/cc/integration/awskms/aws_kms_aead_test.cc deleted file mode 100644 index 99d42e3..0000000 --- a/cc/integration/awskms/aws_kms_aead_test.cc +++ /dev/null
@@ -1,44 +0,0 @@ -// Copyright 2018 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#include "tink/integration/awskms/aws_kms_aead.h" - -#include <string> -#include <vector> - -#include "absl/strings/str_cat.h" -#include "aws/core/Aws.h" -#include "aws/kms/KMSClient.h" -#include "tink/util/status.h" -#include "tink/util/statusor.h" -#include "gtest/gtest.h" - -namespace crypto { -namespace tink { -namespace subtle { -namespace { - -using crypto::tink::integration::awskms::AwsKmsAead; - -class AwsKmsAeadTest : public ::testing::Test { - // TODO(przydatek): add a test with a mock KMSClient. -}; - - -} // namespace -} // namespace subtle -} // namespace tink -} // namespace crypto
diff --git a/cc/integration/awskms/aws_kms_client.cc b/cc/integration/awskms/aws_kms_client.cc index 9b57dc5..158bced 100644 --- a/cc/integration/awskms/aws_kms_client.cc +++ b/cc/integration/awskms/aws_kms_client.cc
@@ -19,13 +19,6 @@ #include <iostream> #include <sstream> -#include "absl/status/status.h" -#include "absl/strings/match.h" -#include "absl/strings/ascii.h" -#include "absl/strings/escaping.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" #include "aws/core/Aws.h" #include "aws/core/auth/AWSCredentialsProvider.h" #include "aws/core/auth/AWSCredentialsProviderChain.h" @@ -33,7 +26,14 @@ #include "aws/core/utils/crypto/Factories.h" #include "aws/core/utils/memory/AWSMemory.h" #include "aws/kms/KMSClient.h" -#include "tink/integration/awskms/aws_crypto.h" +#include "absl/base/call_once.h" +#include "absl/status/status.h" +#include "absl/strings/ascii.h" +#include "absl/strings/escaping.h" +#include "absl/strings/match.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" #include "tink/integration/awskms/aws_kms_aead.h" #include "tink/kms_client.h" #include "tink/util/status.h" @@ -46,9 +46,10 @@ namespace { constexpr absl::string_view kKeyUriPrefix = "aws-kms://"; +constexpr char kTinkAwsKmsAllocationTag[] = "tink::integration::awskms"; // Returns AWS key ARN contained in `key_uri`. If `key_uri` does not refer to an -// AWS key, returns an empty string. +// AWS key, returns an error. util::StatusOr<std::string> GetKeyArn(absl::string_view key_uri) { if (!absl::StartsWithIgnoreCase(key_uri, kKeyUriPrefix)) { return util::Status(absl::StatusCode::kInvalidArgument, @@ -61,8 +62,8 @@ // `key_arn`. // An AWS key ARN is of the form // arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab. -util::StatusOr<Aws::Client::ClientConfiguration> - GetAwsClientConfig(absl::string_view key_arn) { +util::StatusOr<Aws::Client::ClientConfiguration> GetAwsClientConfig( + absl::string_view key_arn) { std::vector<std::string> key_arn_parts = absl::StrSplit(key_arn, ':'); if (key_arn_parts.size() < 6) { return util::Status(absl::StatusCode::kInvalidArgument, @@ -97,9 +98,9 @@ absl::string_view line) { std::vector<std::string> parts = absl::StrSplit(line, '='); if (parts.size() != 2 || absl::StripAsciiWhitespace(parts[0]) != name) { - return util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("Expected line in format ", name, " = value")); + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("Expected line to have the format: ", name, + " = value. Found: ", line)); } return std::string(absl::StripAsciiWhitespace(parts[1])); } @@ -131,68 +132,56 @@ // Aws::Auth::ProfileConfigFileAWSCredentialsProvider. util::StatusOr<Aws::Auth::AWSCredentials> GetAwsCredentials( absl::string_view credentials_path) { - if (!credentials_path.empty()) { // Read credentials from given file. - auto creds_result = ReadFile(std::string(credentials_path)); - if (!creds_result.ok()) { - return creds_result.status(); - } - std::vector<std::string> creds_lines = - absl::StrSplit(creds_result.value(), '\n'); - if (creds_lines.size() < 3) { - return util::Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("Invalid format of credentials in file ", - credentials_path)); - } - auto key_id_result = GetValue("aws_access_key_id", creds_lines[1]); - if (!key_id_result.ok()) { - return util::Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("Invalid format of credentials in file ", - credentials_path, " : ", - key_id_result.status().message())); - } - auto secret_key_result = GetValue("aws_secret_access_key", creds_lines[2]); - if (!secret_key_result.ok()) { - return util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("Invalid format of credentials in file ", - credentials_path, " : ", - secret_key_result.status().message())); - } - return Aws::Auth::AWSCredentials(key_id_result.value().c_str(), - secret_key_result.value().c_str()); + if (credentials_path.empty()) { + // Get default credentials. + Aws::Auth::DefaultAWSCredentialsProviderChain provider_chain; + return provider_chain.GetAWSCredentials(); } + // Read credentials from the given file. + util::StatusOr<std::string> creds_result = + ReadFile(std::string(credentials_path)); + if (!creds_result.ok()) { + return creds_result.status(); + } + std::vector<std::string> creds_lines = + absl::StrSplit(creds_result.value(), '\n'); + if (creds_lines.size() < 3) { + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("Invalid format of credentials in file ", + credentials_path)); + } + util::StatusOr<std::string> key_id_result = + GetValue("aws_access_key_id", creds_lines[1]); + if (!key_id_result.ok()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Invalid format of credentials in file ", credentials_path, + " : ", key_id_result.status().message())); + } + util::StatusOr<std::string> secret_key_result = + GetValue("aws_secret_access_key", creds_lines[2]); + if (!secret_key_result.ok()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Invalid format of credentials in file ", credentials_path, + " : ", secret_key_result.status().message())); + } + return Aws::Auth::AWSCredentials(key_id_result.value().c_str(), + secret_key_result.value().c_str()); +} - // Get default credentials. - Aws::Auth::DefaultAWSCredentialsProviderChain provider_chain; - return provider_chain.GetAWSCredentials(); +void InitAwsApi() { + Aws::SDKOptions options; + Aws::InitAPI(options); } } // namespace -bool AwsKmsClient::aws_api_is_initialized_; -absl::Mutex AwsKmsClient::aws_api_init_mutex_; - -void AwsKmsClient::InitAwsApi() { - absl::MutexLock lock(&aws_api_init_mutex_); - if (aws_api_is_initialized_) { - return; - } - Aws::SDKOptions options; - options.cryptoOptions.sha256Factory_create_fn = []() { - return Aws::MakeShared<AwsSha256Factory>(kAwsCryptoAllocationTag); - }; - options.cryptoOptions.sha256HMACFactory_create_fn = []() { - return Aws::MakeShared<AwsSha256HmacFactory>(kAwsCryptoAllocationTag); - }; - Aws::InitAPI(options); - aws_api_is_initialized_ = true; -} +static absl::once_flag aws_initialization_once; util::StatusOr<std::unique_ptr<AwsKmsClient>> AwsKmsClient::New( absl::string_view key_uri, absl::string_view credentials_path) { - if (!aws_api_is_initialized_) { - InitAwsApi(); - } + absl::call_once(aws_initialization_once, []() { InitAwsApi(); }); // Read credentials. util::StatusOr<Aws::Auth::AWSCredentials> credentials = GetAwsCredentials(credentials_path); @@ -217,7 +206,7 @@ auto client = absl::WrapUnique(new AwsKmsClient(*key_arn, *credentials)); // Create AWS KMSClient. client->aws_client_ = Aws::MakeShared<Aws::KMS::KMSClient>( - kAwsCryptoAllocationTag, client->credentials_, *client_config); + kTinkAwsKmsAllocationTag, client->credentials_, *client_config); return std::move(client); } @@ -230,33 +219,29 @@ return key_arn_.empty() ? true : key_arn_ == *key_arn; } -util::StatusOr<std::unique_ptr<Aead>> -AwsKmsClient::GetAead(absl::string_view key_uri) const { - if (!DoesSupport(key_uri)) { - if (!key_arn_.empty()) { - return util::Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("This client is bound to ", key_arn_, - " and cannot use key ", key_uri)); - } - return util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("This client does not support key ", key_uri)); +util::StatusOr<std::unique_ptr<Aead>> AwsKmsClient::GetAead( + absl::string_view key_uri) const { + util::StatusOr<std::string> key_arn = GetKeyArn(key_uri); + if (!key_arn.ok()) { + return key_arn.status(); } - // This client is bound to a specific key. if (!key_arn_.empty()) { + if (key_arn_ != *key_arn) { + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("This client is bound to ", key_arn_, + " and cannot use key ", key_uri)); + } return AwsKmsAead::New(key_arn_, aws_client_); } - // Create an Aws::KMS::KMSClient for the given key. - util::StatusOr<std::string> key_arn = GetKeyArn(key_uri); util::StatusOr<Aws::Client::ClientConfiguration> client_config = GetAwsClientConfig(*key_arn); if (!client_config.ok()) { return client_config.status(); } auto aws_client = Aws::MakeShared<Aws::KMS::KMSClient>( - kAwsCryptoAllocationTag, credentials_, *client_config); + kTinkAwsKmsAllocationTag, credentials_, *client_config); return AwsKmsAead::New(*key_arn, aws_client); }
diff --git a/cc/integration/awskms/aws_kms_client.h b/cc/integration/awskms/aws_kms_client.h index b97e627..2dac113 100644 --- a/cc/integration/awskms/aws_kms_client.h +++ b/cc/integration/awskms/aws_kms_client.h
@@ -19,10 +19,10 @@ #include <memory> -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" #include "aws/core/auth/AWSCredentialsProvider.h" #include "aws/kms/KMSClient.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" #include "tink/aead.h" #include "tink/kms_client.h" #include "tink/kms_clients.h" @@ -34,17 +34,23 @@ namespace integration { namespace awskms { -// AwsKmsClient is an implementation of KmsClient for -// <a href="https://aws.amazon.com/kms/">AWS KMS</a> +// AwsKmsClient is an implementation of KmsClient for AWS KMS +// (https://aws.amazon.com/kms/). class AwsKmsClient : public crypto::tink::KmsClient { public: + // Move only. + AwsKmsClient(AwsKmsClient&& other) = default; + AwsKmsClient& operator=(AwsKmsClient&& other) = default; + AwsKmsClient(const AwsKmsClient&) = delete; + AwsKmsClient& operator=(const AwsKmsClient&) = delete; + // Creates a new AwsKmsClient that is bound to the key specified in `key_uri`, // if not empty, and that uses the credentials in `credentials_path`, if not // empty, or the default ones to authenticate to the KMS. // // If `key_uri` is empty, then the client is not bound to any particular key. - static crypto::tink::util::StatusOr<std::unique_ptr<AwsKmsClient>> - New(absl::string_view key_uri, absl::string_view credentials_path); + static crypto::tink::util::StatusOr<std::unique_ptr<AwsKmsClient>> New( + absl::string_view key_uri, absl::string_view credentials_path); // Creates a new client and registers it in KMSClients. static crypto::tink::util::Status RegisterNewClient( @@ -55,10 +61,8 @@ // to a specific key. bool DoesSupport(absl::string_view key_uri) const override; - // Returns an Aead-primitive backed by KMS key specified by `key_uri`, - // provided that this KmsClient does support `key_uri`. - crypto::tink::util::StatusOr<std::unique_ptr<Aead>> - GetAead(absl::string_view key_uri) const override; + crypto::tink::util::StatusOr<std::unique_ptr<Aead>> GetAead( + absl::string_view key_uri) const override; private: AwsKmsClient(absl::string_view key_arn, Aws::Auth::AWSCredentials credentials) @@ -66,11 +70,6 @@ AwsKmsClient(Aws::Auth::AWSCredentials credentials) : credentials_(credentials) {} - // Initializes AWS API. - static void InitAwsApi(); - static bool aws_api_is_initialized_; - static absl::Mutex aws_api_init_mutex_; - std::string key_arn_; Aws::Auth::AWSCredentials credentials_; std::shared_ptr<Aws::KMS::KMSClient> aws_client_;
diff --git a/cc/integration/awskms/aws_kms_client_test.cc b/cc/integration/awskms/aws_kms_client_test.cc index 490cfc1..042d76b 100644 --- a/cc/integration/awskms/aws_kms_client_test.cc +++ b/cc/integration/awskms/aws_kms_client_test.cc
@@ -17,6 +17,8 @@ #include "tink/integration/awskms/aws_kms_client.h" #include <cstdlib> +#include <fstream> +#include <ios> #include <string> #include <vector> @@ -25,6 +27,7 @@ #include "gtest/gtest.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" +#include "tink/integration/awskms/internal/test_file_util.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" @@ -41,6 +44,7 @@ using ::crypto::tink::test::IsOkAndHolds; using ::crypto::tink::test::StatusIs; using ::testing::IsNull; +using ::testing::IsSubstring; using ::testing::Not; constexpr absl::string_view kAwsKey1 = @@ -51,7 +55,7 @@ TEST(AwsKmsClientTest, CreateClientNotBoundToSpecificKeySupportsAllValidKeys) { std::string creds_file = - absl::StrCat(getenv("TEST_SRCDIR"), "/tink_cc_awskms/testdata/aws/credentials.ini"); + internal::RunfilesPath("testdata/aws/credentials.ini"); util::StatusOr<std::unique_ptr<AwsKmsClient>> client = AwsKmsClient::New(/*key_uri=*/"", creds_file); ASSERT_THAT(client, IsOk()); @@ -64,7 +68,7 @@ // different key URI. TEST(AwsKmsClientTest, CreateClientBoundToSpecificKeySupportOnlyOneKey) { std::string creds_file = - absl::StrCat(getenv("TEST_SRCDIR"), "/tink_cc_awskms/testdata/aws/credentials.ini"); + internal::RunfilesPath("testdata/aws/credentials.ini"); util::StatusOr<std::unique_ptr<AwsKmsClient>> client = AwsKmsClient::New(kAwsKey1, creds_file); ASSERT_THAT(client, IsOk()); @@ -75,18 +79,43 @@ TEST(AwsKmsClientTest, RegisterKmsClient) { std::string creds_file = - absl::StrCat(getenv("TEST_SRCDIR"), "/tink_cc_awskms/testdata/aws/credentials.ini"); + internal::RunfilesPath("testdata/aws/credentials.ini"); ASSERT_THAT(AwsKmsClient::RegisterNewClient(kAwsKey1, creds_file), IsOk()); util::StatusOr<const KmsClient*> kms_client = KmsClients::Get(kAwsKey1); EXPECT_THAT(kms_client, IsOkAndHolds(Not(IsNull()))); } TEST(AwsKmsClientTest, RegisterKmsClientFailsWhenKeyIsInvalid) { - std::string creds_file = absl::StrCat(getenv("TEST_SRCDIR"), - "/tink_cc_awskms/testdata/gcp/credentials.json"); - auto client = AwsKmsClient::RegisterNewClient( - "gcp-kms://projects/someProject/.../cryptoKeys/key1", creds_file); + util::Status client = AwsKmsClient::RegisterNewClient( + "gcp-kms://projects/someProject/.../cryptoKeys/key1", + internal::RunfilesPath("testdata/aws/credentials.ini")); EXPECT_THAT(client, StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_PRED_FORMAT2(IsSubstring, "Invalid key URI", + std::string(client.message())); +} + +TEST(AwsKmsClientTest, RegisterKmsClientFailsWhenCredentialsDoNotExist) { + util::Status client = + AwsKmsClient::RegisterNewClient(kAwsKey1, "this/file/does/not/exist.ini"); + EXPECT_THAT(client, StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_PRED_FORMAT2(IsSubstring, "Error opening file", + std::string(client.message())); +} + +TEST(AwsKmsClientTest, RegisterKmsClientFailsWhenMalformedCredentials) { + // Create an invalid credentials file. + std::string malformed_content = "These are malformed credentials."; + std::string invalid_credentials_file = + internal::RunfilesPath("testdata/aws/invalid.ini"); + std::ofstream out_stream(invalid_credentials_file, std::ios::binary); + out_stream.write(malformed_content.data(), malformed_content.size()); + out_stream.close(); + + util::Status client = + AwsKmsClient::RegisterNewClient(kAwsKey1, invalid_credentials_file); + EXPECT_THAT(client, StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_PRED_FORMAT2(IsSubstring, "Invalid format", + std::string(client.message())); } } // namespace
diff --git a/cc/integration/awskms/internal/BUILD.bazel b/cc/integration/awskms/internal/BUILD.bazel new file mode 100644 index 0000000..2f41114 --- /dev/null +++ b/cc/integration/awskms/internal/BUILD.bazel
@@ -0,0 +1,17 @@ +package(default_visibility = ["//:__subpackages__"]) + +licenses(["notice"]) + +cc_library( + name = "test_file_util", + srcs = ["test_file_util_bazel.cc"], + hdrs = ["test_file_util.h"], + include_prefix = "tink/integration/awskms/internal", + testonly = 1, + deps = [ + "@bazel_tools//tools/cpp/runfiles", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/strings", + ], + alwayslink = 1, +)
diff --git a/cc/integration/awskms/internal/test_file_util.h b/cc/integration/awskms/internal/test_file_util.h new file mode 100644 index 0000000..80aef6d --- /dev/null +++ b/cc/integration/awskms/internal/test_file_util.h
@@ -0,0 +1,36 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_INTEGRATION_AWSKMS_INTERNAL_TEST_FILE_UTIL_H_ +#define TINK_INTEGRATION_AWSKMS_INTERNAL_TEST_FILE_UTIL_H_ + +#include "absl/strings/string_view.h" + +namespace crypto { +namespace tink { +namespace integration { +namespace awskms { +namespace internal { + +// Returns the path of the specified file in the runfiles directory. +std::string RunfilesPath(absl::string_view path); + +} // namespace internal +} // namespace awskms +} // namespace integration +} // namespace tink +} // namespace crypto + +#endif // TINK_INTEGRATION_AWSKMS_INTERNAL_TEST_FILE_UTIL_H_
diff --git a/cc/integration/awskms/internal/test_file_util_bazel.cc b/cc/integration/awskms/internal/test_file_util_bazel.cc new file mode 100644 index 0000000..e59bf04 --- /dev/null +++ b/cc/integration/awskms/internal/test_file_util_bazel.cc
@@ -0,0 +1,45 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// +#include "tink/integration/awskms/internal/test_file_util.h" + +#include "absl/log/check.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "tools/cpp/runfiles/runfiles.h" + +namespace crypto { +namespace tink { +namespace integration { +namespace awskms { +namespace internal { + +using ::bazel::tools::cpp::runfiles::Runfiles; + +std::string RunfilesPath(absl::string_view path) { + std::string error; + std::unique_ptr<Runfiles> runfiles(Runfiles::CreateForTest(&error)); + CHECK(runfiles != nullptr) << "Unable to determine runfile path: "; + const char* workspace_dir = getenv("TEST_WORKSPACE"); + CHECK(workspace_dir != nullptr && workspace_dir[0] != '\0') + << "Unable to determine workspace name."; + return runfiles->Rlocation(absl::StrCat(workspace_dir, "/", path)); +} + +} // namespace internal +} // namespace awskms +} // namespace integration +} // namespace tink +} // namespace crypto
diff --git a/cc/integration/awskms/testdata/README.md b/cc/integration/awskms/testdata/README.md deleted file mode 100644 index 9d362c7..0000000 --- a/cc/integration/awskms/testdata/README.md +++ /dev/null
@@ -1,21 +0,0 @@ -This folder contains AWS and GCP credentials that are used for testing Tink. - -# AWS - -For security reasons, all AWS credentials in this folder are invalid. If you -want to run tests that depend on them, please create your own -[AWS access keys](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html). -The credentials are required in several formats expected by different APIs. For -example, Java expects the credentials as a -[properties file](https://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingAcctOrUserCredentials.html). -In order to cover all tests across all languages you will have to replace -`aws/credentials.cred`, `aws/credentials.csv` and `aws/credentials.ini`. These -can be generated in a similar way to this -[script](https://github.com/google/tink/blob/master/kokoro/copy_credentials.sh). - -# GCP - -For security reasons, all GCP credentials in this folder are invalid. If you -want to run tests that depend on them, please create your own -[Cloud KMS key](https://cloud.google.com/kms/docs/creating-keys), and copy the -credentials to `gcp/credential.json` and the key URI to `gcp/key_name.txt`.
diff --git a/cc/integration/awskms/testdata/aws/README.md b/cc/integration/awskms/testdata/aws/README.md new file mode 100644 index 0000000..ca15b34 --- /dev/null +++ b/cc/integration/awskms/testdata/aws/README.md
@@ -0,0 +1,16 @@ +This folder contains AWS credentials that are used for testing Tink. + +For security reasons, all credentials in this folder are invalid. If you want to +run tests that depend on them, please create your own [AWS access +keys][aws-access-keys]. + +The credentials are required in several formats expected by different APIs. For +example, Java expects the credentials as a [properties file][properties-file]. +In order to cover all tests across all languages you have to replace +`aws/credentials.cred`, `aws/credentials.csv` and `aws/credentials.ini`. These +can be generated in a similar way to this [credential copying +script][copy-credentials-script]. + +[aws-access-keys]: https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html +[properties-file]: https://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingAcctOrUserCredentials.html +[copy-credentials-script]: https://github.com/google/tink/blob/master/kokoro/copy_credentials.sh
diff --git a/cc/integration/awskms/testdata/gcp/README.md b/cc/integration/awskms/testdata/gcp/README.md new file mode 100644 index 0000000..81b5de0 --- /dev/null +++ b/cc/integration/awskms/testdata/gcp/README.md
@@ -0,0 +1,7 @@ +This folder contains GCP credentials that are used for testing Tink. + +For security reasons, all credentials in this folder are invalid. + +If you want to run tests that depend on them, please create your own +[Cloud KMS key](https://cloud.google.com/kms/docs/creating-keys), and copy the +credentials to `gcp/credential.json` and the key URI to `gcp/key_name.txt`.
diff --git a/cc/integration/awskms/third_party/aws_checksums.BUILD.bazel b/cc/integration/awskms/third_party/aws_checksums.BUILD.bazel index 25fc636..916a28b 100644 --- a/cc/integration/awskms/third_party/aws_checksums.BUILD.bazel +++ b/cc/integration/awskms/third_party/aws_checksums.BUILD.bazel
@@ -3,9 +3,12 @@ cc_library( name = "aws_checksums", srcs = glob([ - "source/intel/*.c", "source/*.c", - ]), + ]) + select({ + "@platforms//cpu:x86_64": glob(["source/intel/*.c"]), + "@platforms//cpu:aarch64": glob(["source/arm/*.c"]), + "//conditions:default": ["@platforms//:incompatible"], + }), hdrs = glob([ "include/**/*.h" ]),
diff --git a/cc/integration/awskms/third_party/curl.BUILD.bazel b/cc/integration/awskms/third_party/curl.BUILD.bazel index 210b632..5a6530d 100644 --- a/cc/integration/awskms/third_party/curl.BUILD.bazel +++ b/cc/integration/awskms/third_party/curl.BUILD.bazel
@@ -3,24 +3,6 @@ licenses(["notice"]) # MIT/X derivative license -# Settings for building in various environments. -config_setting( - name = "linux_x86_64", - values = {"cpu": "k8"}, -) - -config_setting( - name = "mac_x86_64", - values = {"cpu": "darwin"}, -) - -config_setting( - name = "darwin_arm64", - values = { - "cpu": "darwin_arm64", - }, -) - cc_library( name = "curl", srcs = [ @@ -220,15 +202,9 @@ "lib/wildcard.h", "lib/x509asn1.h", ] + select({ - ":mac_x86_64": [ - "lib/vtls/darwinssl.c", - ], - ":darwin_arm64": [ - "lib/vtls/darwinssl.c", - ], - ":linux_x86_64": [ - "lib/vtls/openssl.c", - ], + "@platforms//os:macos": ["lib/vtls/darwinssl.c"], + "@platforms//os:linux": ["lib/vtls/openssl.c"], + "//conditions:default": ["@platforms//:incompatible"], }), hdrs = [ ":configure", @@ -252,34 +228,21 @@ "-DHAVE_ZLIB_H", "-Wno-string-plus-int", ] + select({ - ":mac_x86_64": [ - "-fno-constant-cfstrings", - ], - ":darwin_arm64": [ - "-fno-constant-cfstrings", - ], - ":linux_x86_64": [ - "-DCURL_MAX_WRITE_SIZE=65536", - ], + "@platforms//os:macos": ["-fno-constant-cfstrings"], + "@platforms//os:linux": ["-DCURL_MAX_WRITE_SIZE=65536"], + "//conditions:default": ["@platforms//:incompatible"], }), defines = ["CURL_STATICLIB"], includes = ["include"], linkopts = select({ - ":mac_x86_64": [ + "@platforms//os:macos": [ "-Wl,-framework", "-Wl,CoreFoundation", "-Wl,-framework", "-Wl,Security", ], - ":darwin_arm64": [ - "-Wl,-framework", - "-Wl,CoreFoundation", - "-Wl,-framework", - "-Wl,Security", - ], - ":linux_x86_64": [ - "-lrt", - ], + "@platforms//os:linux": ["-lrt"], + "//conditions:default": ["@platforms//:incompatible"], }), visibility = ["//visibility:public"], deps = [
diff --git a/cc/integration/gcpkms/.bazelrc b/cc/integration/gcpkms/.bazelrc new file mode 100644 index 0000000..0fe2c3f --- /dev/null +++ b/cc/integration/gcpkms/.bazelrc
@@ -0,0 +1,4 @@ +# Minumum C++ version. Override it building this project with +# `bazel build --cxxopt='-std=c++<XY>' --host_cxxopt='c++<XY>' ...` +# (Both -std and --host_cxxopt must be set to force the desired version.) +build --cxxopt='-std=c++14' --host_cxxopt='-std=c++14'
diff --git a/cc/integration/gcpkms/.bazelversion b/cc/integration/gcpkms/.bazelversion new file mode 100644 index 0000000..09b254e --- /dev/null +++ b/cc/integration/gcpkms/.bazelversion
@@ -0,0 +1 @@ +6.0.0
diff --git a/cc/integration/gcpkms/BUILD.bazel b/cc/integration/gcpkms/BUILD.bazel index 86d878f..4e9139d 100644 --- a/cc/integration/gcpkms/BUILD.bazel +++ b/cc/integration/gcpkms/BUILD.bazel
@@ -39,17 +39,25 @@ ], ) -# tests - cc_test( - name = "gcp_kms_aead_test", + name = "gcp_kms_aead_integration_test", size = "medium", - srcs = ["gcp_kms_aead_test.cc"], + srcs = ["gcp_kms_aead_integration_test.cc"], + data = [ + "//testdata/gcp:credentials", + "@google_root_pem//file" + ], + # This target requires valid credentials to interact with the Google Cloud + # KMS. + tags = ["manual"], deps = [ ":gcp_kms_aead", + ":gcp_kms_client", + "@bazel_tools//tools/cpp/runfiles", + "@com_google_absl//absl/log:check", "@com_google_googletest//:gtest_main", - "@tink_cc//util:status", "@tink_cc//util:statusor", + "@tink_cc//util:test_matchers", ], )
diff --git a/cc/integration/gcpkms/gcp_kms_aead.cc b/cc/integration/gcpkms/gcp_kms_aead.cc index ae57ff1..1d879a4 100644 --- a/cc/integration/gcpkms/gcp_kms_aead.cc +++ b/cc/integration/gcpkms/gcp_kms_aead.cc
@@ -16,13 +16,12 @@ #include "tink/integration/gcpkms/gcp_kms_aead.h" +#include <memory> #include <string> -#include <utility> #include "google/cloud/kms/v1/service.grpc.pb.h" #include "absl/memory/memory.h" #include "absl/status/status.h" -#include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "tink/aead.h" @@ -34,37 +33,27 @@ namespace integration { namespace gcpkms { -using crypto::tink::util::Status; -using crypto::tink::util::StatusOr; -using google::cloud::kms::v1::DecryptRequest; -using google::cloud::kms::v1::DecryptResponse; -using google::cloud::kms::v1::EncryptRequest; -using google::cloud::kms::v1::EncryptResponse; -using google::cloud::kms::v1::KeyManagementService; -using grpc::ClientContext; +using ::google::cloud::kms::v1::DecryptRequest; +using ::google::cloud::kms::v1::DecryptResponse; +using ::google::cloud::kms::v1::EncryptRequest; +using ::google::cloud::kms::v1::EncryptResponse; +using ::google::cloud::kms::v1::KeyManagementService; -GcpKmsAead::GcpKmsAead( +util::StatusOr<std::unique_ptr<Aead>> GcpKmsAead::New( absl::string_view key_name, - std::shared_ptr<KeyManagementService::Stub> kms_stub) - : key_name_(key_name), kms_stub_(kms_stub) {} - -// static -StatusOr<std::unique_ptr<Aead>> -GcpKmsAead::New(absl::string_view key_name, - std::shared_ptr<KeyManagementService::Stub> kms_stub) { + std::shared_ptr<KeyManagementService::Stub> kms_stub) { if (key_name.empty()) { - return Status(absl::StatusCode::kInvalidArgument, + return util::Status(absl::StatusCode::kInvalidArgument, "Key URI cannot be empty."); } if (kms_stub == nullptr) { - return Status(absl::StatusCode::kInvalidArgument, + return util::Status(absl::StatusCode::kInvalidArgument, "KMS stub cannot be null."); } - std::unique_ptr<Aead> aead(new GcpKmsAead(key_name, kms_stub)); - return std::move(aead); + return absl::WrapUnique(new GcpKmsAead(key_name, kms_stub)); } -StatusOr<std::string> GcpKmsAead::Encrypt( +util::StatusOr<std::string> GcpKmsAead::Encrypt( absl::string_view plaintext, absl::string_view associated_data) const { EncryptRequest req; req.set_name(key_name_); @@ -72,19 +61,21 @@ req.set_additional_authenticated_data(std::string(associated_data)); EncryptResponse resp; - ClientContext context; + grpc::ClientContext context; context.AddMetadata("x-goog-request-params", absl::StrCat("name=", key_name_)); - auto status = kms_stub_->Encrypt(&context, req, &resp); + grpc::Status status = kms_stub_->Encrypt(&context, req, &resp); - if (status.ok()) return resp.ciphertext(); - return Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("GCP KMS encryption failed: ", status.error_message())); + if (!status.ok()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("GCP KMS encryption failed: ", status.error_message())); + } + return resp.ciphertext(); } -StatusOr<std::string> GcpKmsAead::Decrypt( +util::StatusOr<std::string> GcpKmsAead::Decrypt( absl::string_view ciphertext, absl::string_view associated_data) const { DecryptRequest req; req.set_name(key_name_); @@ -92,16 +83,18 @@ req.set_additional_authenticated_data(std::string(associated_data)); DecryptResponse resp; - ClientContext context; + grpc::ClientContext context; context.AddMetadata("x-goog-request-params", absl::StrCat("name=", key_name_)); - auto status = kms_stub_->Decrypt(&context, req, &resp); + grpc::Status status = kms_stub_->Decrypt(&context, req, &resp); - if (status.ok()) return resp.plaintext(); - return Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("GCP KMS encryption failed: ", status.error_message())); + if (!status.ok()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("GCP KMS encryption failed: ", status.error_message())); + } + return resp.plaintext(); } } // namespace gcpkms
diff --git a/cc/integration/gcpkms/gcp_kms_aead.h b/cc/integration/gcpkms/gcp_kms_aead.h index 8e403ff..ff7833c 100644 --- a/cc/integration/gcpkms/gcp_kms_aead.h +++ b/cc/integration/gcpkms/gcp_kms_aead.h
@@ -17,6 +17,7 @@ #ifndef TINK_INTEGRATION_GCPKMS_GCP_KMS_AEAD_H_ #define TINK_INTEGRATION_GCPKMS_GCP_KMS_AEAD_H_ +#include <memory> #include <string> #include "google/cloud/kms/v1/service.grpc.pb.h" @@ -29,18 +30,25 @@ namespace integration { namespace gcpkms { -// GcpKmsAead is an implementation of AEAD that forwards -// encryption/decryption requests to a key managed by -// <a href="https://cloud.google.com/kms/">Google Cloud KMS</a>. +// GcpKmsAead is an implementation of AEAD that forwards encryption/decryption +// requests to a key managed by the Google Cloud KMS +// (https://cloud.google.com/kms/). class GcpKmsAead : public Aead { public: - // Creates a new GcpKmsAead that is bound to the key specified in 'key_name', + // Move only. + GcpKmsAead(GcpKmsAead&& other) = default; + GcpKmsAead& operator=(GcpKmsAead&& other) = default; + GcpKmsAead(const GcpKmsAead&) = delete; + GcpKmsAead& operator=(const GcpKmsAead&) = delete; + + // Creates a new GcpKmsAead that is bound to the key specified in `key_name`, // and that uses the channel when communicating with the KMS. - // Valid values for 'key_name' have the following format: + // + // Valid values for `key_name` have the following format: // projects/*/locations/*/keyRings/*/cryptoKeys/*. // See https://cloud.google.com/kms/docs/object-hierarchy for more info. - static crypto::tink::util::StatusOr<std::unique_ptr<Aead>> - New(absl::string_view key_name, + static crypto::tink::util::StatusOr<std::unique_ptr<Aead>> New( + absl::string_view key_name, std::shared_ptr<google::cloud::kms::v1::KeyManagementService::Stub> kms_stub); @@ -52,18 +60,17 @@ absl::string_view ciphertext, absl::string_view associated_data) const override; - virtual ~GcpKmsAead() {} - private: - GcpKmsAead( + explicit GcpKmsAead( absl::string_view key_name, std::shared_ptr<google::cloud::kms::v1::KeyManagementService::Stub> - kms_stub); - std::string key_name_; // The location of a crypto key in GCP KMS. - std::shared_ptr<google::cloud::kms::v1::KeyManagementService::Stub> - kms_stub_; -}; + kms_stub) + : key_name_(key_name), kms_stub_(kms_stub) {} + // The location of a crypto key in GCP KMS. + std::string key_name_; + std::shared_ptr<google::cloud::kms::v1::KeyManagementService::Stub> kms_stub_; +}; } // namespace gcpkms } // namespace integration
diff --git a/cc/integration/gcpkms/gcp_kms_aead_integration_test.cc b/cc/integration/gcpkms/gcp_kms_aead_integration_test.cc new file mode 100644 index 0000000..4b7b1e2 --- /dev/null +++ b/cc/integration/gcpkms/gcp_kms_aead_integration_test.cc
@@ -0,0 +1,92 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "gtest/gtest.h" +#include "absl/log/check.h" +#include "tink/integration/gcpkms/gcp_kms_aead.h" +#include "tink/integration/gcpkms/gcp_kms_client.h" +#include "tink/util/test_matchers.h" +#include "tools/cpp/runfiles/runfiles.h" + +namespace crypto { +namespace tink { +namespace integration { +namespace gcpkms { +namespace { + +using ::bazel::tools::cpp::runfiles::Runfiles; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::testing::Environment; + +constexpr absl::string_view kGcpKmsKeyUri = + "gcp-kms://projects/tink-test-infrastructure/locations/global/keyRings/" + "unit-and-integration-testing/cryptoKeys/aead-key"; + +std::string RunfilesPath(absl::string_view path) { + std::string error; + std::unique_ptr<Runfiles> runfiles(Runfiles::CreateForTest(&error)); + CHECK(runfiles != nullptr) << "Unable to determine runfile path: "; + const char* workspace_dir = getenv("TEST_WORKSPACE"); + CHECK(workspace_dir != nullptr && workspace_dir[0] != '\0') + << "Unable to determine workspace name."; + return runfiles->Rlocation(absl::StrCat(workspace_dir, "/", path)); +} + +class GcpKmsAeadIntegrationTestEnvironment : public Environment { + public: + ~GcpKmsAeadIntegrationTestEnvironment() override = default; + + void SetUp() override { + // Set root certificates for gRPC in Bazel Test which are needed on macOS. + const char* test_srcdir = getenv("TEST_SRCDIR"); + if (test_srcdir != nullptr) { + setenv( + "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH", + absl::StrCat(test_srcdir, "/google_root_pem/file/downloaded").c_str(), + /*overwrite=*/false); + } + } +}; + +Environment* const foo_env = testing::AddGlobalTestEnvironment( + new GcpKmsAeadIntegrationTestEnvironment()); + +TEST(GcpKmsAeadIntegrationTest, EncryptDecrypt) { + std::string credentials = RunfilesPath("testdata/gcp/credential.json"); + util::StatusOr<std::unique_ptr<GcpKmsClient>> client = + GcpKmsClient::New(/*key_uri=*/"", credentials); + ASSERT_THAT(client, IsOk()); + + util::StatusOr<std::unique_ptr<Aead>> aead = + (*client)->GetAead(kGcpKmsKeyUri); + ASSERT_THAT(aead, IsOk()); + + constexpr absl::string_view kPlaintext = "plaintext"; + constexpr absl::string_view kAssociatedData = "aad"; + + util::StatusOr<std::string> ciphertext = + (*aead)->Encrypt(kPlaintext, kAssociatedData); + ASSERT_THAT(ciphertext, IsOk()); + EXPECT_THAT((*aead)->Decrypt(*ciphertext, kAssociatedData), + IsOkAndHolds(kPlaintext)); +} + +} // namespace +} // namespace gcpkms +} // namespace integration +} // namespace tink +} // namespace crypto
diff --git a/cc/integration/gcpkms/gcp_kms_aead_test.cc b/cc/integration/gcpkms/gcp_kms_aead_test.cc deleted file mode 100644 index a94433e..0000000 --- a/cc/integration/gcpkms/gcp_kms_aead_test.cc +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright 2019 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#include "gtest/gtest.h" -#include "tink/integration/gcpkms/gcp_kms_aead.h" - -namespace crypto { -namespace tink { -namespace subtle { -namespace { - -using crypto::tink::integration::gcpkms::GcpKmsAead; - -class GcpKmsAeadTest : public ::testing::Test { - // TODO(kste): Add tests when mock for - // google::cloud::kms::v1::KeyManagementService::StubInterface is available. -}; - - -} // namespace -} // namespace subtle -} // namespace tink -} // namespace crypto
diff --git a/cc/integration/gcpkms/gcp_kms_client.cc b/cc/integration/gcpkms/gcp_kms_client.cc index c90572c..f38d133 100644 --- a/cc/integration/gcpkms/gcp_kms_client.cc +++ b/cc/integration/gcpkms/gcp_kms_client.cc
@@ -17,6 +17,7 @@ #include <fstream> #include <iostream> +#include <memory> #include <sstream> #include <string> #include <utility> @@ -26,9 +27,8 @@ #include "grpcpp/security/credentials.h" #include "absl/memory/memory.h" #include "absl/status/status.h" -#include "absl/strings/ascii.h" +#include "absl/strings/match.h" #include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "tink/integration/gcpkms/gcp_kms_aead.h" #include "tink/kms_clients.h" @@ -43,23 +43,18 @@ namespace { -using crypto::tink::Version; -using crypto::tink::util::Status; -using crypto::tink::util::StatusOr; -using google::cloud::kms::v1::KeyManagementService; -using grpc::ChannelArguments; -using grpc::ChannelCredentials; +using ::google::cloud::kms::v1::KeyManagementService; -static constexpr char kKeyUriPrefix[] = "gcp-kms://"; -static constexpr char kGcpKmsServer[] = "cloudkms.googleapis.com"; -static constexpr char kTinkUserAgentPrefix[] = "Tink/"; +static constexpr absl::string_view kKeyUriPrefix = "gcp-kms://"; +static constexpr absl::string_view kGcpKmsServer = "cloudkms.googleapis.com"; +static constexpr absl::string_view kTinkUserAgentPrefix = "Tink/"; -StatusOr<std::string> ReadFile(absl::string_view filename) { +util::StatusOr<std::string> ReadFile(absl::string_view filename) { std::ifstream input_stream; input_stream.open(std::string(filename), std::ifstream::in); if (!input_stream.is_open()) { - return Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("Error reading file ", filename)); + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("Error reading file ", filename)); } std::stringstream input; input << input_stream.rdbuf(); @@ -67,104 +62,110 @@ return input.str(); } -StatusOr<std::shared_ptr<ChannelCredentials>> GetCredentials( +util::StatusOr<std::shared_ptr<grpc::ChannelCredentials>> GetCredentials( absl::string_view credentials_path) { if (credentials_path.empty()) { - auto creds = grpc::GoogleDefaultCredentials(); + std::shared_ptr<grpc::ChannelCredentials> creds = + grpc::GoogleDefaultCredentials(); if (creds == nullptr) { - return Status(absl::StatusCode::kInternal, - "Could not read default credentials"); + return util::Status(absl::StatusCode::kInternal, + "Could not read default credentials"); } return creds; } // Try reading credentials from a file. - auto json_creds_result = ReadFile(credentials_path); - if (!json_creds_result.ok()) return json_creds_result.status(); - auto creds = - grpc::ServiceAccountJWTAccessCredentials(json_creds_result.value()); - if (creds != nullptr) { - // Creating "empty" 'channel_creds', to convert 'creds' - // to ChannelCredentials via CompositeChannelCredentials(). - auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions()); - return grpc::CompositeChannelCredentials(channel_creds, creds); + util::StatusOr<std::string> json_creds_result = ReadFile(credentials_path); + if (!json_creds_result.ok()) { + return json_creds_result.status(); } - return Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("Could not load credentials from file ", credentials_path)); + std::shared_ptr<grpc::CallCredentials> creds = + grpc::ServiceAccountJWTAccessCredentials(json_creds_result.value()); + if (creds == nullptr) { + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("Could not load credentials from file ", + credentials_path)); + } + // Creating "empty" 'channel_creds', to convert 'creds' to ChannelCredentials + // via CompositeChannelCredentials(). + std::shared_ptr<grpc::ChannelCredentials> channel_creds = + grpc::SslCredentials(grpc::SslCredentialsOptions()); + return grpc::CompositeChannelCredentials(channel_creds, creds); } -// Returns GCP KMS key name contained in 'key_uri'. -// If 'key_uri' does not refer to an GCP key, returns an empty string. -std::string GetKeyName(absl::string_view key_uri) { - if (!absl::StartsWithIgnoreCase(key_uri, kKeyUriPrefix)) return ""; - return std::string(key_uri.substr(std::string(kKeyUriPrefix).length())); +// Returns GCP KMS key name contained in `key_uri`. If `key_uri` does not refer +// to a GCP key, returns an error status. +util::StatusOr<std::string> GetKeyName(absl::string_view key_uri) { + if (!absl::StartsWithIgnoreCase(key_uri, kKeyUriPrefix)) { + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("The key URI ", key_uri, + " does not start with ", kKeyUriPrefix)); + } + return std::string(key_uri.substr(kKeyUriPrefix.length())); } } // namespace -// static -StatusOr<std::unique_ptr<GcpKmsClient>> GcpKmsClient::New( +util::StatusOr<std::unique_ptr<GcpKmsClient>> GcpKmsClient::New( absl::string_view key_uri, absl::string_view credentials_path) { - std::unique_ptr<GcpKmsClient> client(new GcpKmsClient()); - - // If a specific key is given, create a GCP KMSClient. + // Empty key name by default. + std::string key_name = ""; if (!key_uri.empty()) { - client->key_name_ = GetKeyName(key_uri); - if (client->key_name_.empty()) { - return Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("Key '", key_uri, "' not supported")); + util::StatusOr<std::string> key_name_from_uri = GetKeyName(key_uri); + if (!key_name_from_uri.ok()) { + return key_name_from_uri.status(); } + key_name = key_name_from_uri.value(); } + // Read credentials. - auto creds_result = GetCredentials(credentials_path); + util::StatusOr<std::shared_ptr<grpc::ChannelCredentials>> creds_result = + GetCredentials(credentials_path); if (!creds_result.ok()) { return creds_result.status(); } // Create a KMS stub. - ChannelArguments args; + grpc::ChannelArguments args; args.SetUserAgentPrefix( - absl::StrCat(kTinkUserAgentPrefix, Version::kTinkVersion, " CPP-Python")); - client->kms_stub_ = KeyManagementService::NewStub( - grpc::CreateCustomChannel(kGcpKmsServer, creds_result.value(), args)); - return std::move(client); + absl::StrCat(kTinkUserAgentPrefix, Version::kTinkVersion, " CPP")); + std::shared_ptr<KeyManagementService::Stub> kms_stub = + KeyManagementService::NewStub(grpc::CreateCustomChannel( + std::string(kGcpKmsServer), creds_result.value(), args)); + return absl::WrapUnique(new GcpKmsClient(key_name, std::move(kms_stub))); } bool GcpKmsClient::DoesSupport(absl::string_view key_uri) const { - if (!key_name_.empty()) { - return key_name_ == GetKeyName(key_uri); + util::StatusOr<std::string> key_name = GetKeyName(key_uri); + if (!key_name.ok()) { + return false; } - return !GetKeyName(key_uri).empty(); + return key_name_.empty() ? true : key_name_ == *key_name; } -StatusOr<std::unique_ptr<Aead>> GcpKmsClient::GetAead( +util::StatusOr<std::unique_ptr<Aead>> GcpKmsClient::GetAead( absl::string_view key_uri) const { - if (!DoesSupport(key_uri)) { - if (!key_name_.empty()) { - return Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("This client is bound to ", key_name_, - " and cannot use key ", key_uri)); - } else { - return Status(absl::StatusCode::kInvalidArgument, - absl::StrCat("This client does not support key ", key_uri)); - } + util::StatusOr<std::string> key_name_from_key_uri = GetKeyName(key_uri); + // key_uri is invalid. + if (!key_name_from_key_uri.ok()) { + return key_name_from_key_uri.status(); } - if (!key_name_.empty()) { // This client is bound to a specific key. - return GcpKmsAead::New(key_name_, kms_stub_); - } else { // Create an GCP KMSClient for the given key. - auto key_name = GetKeyName(key_uri); - return GcpKmsAead::New(key_name, kms_stub_); + // key_uri is valid, but if key_name_ is not empty key_name_from_key_uri must + // be equal to key_name_. + if (!key_name_.empty() && key_name_ != *key_name_from_key_uri) { + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("This client is bound to ", key_name_, + " and cannot use key ", key_uri)); } + return GcpKmsAead::New(*key_name_from_key_uri, kms_stub_); } -Status GcpKmsClient::RegisterNewClient(absl::string_view key_uri, - absl::string_view credentials_path) { +util::Status GcpKmsClient::RegisterNewClient( + absl::string_view key_uri, absl::string_view credentials_path) { auto client_result = GcpKmsClient::New(key_uri, credentials_path); if (!client_result.ok()) { return client_result.status(); } - return KmsClients::Add(std::move(client_result.value())); }
diff --git a/cc/integration/gcpkms/gcp_kms_client.h b/cc/integration/gcpkms/gcp_kms_client.h index 2040eaf..0523dd7 100644 --- a/cc/integration/gcpkms/gcp_kms_client.h +++ b/cc/integration/gcpkms/gcp_kms_client.h
@@ -19,6 +19,7 @@ #include <memory> #include <string> +#include <utility> #include "google/cloud/kms/v1/service.grpc.pb.h" #include "grpcpp/channel.h" @@ -33,40 +34,48 @@ namespace integration { namespace gcpkms { - -// GcpKmsClient is an implementation of KmsClient for -// <a href="https://cloud.google.com/kms/">Google Cloud KMS</a>. -class GcpKmsClient : public crypto::tink::KmsClient { +// GcpKmsClient is an implementation of KmsClient for Google Cloud KMS +// (https://cloud.google.com/kms/). +class GcpKmsClient : public crypto::tink::KmsClient { public: - // Creates a new GcpKmsClient that is bound to the key specified in 'key_uri', - // and that uses the specifed credentials when communicating with the KMS. + // Move only. + GcpKmsClient(GcpKmsClient&& other) = default; + GcpKmsClient& operator=(GcpKmsClient&& other) = default; + GcpKmsClient(const GcpKmsClient&) = delete; + GcpKmsClient& operator=(const GcpKmsClient&) = delete; + + // Creates a new GcpKmsClient that is bound to the key specified in `key_uri`, + // and that uses the specified credentials when communicating with the KMS. // - // Either of arguments can be empty. - // If 'key_uri' is empty, then the client is not bound to any particular key. - // If 'credential_path' is empty, then default credentials will be used. - static crypto::tink::util::StatusOr<std::unique_ptr<GcpKmsClient>> - New(absl::string_view key_uri, absl::string_view credentials_path); + // Either argument can be empty. + // If `key_uri` is empty, then the client is not bound to any particular key. + // If `credential_path` is empty, then default credentials will be used. + static crypto::tink::util::StatusOr<std::unique_ptr<GcpKmsClient>> New( + absl::string_view key_uri, absl::string_view credentials_path); // Creates a new client and registers it in KMSClients. static crypto::tink::util::Status RegisterNewClient( absl::string_view key_uri, absl::string_view credentials_path); - // Returns true iff this client does support KMS key specified by 'key_uri'. + // Returns true iff this client does support KMS key specified by `key_uri`. bool DoesSupport(absl::string_view key_uri) const override; - // Returns an Aead-primitive backed by KMS key specified by 'key_uri', - // provided that this KmsClient does support 'key_uri'. - crypto::tink::util::StatusOr<std::unique_ptr<Aead>> - GetAead(absl::string_view key_uri) const override; + // Returns an Aead-primitive backed by KMS key specified by `key_uri`, + // provided that this KmsClient does support `key_uri`. + crypto::tink::util::StatusOr<std::unique_ptr<Aead>> GetAead( + absl::string_view key_uri) const override; private: - GcpKmsClient() {} + explicit GcpKmsClient( + std::string key_name, + std::shared_ptr<google::cloud::kms::v1::KeyManagementService::Stub> + kms_stub) + : key_name_(key_name), kms_stub_(std::move(kms_stub)) {} std::string key_name_; std::shared_ptr<google::cloud::kms::v1::KeyManagementService::Stub> kms_stub_; }; - } // namespace gcpkms } // namespace integration } // namespace tink
diff --git a/cc/integration/gcpkms/gcp_kms_client_test.cc b/cc/integration/gcpkms/gcp_kms_client_test.cc index c8400f4..a53eed7 100644 --- a/cc/integration/gcpkms/gcp_kms_client_test.cc +++ b/cc/integration/gcpkms/gcp_kms_client_test.cc
@@ -29,16 +29,14 @@ #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" -using ::crypto::tink::test::IsOk; -using ::crypto::tink::test::StatusIs; - namespace crypto { namespace tink { namespace integration { namespace gcpkms { namespace { -using crypto::tink::integration::gcpkms::GcpKmsClient; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; TEST(GcpKmsClientTest, ClientNotBoundToAKey) { std::string gcp_key1 = "gcp-kms://projects/someProject/.../cryptoKeys/key1"; @@ -47,12 +45,12 @@ std::string creds_file = std::string(getenv("TEST_SRCDIR")) + "/tink_cc_gcpkms/testdata/gcp/credential.json"; - auto client_result = GcpKmsClient::New("", creds_file); - EXPECT_TRUE(client_result.ok()) << client_result.status(); - auto client = std::move(client_result.value()); - EXPECT_TRUE(client->DoesSupport(gcp_key1)); - EXPECT_TRUE(client->DoesSupport(gcp_key2)); - EXPECT_FALSE(client->DoesSupport(non_gcp_key)); + util::StatusOr<std::unique_ptr<GcpKmsClient>> client = + GcpKmsClient::New("", creds_file); + ASSERT_THAT(client, IsOk()); + EXPECT_TRUE((*client)->DoesSupport(gcp_key1)); + EXPECT_TRUE((*client)->DoesSupport(gcp_key2)); + EXPECT_FALSE((*client)->DoesSupport(non_gcp_key)); } TEST(GcpKmsClientTest, ClientBoundToASpecificKey) { @@ -62,12 +60,12 @@ std::string creds_file = std::string(getenv("TEST_SRCDIR")) + "/tink_cc_gcpkms/testdata/gcp/credential.json"; - auto client_result = GcpKmsClient::New(gcp_key1, creds_file); - EXPECT_TRUE(client_result.ok()) << client_result.status(); - auto client = std::move(client_result.value()); - EXPECT_TRUE(client->DoesSupport(gcp_key1)); - EXPECT_FALSE(client->DoesSupport(gcp_key2)); - EXPECT_FALSE(client->DoesSupport(non_gcp_key)); + util::StatusOr<std::unique_ptr<GcpKmsClient>> client = + GcpKmsClient::New(gcp_key1, creds_file); + ASSERT_THAT(client, IsOk()); + EXPECT_TRUE((*client)->DoesSupport(gcp_key1)); + EXPECT_FALSE((*client)->DoesSupport(gcp_key2)); + EXPECT_FALSE((*client)->DoesSupport(non_gcp_key)); } TEST(GcpKmsClientTest, ClientCreationAndRegistry) { @@ -75,10 +73,11 @@ std::string creds_file = absl::StrCat(getenv("TEST_SRCDIR"), "/tink_cc_gcpkms/testdata/gcp/credential.json"); - auto client_result = GcpKmsClient::RegisterNewClient(gcp_key1, creds_file); - EXPECT_THAT(client_result, IsOk()); + util::Status client_result = + GcpKmsClient::RegisterNewClient(gcp_key1, creds_file); + ASSERT_THAT(client_result, IsOk()); - auto registry_result = KmsClients::Get(gcp_key1); + util::StatusOr<const KmsClient*> registry_result = KmsClients::Get(gcp_key1); EXPECT_THAT(registry_result, IsOk()); } @@ -87,7 +86,8 @@ std::string creds_file = std::string(getenv("TEST_SRCDIR")) + "/tink_cc_gcpkms/testdata/gcp/credential.json"; - auto client_result = GcpKmsClient::RegisterNewClient(non_gcp_key, creds_file); + util::Status client_result = + GcpKmsClient::RegisterNewClient(non_gcp_key, creds_file); EXPECT_THAT(client_result, StatusIs(absl::StatusCode::kInvalidArgument)); }
diff --git a/cc/integration/gcpkms/testdata/README.md b/cc/integration/gcpkms/testdata/README.md deleted file mode 100644 index 9d362c7..0000000 --- a/cc/integration/gcpkms/testdata/README.md +++ /dev/null
@@ -1,21 +0,0 @@ -This folder contains AWS and GCP credentials that are used for testing Tink. - -# AWS - -For security reasons, all AWS credentials in this folder are invalid. If you -want to run tests that depend on them, please create your own -[AWS access keys](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html). -The credentials are required in several formats expected by different APIs. For -example, Java expects the credentials as a -[properties file](https://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingAcctOrUserCredentials.html). -In order to cover all tests across all languages you will have to replace -`aws/credentials.cred`, `aws/credentials.csv` and `aws/credentials.ini`. These -can be generated in a similar way to this -[script](https://github.com/google/tink/blob/master/kokoro/copy_credentials.sh). - -# GCP - -For security reasons, all GCP credentials in this folder are invalid. If you -want to run tests that depend on them, please create your own -[Cloud KMS key](https://cloud.google.com/kms/docs/creating-keys), and copy the -credentials to `gcp/credential.json` and the key URI to `gcp/key_name.txt`.
diff --git a/cc/integration/gcpkms/testdata/aws/README.md b/cc/integration/gcpkms/testdata/aws/README.md new file mode 100644 index 0000000..ca15b34 --- /dev/null +++ b/cc/integration/gcpkms/testdata/aws/README.md
@@ -0,0 +1,16 @@ +This folder contains AWS credentials that are used for testing Tink. + +For security reasons, all credentials in this folder are invalid. If you want to +run tests that depend on them, please create your own [AWS access +keys][aws-access-keys]. + +The credentials are required in several formats expected by different APIs. For +example, Java expects the credentials as a [properties file][properties-file]. +In order to cover all tests across all languages you have to replace +`aws/credentials.cred`, `aws/credentials.csv` and `aws/credentials.ini`. These +can be generated in a similar way to this [credential copying +script][copy-credentials-script]. + +[aws-access-keys]: https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html +[properties-file]: https://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingAcctOrUserCredentials.html +[copy-credentials-script]: https://github.com/google/tink/blob/master/kokoro/copy_credentials.sh
diff --git a/cc/integration/gcpkms/testdata/gcp/README.md b/cc/integration/gcpkms/testdata/gcp/README.md new file mode 100644 index 0000000..81b5de0 --- /dev/null +++ b/cc/integration/gcpkms/testdata/gcp/README.md
@@ -0,0 +1,7 @@ +This folder contains GCP credentials that are used for testing Tink. + +For security reasons, all credentials in this folder are invalid. + +If you want to run tests that depend on them, please create your own +[Cloud KMS key](https://cloud.google.com/kms/docs/creating-keys), and copy the +credentials to `gcp/credential.json` and the key URI to `gcp/key_name.txt`.
diff --git a/cc/integration/gcpkms/tink_cc_gcpkms_deps.bzl b/cc/integration/gcpkms/tink_cc_gcpkms_deps.bzl index c6ab778..8cfb30d 100644 --- a/cc/integration/gcpkms/tink_cc_gcpkms_deps.bzl +++ b/cc/integration/gcpkms/tink_cc_gcpkms_deps.bzl
@@ -27,25 +27,25 @@ # gRPC needs rules_apple, which in turn needs rules_swift and apple_support. if not native.existing_rule("build_bazel_rules_apple"): - # Release from 2022-05-02. + # Release from 2022-09-16. http_archive( name = "build_bazel_rules_apple", - sha256 = "12865e5944f09d16364aa78050366aca9dc35a32a018fa35f5950238b08bf744", - url = "https://github.com/bazelbuild/rules_apple/releases/download/0.34.2/rules_apple.0.34.2.tar.gz", + sha256 = "90e3b5e8ff942be134e64a83499974203ea64797fd620eddeb71b3a8e1bff681", + url = "https://github.com/bazelbuild/rules_apple/releases/download/1.1.2/rules_apple.1.1.2.tar.gz", ) if not native.existing_rule("build_bazel_rules_swift"): - # Release from 2022-03-23. + # Release from 2022-09-16. http_archive( name = "build_bazel_rules_swift", - sha256 = "a2fd565e527f83fb3f9eb07eb9737240e668c9242d3bc318712efa54a7deda97", - url = "https://github.com/bazelbuild/rules_swift/releases/download/0.27.0/rules_swift.0.27.0.tar.gz", + sha256 = "51efdaf85e04e51174de76ef563f255451d5a5cd24c61ad902feeadafc7046d9", + url = "https://github.com/bazelbuild/rules_swift/releases/download/1.2.0/rules_swift.1.2.0.tar.gz", ) if not native.existing_rule("build_bazel_apple_support"): - # Release from 2022-02-03. + # Release from 2022-10-31. http_archive( name = "build_bazel_apple_support", - sha256 = "5bbce1b2b9a3d4b03c0697687023ef5471578e76f994363c641c5f50ff0c7268", - url = "https://github.com/bazelbuild/apple_support/releases/download/0.13.0/apple_support.0.13.0.tar.gz", + sha256 = "2e3dc4d0000e8c2f5782ea7bb53162f37c485b5d8dc62bb3d7d7fc7c276f0d00", + url = "https://github.com/bazelbuild/apple_support/releases/download/1.3.2/apple_support.1.3.2.tar.gz", ) if not native.existing_rule("com_google_googleapis"): @@ -57,7 +57,7 @@ url = "https://github.com/googleapis/googleapis/archive/2f9af297c84c55c8b871ba4495e01ade42476c92.tar.gz", ) - if "upb" not in native.existing_rules(): + if not native.existing_rule("upb"): # Matches version embedded in com_github_grpc_grpc from 2022-05-11. http_archive( name = "upb", @@ -66,7 +66,7 @@ url = "https://github.com/protocolbuffers/upb/archive/bef53686ec702607971bd3ea4d4fefd80c6cc6e8.tar.gz", ) - if "envoy_api" not in native.existing_rules(): + if not native.existing_rule("envoy_api"): # Matches version embedded in com_github_grpc_grpc from 2022-05-11. http_archive( name = "envoy_api", @@ -75,7 +75,7 @@ url = "https://github.com/envoyproxy/data-plane-api/archive/9c42588c956220b48eb3099d186487c2f04d32ec.tar.gz", ) - if "com_envoyproxy_protoc_gen_validate" not in native.existing_rules(): + if not native.existing_rule("com_envoyproxy_protoc_gen_validate"): # Matches version embedded in com_github_grpc_grpc from 2022-05-11. http_archive( name = "com_envoyproxy_protoc_gen_validate", @@ -88,7 +88,7 @@ patch_args = ["-p1"], ) - if "bazel_gazelle" not in native.existing_rules(): + if not native.existing_rule("bazel_gazelle"): # Matches version embedded in com_github_grpc_grpc from 2022-05-11. http_archive( name = "bazel_gazelle", @@ -117,23 +117,3 @@ strip_prefix = "grpc-java-1.46.0", url = "https://github.com/grpc/grpc-java/archive/v1.46.0.tar.gz", ) - - if not native.existing_rule("curl"): - # Release from 2016-05-30. - http_archive( - name = "curl", - url = "https://mirror.bazel.build/curl.haxx.se/download/curl-7.49.1.tar.gz", - sha256 = "ff3e80c1ca6a068428726cd7dd19037a47cc538ce58ef61c59587191039b2ca6", - strip_prefix = "curl-7.49.1", - build_file = "@tink_cc_awskms//:third_party/curl.BUILD.bazel", - ) - - if not native.existing_rule("zlib"): - # Releaes from 2022-03-27. - http_archive( - name = "zlib", - url = "https://mirror.bazel.build/zlib.net/zlib-1.2.12.tar.gz", - sha256 = "91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9", - strip_prefix = "zlib-1.2.12", - build_file = "@tink_cc_awskms//:third_party/zlib.BUILD.bazel", - )
diff --git a/cc/internal/BUILD.bazel b/cc/internal/BUILD.bazel index 84932a9..d573b1d 100644 --- a/cc/internal/BUILD.bazel +++ b/cc/internal/BUILD.bazel
@@ -25,18 +25,30 @@ srcs = ["util.cc"], hdrs = ["util.h"], include_prefix = "tink/internal", - deps = ["@com_google_absl//absl/strings"], + deps = [ + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/log", + "@com_google_absl//absl/strings", + ], ) cc_library( name = "test_file_util", testonly = 1, - srcs = ["test_file_util_bazel.cc"], + srcs = [ + "test_file_util.cc", + "test_file_util_bazel.cc", + ], hdrs = ["test_file_util.h"], include_prefix = "tink/internal", deps = [ + "//subtle:random", + "//util:status", + "//util:test_util", "@bazel_tools//tools/cpp/runfiles", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", ], ) @@ -50,6 +62,7 @@ "//:primitive_set", "//:primitive_wrapper", "//proto:tink_cc_proto", + "//util:status", "//util:statusor", "//util:validation", "@com_google_absl//absl/container:flat_hash_map", @@ -78,30 +91,26 @@ include_prefix = "tink/internal", deps = [ ":fips_utils", + ":key_type_info_store", ":keyset_wrapper", - ":keyset_wrapper_impl", - "//:catalogue", - "//:core/key_manager_impl", + ":keyset_wrapper_store", "//:core/key_type_manager", - "//:core/private_key_manager_impl", "//:core/private_key_type_manager", + "//:input_stream", "//:key_manager", "//:primitive_set", "//:primitive_wrapper", "//monitoring", "//proto:tink_cc_proto", "//util:errors", - "//util:protobuf_helper", "//util:status", "//util:statusor", - "//util:validation", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/types:optional", ], ) @@ -148,6 +157,8 @@ "//util:status", "//util:statusor", "@boringssl//:crypto", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", ], ) @@ -160,6 +171,7 @@ deps = [ ":bn_util", ":err_util", + ":fips_utils", ":ssl_unique_ptr", ":ssl_util", "//config:tink_fips", @@ -170,6 +182,7 @@ "//util:statusor", "@boringssl//:crypto", "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", ], ) @@ -234,6 +247,7 @@ "//:primitive_set", "//:primitive_wrapper", "//proto:tink_cc_proto", + "//util:status", "//util:statusor", "//util:test_matchers", "//util:test_util", @@ -252,6 +266,7 @@ ":key_info", "//proto:tink_cc_proto", "@com_google_googletest//:gtest_main", + "@com_google_protobuf//:protobuf", ], ) @@ -261,38 +276,46 @@ srcs = ["registry_impl_test.cc"], tags = ["fips"], deps = [ + ":fips_utils", + ":registry_impl", "//:aead", - "//:catalogue", "//:core/key_manager_impl", "//:core/key_type_manager", - "//:crypto_format", - "//:keyset_manager", + "//:core/private_key_manager_impl", + "//:core/private_key_type_manager", + "//:core/template_util", + "//:hybrid_decrypt", + "//:input_stream", + "//:key_manager", + "//:mac", + "//:primitive_set", + "//:primitive_wrapper", "//:registry", "//aead:aead_wrapper", "//aead:aes_gcm_key_manager", - "//config:tink_fips", "//hybrid:ecies_aead_hkdf_private_key_manager", "//hybrid:ecies_aead_hkdf_public_key_manager", - "//monitoring", "//monitoring:monitoring_client_mocks", "//proto:aes_ctr_hmac_aead_cc_proto", "//proto:aes_gcm_cc_proto", "//proto:common_cc_proto", "//proto:ecdsa_cc_proto", + "//proto:ecies_aead_hkdf_cc_proto", "//proto:tink_cc_proto", "//subtle:aes_gcm_boringssl", "//subtle:random", + "//util:input_stream_util", "//util:istream_input_stream", "//util:protobuf_helper", "//util:secret_data", "//util:status", "//util:statusor", - "//util:test_keyset_handle", "//util:test_matchers", "//util:test_util", "@boringssl//:crypto", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -333,27 +356,30 @@ ":bn_util", ":ssl_unique_ptr", "//util:secret_data", + "//util:status", "//util:statusor", "//util:test_matchers", "@boringssl//:crypto", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", "@com_google_googletest//:gtest_main", ], ) cc_test( name = "rsa_util_test", - size = "large", srcs = ["rsa_util_test.cc"], deps = [ ":bn_util", ":rsa_util", ":ssl_unique_ptr", "//subtle:random", + "//util:secret_data", "//util:status", "//util:statusor", "//util:test_matchers", "@boringssl//:crypto", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -361,12 +387,11 @@ cc_test( name = "ec_util_test", - size = "large", srcs = ["ec_util_test.cc"], data = [ - "@wycheproof//testvectors:ecdh", - "@wycheproof//testvectors:ecdsa_webcrypto", - "@wycheproof//testvectors:eddsa", + "//testvectors:ecdh", + "//testvectors:ecdsa_webcrypto", + "//testvectors:eddsa", ], deps = [ ":bn_util", @@ -378,12 +403,15 @@ "//subtle:subtle_util", "//subtle:wycheproof_util", "//util:secret_data", + "//util:status", "//util:statusor", "//util:test_matchers", "@boringssl//:crypto", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", "@com_google_googletest//:gtest_main", + "@rapidjson", ], ) @@ -394,11 +422,11 @@ deps = [ ":md_util", "//subtle:common_enums", + "//util:status", "//util:statusor", "//util:test_matchers", "@boringssl//:crypto", "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:span", "@com_google_googletest//:gtest_main", ], ) @@ -428,6 +456,7 @@ ":aes_util", "//subtle:subtle_util", "//util:secret_data", + "//util:status", "//util:statusor", "//util:test_matchers", "@boringssl//:crypto", @@ -443,12 +472,16 @@ hdrs = ["monitoring_util.h"], include_prefix = "tink/internal", deps = [ + ":key_status_util", + "//:key_status", "//:primitive_set", "//monitoring", "//proto:tink_cc_proto", + "//util:status", "//util:statusor", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", ], ) @@ -457,12 +490,718 @@ srcs = ["monitoring_util_test.cc"], deps = [ ":monitoring_util", + "//:key_status", "//:primitive_set", "//monitoring", "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", "//util:test_matchers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "serialization", + hdrs = ["serialization.h"], + include_prefix = "tink/internal", + deps = ["@com_google_absl//absl/strings"], +) + +cc_library( + name = "proto_parameters_serialization", + srcs = ["proto_parameters_serialization.cc"], + hdrs = ["proto_parameters_serialization.h"], + include_prefix = "tink/internal", + deps = [ + ":serialization", + ":util", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "proto_parameters_serialization_test", + srcs = ["proto_parameters_serialization_test.cc"], + deps = [ + ":proto_parameters_serialization", + "//proto:test_proto_cc_proto", + "//proto:tink_cc_proto", + "//util:statusor", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "proto_key_serialization", + srcs = ["proto_key_serialization.cc"], + hdrs = ["proto_key_serialization.h"], + include_prefix = "tink/internal", + deps = [ + ":serialization", + ":util", + "//:restricted_data", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + ], +) + +cc_test( + name = "proto_key_serialization_test", + srcs = ["proto_key_serialization_test.cc"], + deps = [ + ":proto_key_serialization", + "//:insecure_secret_key_access", + "//:restricted_data", + "//proto:tink_cc_proto", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "legacy_proto_parameters", + srcs = ["legacy_proto_parameters.cc"], + hdrs = ["legacy_proto_parameters.h"], + include_prefix = "tink/internal", + deps = [ + ":proto_parameters_serialization", + "//:parameters", + "//proto:tink_cc_proto", + ], +) + +cc_test( + name = "legacy_proto_parameters_test", + srcs = ["legacy_proto_parameters_test.cc"], + deps = [ + ":legacy_proto_parameters", + ":proto_parameters_serialization", + "//:parameters", + "//proto:test_proto_cc_proto", + "//proto:tink_cc_proto", + "//util:statusor", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "legacy_proto_key", + srcs = ["legacy_proto_key.cc"], + hdrs = ["legacy_proto_key.h"], + include_prefix = "tink/internal", + deps = [ + ":proto_key_serialization", + "//:key", + "//:parameters", + "//:secret_key_access_token", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + ], +) + +cc_test( + name = "legacy_proto_key_test", + srcs = ["legacy_proto_key_test.cc"], + deps = [ + ":legacy_proto_key", + ":proto_key_serialization", + "//:insecure_secret_key_access", + "//:key", + "//:parameters", + "//:restricted_data", + "//:secret_key_access_token", + "//proto:tink_cc_proto", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "parser_index", + hdrs = ["parser_index.h"], + include_prefix = "tink/internal", + deps = [ + ":serialization", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "parser_index_test", + srcs = ["parser_index_test.cc"], + deps = [ + ":parser_index", + ":serialization", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "serializer_index", + hdrs = ["serializer_index.h"], + include_prefix = "tink/internal", + deps = [ + ":serialization", + "//:key", + "//:parameters", + ], +) + +cc_test( + name = "serializer_index_test", + srcs = ["serializer_index_test.cc"], + deps = [ + ":serialization_test_util", + ":serializer_index", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "parameters_parser", + hdrs = ["parameters_parser.h"], + include_prefix = "tink/internal", + deps = [ + ":parser_index", + ":serialization", + "//:parameters", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "parameters_parser_test", + srcs = ["parameters_parser_test.cc"], + deps = [ + ":parameters_parser", + ":parser_index", + ":serialization", + ":serialization_test_util", + "//:parameters", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "parameters_serializer", + hdrs = ["parameters_serializer.h"], + include_prefix = "tink/internal", + deps = [ + ":serialization", + ":serializer_index", + "//:parameters", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "parameters_serializer_test", + srcs = ["parameters_serializer_test.cc"], + deps = [ + ":parameters_serializer", + ":serialization", + ":serialization_test_util", + ":serializer_index", + "//:parameters", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "key_parser", + hdrs = ["key_parser.h"], + include_prefix = "tink/internal", + deps = [ + ":parser_index", + ":serialization", + "//:key", + "//:secret_key_access_token", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/functional:function_ref", + "@com_google_absl//absl/log", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + ], +) + +cc_test( + name = "key_parser_test", + srcs = ["key_parser_test.cc"], + deps = [ + ":key_parser", + ":parser_index", + ":serialization", + ":serialization_test_util", + "//:insecure_secret_key_access", + "//:key", + "//:parameters", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "key_serializer", + hdrs = ["key_serializer.h"], + include_prefix = "tink/internal", + deps = [ + ":serialization", + ":serializer_index", + "//:key", + "//:secret_key_access_token", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/functional:function_ref", + "@com_google_absl//absl/log", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + ], +) + +cc_test( + name = "key_serializer_test", + srcs = ["key_serializer_test.cc"], + deps = [ + ":key_serializer", + ":serialization", + ":serialization_test_util", + ":serializer_index", + "//:insecure_secret_key_access", + "//:key", + "//:secret_key_access_token", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "key_status_util", + srcs = ["key_status_util.cc"], + hdrs = ["key_status_util.h"], + include_prefix = "tink/internal", + deps = [ + "//:key_status", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + ], +) + +cc_test( + name = "key_status_util_test", + srcs = ["key_status_util_test.cc"], + deps = [ + ":key_status_util", + "//:key_status", + "//proto:tink_cc_proto", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "keyset_handle_builder_entry", + srcs = ["keyset_handle_builder_entry.cc"], + hdrs = ["keyset_handle_builder_entry.h"], + include_prefix = "tink/internal", + deps = [ + ":key_status_util", + ":legacy_proto_key", + ":legacy_proto_parameters", + ":mutable_serialization_registry", + ":proto_key_serialization", + ":proto_parameters_serialization", + ":serialization", + "//:insecure_secret_key_access", + "//:key", + "//:key_status", + "//:parameters", + "//:registry", + "//:restricted_data", + "//:secret_key_access_token", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + ], +) + +cc_test( + name = "keyset_handle_builder_entry_test", + srcs = ["keyset_handle_builder_entry_test.cc"], + deps = [ + ":keyset_handle_builder_entry", + ":legacy_proto_key", + ":legacy_proto_parameters", + ":proto_key_serialization", + ":proto_parameters_serialization", + "//:insecure_secret_key_access", + "//:key", + "//:key_status", + "//:keyset_handle", + "//:keyset_handle_builder", + "//:parameters", + "//:partial_key_access", + "//:restricted_data", + "//:secret_key_access_token", + "//config:tink_config", + "//mac:aes_cmac_key", + "//mac:aes_cmac_parameters", + "//mac:mac_key_templates", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "serialization_registry", + srcs = ["serialization_registry.cc"], + hdrs = ["serialization_registry.h"], + include_prefix = "tink/internal", + deps = [ + ":key_parser", + ":key_serializer", + ":parameters_parser", + ":parameters_serializer", + ":parser_index", + ":serialization", + ":serializer_index", + "//:key", + "//:parameters", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:optional", + ], +) + +cc_test( + name = "serialization_registry_test", + srcs = ["serialization_registry_test.cc"], + deps = [ + ":key_parser", + ":key_serializer", + ":parameters_parser", + ":parameters_serializer", + ":serialization", + ":serialization_registry", + ":serialization_test_util", + "//:insecure_secret_key_access", + "//:key", + "//:parameters", + "//:secret_key_access_token", + "//util:status", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "mutable_serialization_registry", + srcs = ["mutable_serialization_registry.cc"], + hdrs = ["mutable_serialization_registry.h"], + include_prefix = "tink/internal", + deps = [ + ":key_parser", + ":key_serializer", + ":legacy_proto_key", + ":parameters_parser", + ":parameters_serializer", + ":proto_key_serialization", + ":serialization", + ":serialization_registry", + "//:insecure_secret_key_access", + "//:key", + "//:parameters", + "//:secret_key_access_token", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/types:optional", + ], +) + +cc_test( + name = "mutable_serialization_registry_test", + srcs = ["mutable_serialization_registry_test.cc"], + deps = [ + ":key_parser", + ":key_serializer", + ":mutable_serialization_registry", + ":parameters_parser", + ":parameters_serializer", + ":proto_key_serialization", + ":serialization", + ":serialization_test_util", + "//:insecure_secret_key_access", + "//:key", + "//:parameters", + "//:restricted_data", + "//:secret_key_access_token", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "serialization_test_util", + testonly = 1, + hdrs = ["serialization_test_util.h"], + include_prefix = "tink/internal", + deps = [ + ":serialization", + "//:key", + "//:parameters", + "//:secret_key_access_token", + "//util:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + ], +) + +cc_test( + name = "serialization_test_util_test", + srcs = ["serialization_test_util_test.cc"], + deps = [ + ":serialization_test_util", + "//:insecure_secret_key_access", + "//:parameters", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "test_random_access_stream", + testonly = 1, + srcs = ["test_random_access_stream.cc"], + hdrs = ["test_random_access_stream.h"], + include_prefix = "tink/internal", + deps = [ + "//:random_access_stream", + "//util:buffer", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "test_random_access_stream_test", + srcs = ["test_random_access_stream_test.cc"], + deps = [ + ":test_random_access_stream", + "//subtle:random", + "//util:buffer", + "//util:status", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "configuration_impl", + hdrs = ["configuration_impl.h"], + include_prefix = "tink/internal", + deps = [ + ":key_type_info_store", + ":keyset_wrapper_store", + "//:configuration", + ], +) + +cc_test( + name = "configuration_impl_test", + srcs = ["configuration_impl_test.cc"], + deps = [ + ":configuration_impl", + ":keyset_wrapper_store", + "//:cleartext_keyset_handle", + "//:configuration", + "//proto:aes_gcm_cc_proto", + "//proto:rsa_ssa_pss_cc_proto", + "//subtle:random", + "//util:test_matchers", + "//util:test_util", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "key_type_info_store", + srcs = ["key_type_info_store.cc"], + hdrs = ["key_type_info_store.h"], + include_prefix = "tink/internal", + deps = [ + ":fips_utils", + "//:core/key_manager_impl", + "//:core/key_type_manager", + "//:core/private_key_manager_impl", + "//:core/private_key_type_manager", + "//:key_manager", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "key_type_info_store_test", + srcs = ["key_type_info_store_test.cc"], + deps = [ + ":fips_utils", + ":key_type_info_store", + "//:aead", + "//:core/key_manager_impl", + "//:key_manager", + "//aead:aes_gcm_key_manager", + "//aead:cord_aead", + "//aead:kms_envelope_aead_key_manager", + "//proto:aes_gcm_cc_proto", + "//proto:common_cc_proto", + "//proto:ecdsa_cc_proto", + "//signature:ecdsa_sign_key_manager", + "//signature:ecdsa_verify_key_manager", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "keyset_wrapper_store", + hdrs = ["keyset_wrapper_store.h"], + include_prefix = "tink/internal", + deps = [ + ":keyset_wrapper", + ":keyset_wrapper_impl", + "//:primitive_wrapper", + "//util:status", + "//util:statusor", + ], +) + +cc_test( + name = "keyset_wrapper_store_test", + srcs = ["keyset_wrapper_store_test.cc"], + deps = [ + ":keyset_wrapper_store", + ":registry_impl", + "//:primitive_set", + "//:primitive_wrapper", + "//mac:mac_wrapper", + "//proto:aes_gcm_cc_proto", + "//subtle:random", + "//util:status", + "//util:statusor", + "//util:test_matchers", + "//util:test_util", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "key_gen_configuration_impl", + hdrs = ["key_gen_configuration_impl.h"], + include_prefix = "tink/internal", + deps = [ + ":key_type_info_store", + "//:key_gen_configuration", + ], +) + +cc_test( + name = "key_gen_configuration_impl_test", + srcs = ["key_gen_configuration_impl_test.cc"], + deps = [ + ":key_gen_configuration_impl", + "//:key_gen_configuration", + "//aead:aead_key_templates", + "//proto:aes_gcm_cc_proto", + "//proto:rsa_ssa_pss_cc_proto", + "//util:test_matchers", + "//util:test_util", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/cc/internal/BUILD.gn b/cc/internal/BUILD.gn index 7e51e25..affe66f 100644 --- a/cc/internal/BUILD.gn +++ b/cc/internal/BUILD.gn
@@ -26,7 +26,11 @@ "util.cc", "util.h", ] - public_deps = [ "//third_party/abseil-cpp/absl/strings:strings" ] + public_deps = [ + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/log:log", + "//third_party/abseil-cpp/absl/strings:strings", + ] public_configs = [ "//third_party/tink:tink_config" ] } @@ -42,6 +46,7 @@ "//third_party/tink/cc:primitive_set", "//third_party/tink/cc:primitive_wrapper", "//third_party/tink/cc/proto:tink_proto", + "//third_party/tink/cc/util:status", "//third_party/tink/cc/util:statusor", "//third_party/tink/cc/util:validation", ] @@ -79,30 +84,26 @@ ] public_deps = [ ":fips_utils", + ":key_type_info_store", ":keyset_wrapper", - ":keyset_wrapper_impl", + ":keyset_wrapper_store", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/container:flat_hash_map", "//third_party/abseil-cpp/absl/memory:memory", "//third_party/abseil-cpp/absl/status:status", "//third_party/abseil-cpp/absl/strings:strings", "//third_party/abseil-cpp/absl/synchronization:synchronization", - "//third_party/abseil-cpp/absl/types:optional", - "//third_party/tink/cc:catalogue", - "//third_party/tink/cc:core/key_manager_impl", "//third_party/tink/cc:core/key_type_manager", - "//third_party/tink/cc:core/private_key_manager_impl", "//third_party/tink/cc:core/private_key_type_manager", + "//third_party/tink/cc:input_stream", "//third_party/tink/cc:key_manager", "//third_party/tink/cc:primitive_set", "//third_party/tink/cc:primitive_wrapper", "//third_party/tink/cc/monitoring:monitoring", "//third_party/tink/cc/proto:tink_proto", "//third_party/tink/cc/util:errors", - "//third_party/tink/cc/util:protobuf_helper", "//third_party/tink/cc/util:status", "//third_party/tink/cc/util:statusor", - "//third_party/tink/cc/util:validation", ] public_configs = [ "//third_party/tink:tink_config" ] } @@ -146,6 +147,8 @@ ] public_deps = [ ":ssl_unique_ptr", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:strings", "//third_party/abseil-cpp/absl/types:span", "//third_party/boringssl:crypto", "//third_party/tink/cc/subtle:subtle_util", @@ -233,12 +236,332 @@ configs -= [ "//build/config:no_rtti" ] sources = [ "monitoring_util.h" ] public_deps = [ + ":key_status_util", "//third_party/abseil-cpp/absl/container:flat_hash_map", "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/tink/cc:key_status", "//third_party/tink/cc:primitive_set", "//third_party/tink/cc/monitoring:monitoring", "//third_party/tink/cc/proto:tink_proto", + "//third_party/tink/cc/util:status", "//third_party/tink/cc/util:statusor", ] public_configs = [ "//third_party/tink:tink_config" ] } + +# CC Library : serialization +source_set("serialization") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "serialization.h" ] + public_deps = [ "//third_party/abseil-cpp/absl/strings:strings" ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : proto_parameters_serialization +source_set("proto_parameters_serialization") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "proto_parameters_serialization.cc", + "proto_parameters_serialization.h", + ] + public_deps = [ + ":serialization", + ":util", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/tink/cc/proto:tink_proto", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : proto_key_serialization +source_set("proto_key_serialization") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "proto_key_serialization.cc", + "proto_key_serialization.h", + ] + public_deps = [ + ":serialization", + ":util", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc:restricted_data", + "//third_party/tink/cc/proto:tink_proto", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : legacy_proto_key +source_set("legacy_proto_key") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "legacy_proto_key.cc", + "legacy_proto_key.h", + ] + public_deps = [ + ":proto_key_serialization", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc:key", + "//third_party/tink/cc:parameters", + "//third_party/tink/cc:secret_key_access_token", + "//third_party/tink/cc/proto:tink_proto", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : parser_index +source_set("parser_index") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "parser_index.h" ] + public_deps = [ + ":serialization", + "//third_party/abseil-cpp/absl/strings:strings", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : serializer_index +source_set("serializer_index") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "serializer_index.h" ] + public_deps = [ + ":serialization", + "//third_party/tink/cc:key", + "//third_party/tink/cc:parameters", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : parameters_parser +source_set("parameters_parser") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "parameters_parser.h" ] + public_deps = [ + ":parser_index", + ":serialization", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/tink/cc:parameters", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : parameters_serializer +source_set("parameters_serializer") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "parameters_serializer.h" ] + public_deps = [ + ":serialization", + ":serializer_index", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/tink/cc:parameters", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : key_parser +source_set("key_parser") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "key_parser.h" ] + public_deps = [ + ":parser_index", + ":serialization", + "//third_party/abseil-cpp/absl/functional:function_ref", + "//third_party/abseil-cpp/absl/log:log", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc:key", + "//third_party/tink/cc:secret_key_access_token", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : key_serializer +source_set("key_serializer") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "key_serializer.h" ] + public_deps = [ + ":serialization", + ":serializer_index", + "//third_party/abseil-cpp/absl/functional:function_ref", + "//third_party/abseil-cpp/absl/log:log", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc:key", + "//third_party/tink/cc:secret_key_access_token", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : key_status_util +source_set("key_status_util") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "key_status_util.cc", + "key_status_util.h", + ] + public_deps = [ + "//third_party/abseil-cpp/absl/status:status", + "//third_party/tink/cc:key_status", + "//third_party/tink/cc/proto:tink_proto", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : serialization_registry +source_set("serialization_registry") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "serialization_registry.cc", + "serialization_registry.h", + ] + public_deps = [ + ":key_parser", + ":key_serializer", + ":parameters_parser", + ":parameters_serializer", + ":parser_index", + ":serialization", + ":serializer_index", + "//third_party/abseil-cpp/absl/container:flat_hash_map", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:str_format", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc:key", + "//third_party/tink/cc:parameters", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : mutable_serialization_registry +source_set("mutable_serialization_registry") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "mutable_serialization_registry.cc", + "mutable_serialization_registry.h", + ] + public_deps = [ + ":key_parser", + ":key_serializer", + ":legacy_proto_key", + ":parameters_parser", + ":parameters_serializer", + ":proto_key_serialization", + ":serialization", + ":serialization_registry", + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/memory:memory", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/synchronization:synchronization", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc:insecure_secret_key_access", + "//third_party/tink/cc:key", + "//third_party/tink/cc:parameters", + "//third_party/tink/cc:secret_key_access_token", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : configuration_impl +source_set("configuration_impl") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "configuration_impl.h" ] + public_deps = [ + ":key_type_info_store", + ":keyset_wrapper_store", + "//third_party/tink/cc:configuration", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : key_type_info_store +source_set("key_type_info_store") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "key_type_info_store.cc", + "key_type_info_store.h", + ] + public_deps = [ + ":fips_utils", + "//third_party/abseil-cpp/absl/container:flat_hash_map", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/tink/cc:core/key_manager_impl", + "//third_party/tink/cc:core/key_type_manager", + "//third_party/tink/cc:core/private_key_manager_impl", + "//third_party/tink/cc:core/private_key_type_manager", + "//third_party/tink/cc:key_manager", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : keyset_wrapper_store +source_set("keyset_wrapper_store") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "keyset_wrapper_store.h" ] + public_deps = [ + ":keyset_wrapper", + ":keyset_wrapper_impl", + "//third_party/tink/cc:primitive_wrapper", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : key_gen_configuration_impl +source_set("key_gen_configuration_impl") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "key_gen_configuration_impl.h" ] + public_deps = [ + ":key_type_info_store", + "//third_party/tink/cc:key_gen_configuration", + ] + public_configs = [ "//third_party/tink:tink_config" ] +}
diff --git a/cc/internal/CMakeLists.txt b/cc/internal/CMakeLists.txt index 20e4305..82d2499 100644 --- a/cc/internal/CMakeLists.txt +++ b/cc/internal/CMakeLists.txt
@@ -24,16 +24,25 @@ util.cc util.h DEPS + absl::core_headers + absl::log absl::strings ) tink_cc_library( NAME test_file_util SRCS + test_file_util.cc test_file_util_cmake.cc test_file_util.h DEPS + absl::check absl::strings + gmock + tink::subtle::random + tink::util::status + tink::util::test_util + TESTONLY ) tink_cc_library( @@ -46,6 +55,7 @@ absl::flat_hash_map tink::core::primitive_set tink::core::primitive_wrapper + tink::util::status tink::util::statusor tink::util::validation tink::proto::tink_cc_proto @@ -84,29 +94,25 @@ registry_impl.h DEPS tink::internal::fips_utils + tink::internal::key_type_info_store tink::internal::keyset_wrapper - tink::internal::keyset_wrapper_impl + tink::internal::keyset_wrapper_store absl::core_headers absl::flat_hash_map absl::memory absl::status absl::strings absl::synchronization - absl::optional - tink::core::catalogue - tink::core::key_manager_impl tink::core::key_type_manager - tink::core::private_key_manager_impl tink::core::private_key_type_manager + tink::core::input_stream tink::core::key_manager tink::core::primitive_set tink::core::primitive_wrapper tink::monitoring::monitoring tink::util::errors - tink::util::protobuf_helper tink::util::status tink::util::statusor - tink::util::validation tink::proto::tink_cc_proto ) @@ -152,6 +158,8 @@ bn_util.h DEPS tink::internal::ssl_unique_ptr + absl::status + absl::strings absl::span crypto tink::subtle::subtle_util @@ -168,9 +176,11 @@ DEPS tink::internal::bn_util tink::internal::err_util + tink::internal::fips_utils tink::internal::ssl_unique_ptr tink::internal::ssl_util absl::status + absl::statusor absl::strings crypto tink::config::tink_fips @@ -204,6 +214,7 @@ absl::strings tink::core::primitive_set tink::core::primitive_wrapper + tink::util::status tink::util::statusor tink::util::test_matchers tink::util::test_util @@ -217,6 +228,7 @@ DEPS tink::internal::key_info gmock + protobuf::libprotobuf-lite tink::proto::tink_cc_proto ) @@ -225,39 +237,47 @@ SRCS registry_impl_test.cc DEPS + tink::internal::fips_utils + tink::internal::registry_impl gmock absl::memory absl::status + absl::statusor absl::strings crypto tink::core::aead - tink::core::catalogue tink::core::key_manager_impl tink::core::key_type_manager - tink::core::crypto_format - tink::core::keyset_manager + tink::core::private_key_manager_impl + tink::core::private_key_type_manager + tink::core::template_util + tink::core::hybrid_decrypt + tink::core::input_stream + tink::core::key_manager + tink::core::mac + tink::core::primitive_set + tink::core::primitive_wrapper tink::core::registry tink::aead::aead_wrapper tink::aead::aes_gcm_key_manager - tink::config::tink_fips tink::hybrid::ecies_aead_hkdf_private_key_manager tink::hybrid::ecies_aead_hkdf_public_key_manager - tink::monitoring::monitoring tink::monitoring::monitoring_client_mocks tink::subtle::aes_gcm_boringssl tink::subtle::random + tink::util::input_stream_util tink::util::istream_input_stream tink::util::protobuf_helper tink::util::secret_data tink::util::status tink::util::statusor - tink::util::test_keyset_handle tink::util::test_matchers tink::util::test_util tink::proto::aes_ctr_hmac_aead_cc_proto tink::proto::aes_gcm_cc_proto tink::proto::common_cc_proto tink::proto::ecdsa_cc_proto + tink::proto::ecies_aead_hkdf_cc_proto tink::proto::tink_cc_proto ) @@ -281,8 +301,10 @@ tink::internal::ssl_unique_ptr gmock absl::strings + absl::span crypto tink::util::secret_data + tink::util::status tink::util::statusor tink::util::test_matchers ) @@ -296,9 +318,11 @@ tink::internal::rsa_util tink::internal::ssl_unique_ptr gmock + absl::status absl::strings crypto tink::subtle::random + tink::util::secret_data tink::util::status tink::util::statusor tink::util::test_matchers @@ -330,13 +354,16 @@ tink::internal::ssl_unique_ptr tink::internal::ssl_util gmock + absl::status absl::strings absl::span crypto + rapidjson tink::subtle::common_enums tink::subtle::subtle_util tink::subtle::wycheproof_util tink::util::secret_data + tink::util::status tink::util::statusor tink::util::test_matchers ) @@ -366,9 +393,9 @@ tink::internal::md_util gmock absl::strings - absl::span crypto tink::subtle::common_enums + tink::util::status tink::util::statusor tink::util::test_matchers ) @@ -402,6 +429,7 @@ crypto tink::subtle::subtle_util tink::util::secret_data + tink::util::status tink::util::statusor tink::util::test_matchers ) @@ -411,10 +439,14 @@ SRCS monitoring_util.h DEPS + tink::internal::key_status_util absl::flat_hash_map absl::status + absl::strings + tink::core::key_status tink::core::primitive_set tink::monitoring::monitoring + tink::util::status tink::util::statusor tink::proto::tink_cc_proto ) @@ -426,10 +458,697 @@ DEPS tink::internal::monitoring_util gmock + absl::flat_hash_map + absl::memory absl::status absl::strings + tink::core::key_status tink::core::primitive_set tink::monitoring::monitoring + tink::util::status + tink::util::statusor tink::util::test_matchers tink::proto::tink_cc_proto ) + +tink_cc_library( + NAME serialization + SRCS + serialization.h + DEPS + absl::strings +) + +tink_cc_library( + NAME proto_parameters_serialization + SRCS + proto_parameters_serialization.cc + proto_parameters_serialization.h + DEPS + tink::internal::serialization + tink::internal::util + absl::status + absl::strings + tink::util::status + tink::util::statusor + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME proto_parameters_serialization_test + SRCS + proto_parameters_serialization_test.cc + DEPS + tink::internal::proto_parameters_serialization + gmock + tink::util::statusor + tink::util::test_matchers + tink::proto::test_proto_cc_proto + tink::proto::tink_cc_proto +) + +tink_cc_library( + NAME proto_key_serialization + SRCS + proto_key_serialization.cc + proto_key_serialization.h + DEPS + tink::internal::serialization + tink::internal::util + absl::status + absl::strings + absl::optional + tink::core::restricted_data + tink::util::status + tink::util::statusor + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME proto_key_serialization_test + SRCS + proto_key_serialization_test.cc + DEPS + tink::internal::proto_key_serialization + gmock + absl::status + absl::optional + tink::core::insecure_secret_key_access + tink::core::restricted_data + tink::util::statusor + tink::util::test_matchers + tink::proto::tink_cc_proto +) + +tink_cc_library( + NAME legacy_proto_parameters + SRCS + legacy_proto_parameters.cc + legacy_proto_parameters.h + DEPS + tink::internal::proto_parameters_serialization + tink::core::parameters + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME legacy_proto_parameters_test + SRCS + legacy_proto_parameters_test.cc + DEPS + tink::internal::legacy_proto_parameters + tink::internal::proto_parameters_serialization + gmock + tink::core::parameters + tink::util::statusor + tink::util::test_matchers + tink::proto::test_proto_cc_proto + tink::proto::tink_cc_proto +) + +tink_cc_library( + NAME legacy_proto_key + SRCS + legacy_proto_key.cc + legacy_proto_key.h + DEPS + tink::internal::proto_key_serialization + absl::status + absl::strings + absl::optional + tink::core::key + tink::core::parameters + tink::core::secret_key_access_token + tink::util::status + tink::util::statusor + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME legacy_proto_key_test + SRCS + legacy_proto_key_test.cc + DEPS + tink::internal::legacy_proto_key + tink::internal::proto_key_serialization + gmock + absl::status + absl::optional + tink::core::insecure_secret_key_access + tink::core::key + tink::core::parameters + tink::core::restricted_data + tink::core::secret_key_access_token + tink::util::statusor + tink::util::test_matchers + tink::proto::tink_cc_proto +) + +tink_cc_library( + NAME parser_index + SRCS + parser_index.h + DEPS + tink::internal::serialization + absl::strings +) + +tink_cc_test( + NAME parser_index_test + SRCS + parser_index_test.cc + DEPS + tink::internal::parser_index + tink::internal::serialization + gmock + absl::strings +) + +tink_cc_library( + NAME serializer_index + SRCS + serializer_index.h + DEPS + tink::internal::serialization + tink::core::key + tink::core::parameters +) + +tink_cc_test( + NAME serializer_index_test + SRCS + serializer_index_test.cc + DEPS + tink::internal::serialization_test_util + tink::internal::serializer_index + gmock +) + +tink_cc_library( + NAME parameters_parser + SRCS + parameters_parser.h + DEPS + tink::internal::parser_index + tink::internal::serialization + absl::status + absl::strings + tink::core::parameters + tink::util::status + tink::util::statusor +) + +tink_cc_test( + NAME parameters_parser_test + SRCS + parameters_parser_test.cc + DEPS + tink::internal::parameters_parser + tink::internal::parser_index + tink::internal::serialization + tink::internal::serialization_test_util + gmock + absl::memory + absl::status + tink::core::parameters + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_library( + NAME parameters_serializer + SRCS + parameters_serializer.h + DEPS + tink::internal::serialization + tink::internal::serializer_index + absl::status + absl::strings + tink::core::parameters + tink::util::status + tink::util::statusor +) + +tink_cc_test( + NAME parameters_serializer_test + SRCS + parameters_serializer_test.cc + DEPS + tink::internal::parameters_serializer + tink::internal::serialization + tink::internal::serialization_test_util + tink::internal::serializer_index + gmock + absl::memory + absl::status + tink::core::parameters + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_library( + NAME key_parser + SRCS + key_parser.h + DEPS + tink::internal::parser_index + tink::internal::serialization + absl::function_ref + absl::log + absl::status + absl::strings + absl::optional + tink::core::key + tink::core::secret_key_access_token + tink::util::status + tink::util::statusor +) + +tink_cc_test( + NAME key_parser_test + SRCS + key_parser_test.cc + DEPS + tink::internal::key_parser + tink::internal::parser_index + tink::internal::serialization + tink::internal::serialization_test_util + gmock + absl::memory + absl::status + absl::optional + tink::core::insecure_secret_key_access + tink::core::key + tink::core::parameters + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_library( + NAME key_serializer + SRCS + key_serializer.h + DEPS + tink::internal::serialization + tink::internal::serializer_index + absl::function_ref + absl::log + absl::status + absl::optional + tink::core::key + tink::core::secret_key_access_token + tink::util::status + tink::util::statusor +) + +tink_cc_test( + NAME key_serializer_test + SRCS + key_serializer_test.cc + DEPS + tink::internal::key_serializer + tink::internal::serialization + tink::internal::serialization_test_util + tink::internal::serializer_index + gmock + absl::memory + absl::status + tink::core::insecure_secret_key_access + tink::core::key + tink::core::secret_key_access_token + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_library( + NAME key_status_util + SRCS + key_status_util.cc + key_status_util.h + DEPS + absl::status + tink::core::key_status + tink::util::status + tink::util::statusor + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME key_status_util_test + SRCS + key_status_util_test.cc + DEPS + tink::internal::key_status_util + gmock + absl::status + tink::core::key_status + tink::util::statusor + tink::util::test_matchers + tink::proto::tink_cc_proto +) + +tink_cc_library( + NAME keyset_handle_builder_entry + SRCS + keyset_handle_builder_entry.cc + keyset_handle_builder_entry.h + DEPS + tink::internal::key_status_util + tink::internal::legacy_proto_key + tink::internal::legacy_proto_parameters + tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::internal::serialization + absl::status + absl::strings + absl::optional + tink::core::insecure_secret_key_access + tink::core::key + tink::core::key_status + tink::core::parameters + tink::core::registry + tink::core::restricted_data + tink::core::secret_key_access_token + tink::util::status + tink::util::statusor + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME keyset_handle_builder_entry_test + SRCS + keyset_handle_builder_entry_test.cc + DEPS + tink::internal::keyset_handle_builder_entry + tink::internal::legacy_proto_key + tink::internal::legacy_proto_parameters + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + gmock + absl::memory + absl::status + absl::optional + tink::core::insecure_secret_key_access + tink::core::key + tink::core::key_status + tink::core::keyset_handle + tink::core::keyset_handle_builder + tink::core::parameters + tink::core::partial_key_access + tink::core::restricted_data + tink::core::secret_key_access_token + tink::config::tink_config + tink::mac::aes_cmac_key + tink::mac::aes_cmac_parameters + tink::mac::mac_key_templates + tink::util::status + tink::util::statusor + tink::util::test_matchers + tink::proto::tink_cc_proto +) + +tink_cc_library( + NAME serialization_registry + SRCS + serialization_registry.cc + serialization_registry.h + DEPS + tink::internal::key_parser + tink::internal::key_serializer + tink::internal::parameters_parser + tink::internal::parameters_serializer + tink::internal::parser_index + tink::internal::serialization + tink::internal::serializer_index + absl::flat_hash_map + absl::status + absl::str_format + absl::optional + tink::core::key + tink::core::parameters + tink::util::status + tink::util::statusor +) + +tink_cc_test( + NAME serialization_registry_test + SRCS + serialization_registry_test.cc + DEPS + tink::internal::key_parser + tink::internal::key_serializer + tink::internal::parameters_parser + tink::internal::parameters_serializer + tink::internal::serialization + tink::internal::serialization_registry + tink::internal::serialization_test_util + gmock + absl::status + tink::core::insecure_secret_key_access + tink::core::key + tink::core::parameters + tink::core::secret_key_access_token + tink::util::status + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_library( + NAME mutable_serialization_registry + SRCS + mutable_serialization_registry.cc + mutable_serialization_registry.h + DEPS + tink::internal::key_parser + tink::internal::key_serializer + tink::internal::legacy_proto_key + tink::internal::parameters_parser + tink::internal::parameters_serializer + tink::internal::proto_key_serialization + tink::internal::serialization + tink::internal::serialization_registry + absl::core_headers + absl::memory + absl::status + absl::synchronization + absl::optional + tink::core::insecure_secret_key_access + tink::core::key + tink::core::parameters + tink::core::secret_key_access_token + tink::util::status + tink::util::statusor +) + +tink_cc_test( + NAME mutable_serialization_registry_test + SRCS + mutable_serialization_registry_test.cc + DEPS + tink::internal::key_parser + tink::internal::key_serializer + tink::internal::mutable_serialization_registry + tink::internal::parameters_parser + tink::internal::parameters_serializer + tink::internal::proto_key_serialization + tink::internal::serialization + tink::internal::serialization_test_util + gmock + absl::status + absl::optional + tink::core::insecure_secret_key_access + tink::core::key + tink::core::parameters + tink::core::restricted_data + tink::core::secret_key_access_token + tink::util::status + tink::util::statusor + tink::util::test_matchers + tink::proto::tink_cc_proto +) + +tink_cc_library( + NAME serialization_test_util + SRCS + serialization_test_util.h + DEPS + tink::internal::serialization + absl::strings + absl::optional + tink::core::key + tink::core::parameters + tink::core::secret_key_access_token + tink::util::statusor + TESTONLY +) + +tink_cc_test( + NAME serialization_test_util_test + SRCS + serialization_test_util_test.cc + DEPS + tink::internal::serialization_test_util + gmock + absl::optional + tink::core::insecure_secret_key_access + tink::core::parameters + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_library( + NAME test_random_access_stream + SRCS + test_random_access_stream.cc + test_random_access_stream.h + DEPS + absl::status + absl::strings + tink::core::random_access_stream + tink::util::buffer + tink::util::status + tink::util::statusor + TESTONLY +) + +tink_cc_test( + NAME test_random_access_stream_test + SRCS + test_random_access_stream_test.cc + DEPS + tink::internal::test_random_access_stream + gmock + absl::status + tink::subtle::random + tink::util::buffer + tink::util::status + tink::util::test_matchers +) + +tink_cc_library( + NAME configuration_impl + SRCS + configuration_impl.h + DEPS + tink::internal::key_type_info_store + tink::internal::keyset_wrapper_store + tink::core::configuration +) + +tink_cc_test( + NAME configuration_impl_test + SRCS + configuration_impl_test.cc + DEPS + tink::internal::configuration_impl + tink::internal::keyset_wrapper_store + gmock + absl::status + tink::core::cleartext_keyset_handle + tink::core::configuration + tink::subtle::random + tink::util::test_matchers + tink::util::test_util + tink::proto::aes_gcm_cc_proto + tink::proto::rsa_ssa_pss_cc_proto +) + +tink_cc_library( + NAME key_type_info_store + SRCS + key_type_info_store.cc + key_type_info_store.h + DEPS + tink::internal::fips_utils + absl::flat_hash_map + absl::status + absl::strings + tink::core::key_manager_impl + tink::core::key_type_manager + tink::core::private_key_manager_impl + tink::core::private_key_type_manager + tink::core::key_manager + tink::util::status + tink::util::statusor +) + +tink_cc_test( + NAME key_type_info_store_test + SRCS + key_type_info_store_test.cc + DEPS + tink::internal::fips_utils + tink::internal::key_type_info_store + gmock + absl::status + absl::optional + tink::core::aead + tink::core::key_manager_impl + tink::core::key_manager + tink::aead::aes_gcm_key_manager + tink::aead::cord_aead + tink::aead::kms_envelope_aead_key_manager + tink::signature::ecdsa_sign_key_manager + tink::signature::ecdsa_verify_key_manager + tink::util::test_matchers + tink::proto::aes_gcm_cc_proto + tink::proto::common_cc_proto + tink::proto::ecdsa_cc_proto +) + +tink_cc_library( + NAME keyset_wrapper_store + SRCS + keyset_wrapper_store.h + DEPS + tink::internal::keyset_wrapper + tink::internal::keyset_wrapper_impl + tink::core::primitive_wrapper + tink::util::status + tink::util::statusor +) + +tink_cc_test( + NAME keyset_wrapper_store_test + SRCS + keyset_wrapper_store_test.cc + DEPS + tink::internal::keyset_wrapper_store + tink::internal::registry_impl + gmock + absl::status + tink::core::primitive_set + tink::core::primitive_wrapper + tink::mac::mac_wrapper + tink::subtle::random + tink::util::status + tink::util::statusor + tink::util::test_matchers + tink::util::test_util + tink::proto::aes_gcm_cc_proto +) + +tink_cc_library( + NAME key_gen_configuration_impl + SRCS + key_gen_configuration_impl.h + DEPS + tink::internal::key_type_info_store + tink::core::key_gen_configuration +) + +tink_cc_test( + NAME key_gen_configuration_impl_test + SRCS + key_gen_configuration_impl_test.cc + DEPS + tink::internal::key_gen_configuration_impl + gmock + tink::core::key_gen_configuration + tink::aead::aead_key_templates + tink::util::test_matchers + tink::util::test_util + tink::proto::aes_gcm_cc_proto + tink::proto::rsa_ssa_pss_cc_proto +)
diff --git a/cc/internal/aes_util.cc b/cc/internal/aes_util.cc index d7d8c29..55af17b 100644 --- a/cc/internal/aes_util.cc +++ b/cc/internal/aes_util.cc
@@ -16,6 +16,7 @@ #include "tink/internal/aes_util.h" #include <cstdint> +#include <string> #include <vector> #include "absl/status/status.h" @@ -24,6 +25,7 @@ #include "absl/types/span.h" #include "openssl/aes.h" #include "openssl/evp.h" +#include "tink/util/statusor.h" #ifndef OPENSSL_IS_BORINGSSL // This is needed to use block128_f, which is necessary when OpenSSL is used. #include "openssl/modes.h"
diff --git a/cc/internal/aes_util_test.cc b/cc/internal/aes_util_test.cc index d28a813..806a49b 100644 --- a/cc/internal/aes_util_test.cc +++ b/cc/internal/aes_util_test.cc
@@ -18,7 +18,6 @@ #include <algorithm> #include <cstdint> #include <string> -#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -30,6 +29,7 @@ #include "openssl/evp.h" #include "tink/subtle/subtle_util.h" #include "tink/util/secret_data.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h"
diff --git a/cc/internal/bn_util.cc b/cc/internal/bn_util.cc index 22a2513..0d73ba8 100644 --- a/cc/internal/bn_util.cc +++ b/cc/internal/bn_util.cc
@@ -15,15 +15,21 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/bn_util.h" +#include <stddef.h> + +#include <memory> #include <string> #include <utility> +#include "absl/status/status.h" +#include "absl/strings/string_view.h" #include "absl/types/span.h" #include "openssl/bn.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/subtle/subtle_util.h" #include "tink/util/secret_data.h" #include "tink/util/status.h" +#include "tink/util/statusor.h" namespace crypto { namespace tink { @@ -34,6 +40,10 @@ if (bignum == nullptr) { return util::Status(absl::StatusCode::kInvalidArgument, "BIGNUM is NULL"); } + if (BN_is_negative(bignum)) { + return util::Status(absl::StatusCode::kInternal, + "Value must not be negative"); + } // BN_bn2binpad returns the length of the buffer on success and -1 on failure. int len = BN_bn2binpad(
diff --git a/cc/internal/bn_util.h b/cc/internal/bn_util.h index 8eea532..fce7eda 100644 --- a/cc/internal/bn_util.h +++ b/cc/internal/bn_util.h
@@ -16,11 +16,16 @@ #ifndef TINK_INTERNAL_BN_UTIL_H_ #define TINK_INTERNAL_BN_UTIL_H_ +#include <stddef.h> + #include <string> +#include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "openssl/bn.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/util/secret_data.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" namespace crypto {
diff --git a/cc/internal/bn_util_test.cc b/cc/internal/bn_util_test.cc index 161cbe6..7b6fd5c 100644 --- a/cc/internal/bn_util_test.cc +++ b/cc/internal/bn_util_test.cc
@@ -15,6 +15,9 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/bn_util.h" +#include <stddef.h> + +#include <memory> #include <string> #include <vector> @@ -22,9 +25,12 @@ #include "gtest/gtest.h" #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "openssl/bn.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/util/secret_data.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" @@ -59,6 +65,21 @@ } } +TEST(StringToBignum, IgnoresLeadingZeros) { + std::string encoded = absl::HexStringToBytes("0102"); + std::string encoded_with_leading_zeros = absl::HexStringToBytes("0000000102"); + + util::StatusOr<internal::SslUniquePtr<BIGNUM>> num = + StringToBignum(encoded); + ASSERT_THAT(num, IsOk()); + + util::StatusOr<internal::SslUniquePtr<BIGNUM>> num2 = + StringToBignum(encoded_with_leading_zeros); + ASSERT_THAT(num2, IsOk()); + + EXPECT_EQ(BN_cmp(num2->get(), num->get()), 0); +} + TEST(BnUtil, BignumToString) { std::vector<std::string> bn_strs = {"0000000000000000", "0000000000000001", "1000000000000000", "ffffffffffffffff", @@ -75,6 +96,94 @@ } } +TEST(BignumToStringWithBNNumBytes, NoLeadingZeros) { + { + util::StatusOr<internal::SslUniquePtr<BIGNUM>> bn0 = + StringToBignum(absl::HexStringToBytes("000000")); + ASSERT_THAT(bn0, IsOk()); + + util::StatusOr<std::string> encoded0 = + internal::BignumToString(bn0->get(), BN_num_bytes(bn0->get())); + ASSERT_THAT(encoded0, IsOk()); + EXPECT_EQ(*encoded0, absl::HexStringToBytes("")); + } + + { + util::StatusOr<internal::SslUniquePtr<BIGNUM>> bn127 = + StringToBignum(absl::HexStringToBytes("00007F")); + ASSERT_THAT(bn127, IsOk()); + + util::StatusOr<std::string> encoded127 = + internal::BignumToString(bn127->get(), BN_num_bytes(bn127->get())); + ASSERT_THAT(encoded127, IsOk()); + EXPECT_EQ(*encoded127, absl::HexStringToBytes("7F")); + } + + { + util::StatusOr<internal::SslUniquePtr<BIGNUM>> bn128 = + StringToBignum(absl::HexStringToBytes("000080")); + ASSERT_THAT(bn128, IsOk()); + + util::StatusOr<std::string> encoded128 = + internal::BignumToString(bn128->get(), BN_num_bytes(bn128->get())); + ASSERT_THAT(encoded128, IsOk()); + EXPECT_EQ(*encoded128, absl::HexStringToBytes("80")); + } + + { + util::StatusOr<internal::SslUniquePtr<BIGNUM>> bn255 = + StringToBignum(absl::HexStringToBytes("0000FF")); + ASSERT_THAT(bn255, IsOk()); + + util::StatusOr<std::string> encoded255 = + internal::BignumToString(bn255->get(), BN_num_bytes(bn255->get())); + ASSERT_THAT(encoded255, IsOk()); + EXPECT_EQ(*encoded255, absl::HexStringToBytes("FF")); + } + + { + util::StatusOr<internal::SslUniquePtr<BIGNUM>> bn256 = + StringToBignum(absl::HexStringToBytes("000100")); + ASSERT_THAT(bn256, IsOk()); + + util::StatusOr<std::string> encoded256 = + internal::BignumToString(bn256->get(), BN_num_bytes(bn256->get())); + ASSERT_THAT(encoded256, IsOk()); + EXPECT_EQ(*encoded256, absl::HexStringToBytes("0100")); + } +} + + +TEST(BignumToString, PadsWithLeadingZeros) { + util::StatusOr<internal::SslUniquePtr<BIGNUM>> num = + StringToBignum(absl::HexStringToBytes("0102")); + ASSERT_THAT(num, IsOk()); + + util::StatusOr<std::string> encoded = + BignumToString(num->get(), /*len=*/ 2); + ASSERT_THAT(encoded, IsOk()); + EXPECT_EQ(*encoded, absl::HexStringToBytes("0102")); + + util::StatusOr<std::string> encodedWithPadding = + BignumToString(num->get(), /*len=*/ 5); + ASSERT_THAT(encodedWithPadding, IsOk()); + EXPECT_EQ(*encodedWithPadding, absl::HexStringToBytes("0000000102")); + + // try to encode with a value for len that is too short. + ASSERT_THAT(BignumToString(num->get(), /*len=*/1), Not(IsOk())); +} + +TEST(BignumToString, RejectsNegativeNumbers) { + // create a negative BIGNUM + util::StatusOr<internal::SslUniquePtr<BIGNUM>> number = HexToBignum("01"); + ASSERT_THAT(number, IsOk()); + BN_set_negative(number->get(), 1); + // Check that number is negative + ASSERT_EQ(CompareBignumWithWord(number->get(), /*word=*/0), -1); + + ASSERT_THAT(BignumToString(number->get(), /*len=*/2), Not(IsOk())); +} + TEST(BnUtil, BignumToSecretData) { std::vector<std::string> bn_strs = {"0000000000000000", "0000000000000001", "1000000000000000", "ffffffffffffffff",
diff --git a/cc/internal/configuration_impl.h b/cc/internal/configuration_impl.h new file mode 100644 index 0000000..cf59e10 --- /dev/null +++ b/cc/internal/configuration_impl.h
@@ -0,0 +1,141 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_CONFIGURATION_IMPL_H_ +#define TINK_INTERNAL_CONFIGURATION_IMPL_H_ + +#include "tink/configuration.h" +#include "tink/internal/key_type_info_store.h" +#include "tink/internal/keyset_wrapper_store.h" + +namespace crypto { +namespace tink { +namespace internal { + +constexpr absl::string_view kConfigurationImplErr = + "Use crypto::tink::Registry instead when in global registry mode."; + +class ConfigurationImpl { + public: + template <class PW> + static crypto::tink::util::Status AddPrimitiveWrapper( + std::unique_ptr<PW> wrapper, crypto::tink::Configuration& config) { + if (config.global_registry_mode_) { + return crypto::tink::util::Status(absl::StatusCode::kFailedPrecondition, + kConfigurationImplErr); + } + + // Function `primitive_getter` must be defined here, since + // PW::InputPrimitive is not accessible later. + // TODO(b/284084337): Move primitive getter out of key manager. + std::function<crypto::tink::util::StatusOr< + std::unique_ptr<typename PW::InputPrimitive>>( + const google::crypto::tink::KeyData& key_data)> + primitive_getter = + [&config](const google::crypto::tink::KeyData& key_data) + -> crypto::tink::util::StatusOr< + std::unique_ptr<typename PW::InputPrimitive>> { + crypto::tink::util::StatusOr< + const crypto::tink::internal::KeyTypeInfoStore::Info*> + info = config.key_type_info_store_.Get(key_data.type_url()); + if (!info.ok()) { + return info.status(); + } + + crypto::tink::util::StatusOr< + const crypto::tink::KeyManager<typename PW::InputPrimitive>*> + key_manager = (*info)->get_key_manager<typename PW::InputPrimitive>( + key_data.type_url()); + if (!key_manager.ok()) { + return key_manager.status(); + } + + return (*key_manager)->GetPrimitive(key_data); + }; + + return config.keyset_wrapper_store_ + .Add<typename PW::InputPrimitive, typename PW::Primitive>( + std::move(wrapper), primitive_getter); + } + + template <class KM> + static crypto::tink::util::Status AddKeyTypeManager( + std::unique_ptr<KM> key_manager, crypto::tink::Configuration& config) { + if (config.global_registry_mode_) { + return crypto::tink::util::Status(absl::StatusCode::kFailedPrecondition, + kConfigurationImplErr); + } + return config.key_type_info_store_.AddKeyTypeManager( + std::move(key_manager), /*new_key_allowed=*/true); + } + + template <class PrivateKM, class PublicKM> + static crypto::tink::util::Status AddAsymmetricKeyManagers( + std::unique_ptr<PrivateKM> private_key_manager, + std::unique_ptr<PublicKM> public_key_manager, + crypto::tink::Configuration& config) { + if (config.global_registry_mode_) { + return crypto::tink::util::Status(absl::StatusCode::kFailedPrecondition, + kConfigurationImplErr); + } + return config.key_type_info_store_.AddAsymmetricKeyTypeManagers( + std::move(private_key_manager), std::move(public_key_manager), + /*new_key_allowed=*/true); + } + + static crypto::tink::util::StatusOr< + const crypto::tink::internal::KeyTypeInfoStore*> + GetKeyTypeInfoStore(const crypto::tink::Configuration& config) { + if (config.global_registry_mode_) { + return crypto::tink::util::Status(absl::StatusCode::kFailedPrecondition, + kConfigurationImplErr); + } + return &config.key_type_info_store_; + } + + static crypto::tink::util::StatusOr< + const crypto::tink::internal::KeysetWrapperStore*> + GetKeysetWrapperStore(const crypto::tink::Configuration& config) { + if (config.global_registry_mode_) { + return crypto::tink::util::Status(absl::StatusCode::kFailedPrecondition, + kConfigurationImplErr); + } + return &config.keyset_wrapper_store_; + } + + // `config` can be set to global registry mode only if empty. + static crypto::tink::util::Status SetGlobalRegistryMode( + crypto::tink::Configuration& config) { + if (!config.key_type_info_store_.IsEmpty() || + !config.keyset_wrapper_store_.IsEmpty()) { + return crypto::tink::util::Status(absl::StatusCode::kFailedPrecondition, + "Using the global registry is only " + "allowed when Configuration is empty."); + } + config.global_registry_mode_ = true; + return crypto::tink::util::OkStatus(); + } + + static bool GetGlobalRegistryMode(const crypto::tink::Configuration& config) { + return config.global_registry_mode_; + } +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_CONFIGURATION_IMPL_H_
diff --git a/cc/internal/configuration_impl_test.cc b/cc/internal/configuration_impl_test.cc new file mode 100644 index 0000000..0b33108 --- /dev/null +++ b/cc/internal/configuration_impl_test.cc
@@ -0,0 +1,466 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/configuration_impl.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/cleartext_keyset_handle.h" +#include "tink/configuration.h" +#include "tink/internal/keyset_wrapper_store.h" +#include "tink/subtle/random.h" +#include "tink/util/test_matchers.h" +#include "tink/util/test_util.h" +#include "proto/aes_gcm.pb.h" +#include "proto/rsa_ssa_pss.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesGcmKey; +using ::google::crypto::tink::AesGcmKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::OutputPrefixType; +using ::google::crypto::tink::RsaSsaPssKeyFormat; +using ::google::crypto::tink::RsaSsaPssParams; +using ::google::crypto::tink::RsaSsaPssPrivateKey; +using ::google::crypto::tink::RsaSsaPssPublicKey; + +class FakePrimitive { + public: + explicit FakePrimitive(std::string s) : s_(s) {} + std::string get() { return s_; } + + private: + std::string s_; +}; + +class FakePrimitive2 { + public: + explicit FakePrimitive2(std::string s) : s_(s) {} + std::string get() { return s_ + "2"; } + + private: + std::string s_; +}; + +// Transforms AesGcmKey into FakePrimitive. +class FakeKeyTypeManager + : public KeyTypeManager<AesGcmKey, AesGcmKeyFormat, List<FakePrimitive>> { + public: + class FakePrimitiveFactory : public PrimitiveFactory<FakePrimitive> { + public: + util::StatusOr<std::unique_ptr<FakePrimitive>> Create( + const AesGcmKey& key) const override { + return absl::make_unique<FakePrimitive>(key.key_value()); + } + }; + + FakeKeyTypeManager() + : KeyTypeManager(absl::make_unique<FakePrimitiveFactory>()) {} + + KeyData::KeyMaterialType key_material_type() const override { + return KeyData::SYMMETRIC; + } + + uint32_t get_version() const override { return 0; } + + const std::string& get_key_type() const override { return key_type_; } + + util::Status ValidateKey(const AesGcmKey& key) const override { + return util::OkStatus(); + } + + util::Status ValidateKeyFormat( + const AesGcmKeyFormat& key_format) const override { + return util::OkStatus(); + } + + util::StatusOr<AesGcmKey> CreateKey( + const AesGcmKeyFormat& key_format) const override { + return AesGcmKey(); + } + + util::StatusOr<AesGcmKey> DeriveKey( + const AesGcmKeyFormat& key_format, + InputStream* input_stream) const override { + return AesGcmKey(); + } + + private: + const std::string key_type_ = + "type.googleapis.com/google.crypto.tink.AesGcmKey"; +}; + +// Transforms FakePrimitive into FakePrimitive. +class FakePrimitiveWrapper + : public PrimitiveWrapper<FakePrimitive, FakePrimitive> { + public: + util::StatusOr<std::unique_ptr<FakePrimitive>> Wrap( + std::unique_ptr<PrimitiveSet<FakePrimitive>> primitive_set) + const override { + return absl::make_unique<FakePrimitive>( + primitive_set->get_primary()->get_primitive().get()); + } +}; + +// Transforms FakePrimitive2 into FakePrimitive. +class FakePrimitiveWrapper2 + : public PrimitiveWrapper<FakePrimitive2, FakePrimitive> { + public: + util::StatusOr<std::unique_ptr<FakePrimitive>> Wrap( + std::unique_ptr<PrimitiveSet<FakePrimitive2>> primitive_set) + const override { + return absl::make_unique<FakePrimitive>( + primitive_set->get_primary()->get_primitive().get()); + } +}; + +std::string AddAesGcmKeyToKeyset(Keyset& keyset, uint32_t key_id, + OutputPrefixType output_prefix_type, + KeyStatusType key_status_type) { + AesGcmKey key; + key.set_version(0); + key.set_key_value(subtle::Random::GetRandomBytes(16)); + KeyData key_data; + key_data.set_value(key.SerializeAsString()); + key_data.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey"); + test::AddKeyData(key_data, key_id, output_prefix_type, key_status_type, + &keyset); + return key.key_value(); +} + +TEST(ConfigurationImplTest, AddPrimitiveWrapper) { + Configuration config; + EXPECT_THAT((ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<FakePrimitiveWrapper>(), config)), + IsOk()); +} + +TEST(ConfigurationImplTest, AddKeyTypeManager) { + Configuration config; + EXPECT_THAT(ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<FakeKeyTypeManager>(), config), + IsOk()); +} + +TEST(ConfigurationImplTest, GetKeyTypeInfoStore) { + Configuration config; + ASSERT_THAT(ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<FakeKeyTypeManager>(), config), + IsOk()); + + std::string type_url = FakeKeyTypeManager().get_key_type(); + util::StatusOr<const KeyTypeInfoStore*> store = + ConfigurationImpl::GetKeyTypeInfoStore(config); + ASSERT_THAT(store, IsOk()); + util::StatusOr<const KeyTypeInfoStore::Info*> info = (*store)->Get(type_url); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<const KeyManager<FakePrimitive>*> key_manager = + (*info)->get_key_manager<FakePrimitive>(type_url); + ASSERT_THAT(key_manager, IsOk()); + EXPECT_EQ((*key_manager)->get_key_type(), type_url); +} + +TEST(ConfigurationImplTest, GetKeyTypeInfoStoreMissingInfoFails) { + Configuration config; + util::StatusOr<const KeyTypeInfoStore*> store = + ConfigurationImpl::GetKeyTypeInfoStore(config); + ASSERT_THAT(store, IsOk()); + EXPECT_THAT((*store)->Get("i.do.not.exist").status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(ConfigurationImplTest, GetKeysetWrapperStoreAndWrap) { + Configuration config; + ASSERT_THAT((ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<FakePrimitiveWrapper>(), config)), + IsOk()); + ASSERT_THAT(ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<FakeKeyTypeManager>(), config), + IsOk()); + + util::StatusOr<const KeysetWrapperStore*> store = + ConfigurationImpl::GetKeysetWrapperStore(config); + ASSERT_THAT(store, IsOk()); + util::StatusOr<const KeysetWrapper<FakePrimitive>*> wrapper = + (*store)->Get<FakePrimitive>(); + ASSERT_THAT(wrapper, IsOk()); + + Keyset keyset; + std::string raw_key = AddAesGcmKeyToKeyset( + keyset, /*key_id=*/13, OutputPrefixType::TINK, KeyStatusType::ENABLED); + keyset.set_primary_key_id(13); + + util::StatusOr<std::unique_ptr<FakePrimitive>> aead = + (*wrapper)->Wrap(keyset, /*annotations=*/{}); + ASSERT_THAT(aead, IsOk()); + EXPECT_EQ((*aead)->get(), raw_key); +} + +TEST(ConfigurationImplTest, KeysetWrapperWrapMissingKeyTypeInfoFails) { + Configuration config; + ASSERT_THAT(ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<FakePrimitiveWrapper>(), config), + IsOk()); + + util::StatusOr<const KeysetWrapperStore*> store = + ConfigurationImpl::GetKeysetWrapperStore(config); + ASSERT_THAT(store, IsOk()); + util::StatusOr<const KeysetWrapper<FakePrimitive>*> wrapper = + (*store)->Get<FakePrimitive>(); + ASSERT_THAT(wrapper, IsOk()); + + Keyset keyset; + std::string raw_key = AddAesGcmKeyToKeyset( + keyset, /*key_id=*/13, OutputPrefixType::TINK, KeyStatusType::ENABLED); + keyset.set_primary_key_id(13); + + EXPECT_THAT((*wrapper)->Wrap(keyset, /*annotations=*/{}).status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(ConfigurationImplTest, KeysetWrapperWrapMissingKeyManagerFails) { + Configuration config; + // Transforms FakePrimitive2 to FakePrimitive. + ASSERT_THAT((ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<FakePrimitiveWrapper2>(), config)), + IsOk()); + // Transforms KeyData to FakePrimitive. + ASSERT_THAT(ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<FakeKeyTypeManager>(), config), + IsOk()); + + // AesGcmKey KeyData -> FakePrimitive2 -> FakePrimitive is the success path, + // but the AesGcmKey KeyData -> FakePrimitive2 transformation is not + // registered. + util::StatusOr<const KeysetWrapperStore*> store = + ConfigurationImpl::GetKeysetWrapperStore(config); + ASSERT_THAT(store, IsOk()); + util::StatusOr<const KeysetWrapper<FakePrimitive>*> wrapper = + (*store)->Get<FakePrimitive>(); + ASSERT_THAT(wrapper, IsOk()); + + Keyset keyset; + std::string raw_key = AddAesGcmKeyToKeyset( + keyset, /*key_id=*/13, OutputPrefixType::TINK, KeyStatusType::ENABLED); + keyset.set_primary_key_id(13); + + // FakeKeyTypeManager cannot transform AesGcmKey KeyData -> FakePrimitive2. + EXPECT_THAT((*wrapper)->Wrap(keyset, /*annotations=*/{}).status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +class FakeSignKeyManager + : public PrivateKeyTypeManager<RsaSsaPssPrivateKey, RsaSsaPssKeyFormat, + RsaSsaPssPublicKey, List<PublicKeySign>> { + public: + class PublicKeySignFactory : public PrimitiveFactory<PublicKeySign> { + public: + util::StatusOr<std::unique_ptr<PublicKeySign>> Create( + const RsaSsaPssPrivateKey& key) const override { + return {absl::make_unique<test::DummyPublicKeySign>("a public key sign")}; + } + }; + + explicit FakeSignKeyManager() + : PrivateKeyTypeManager(absl::make_unique<PublicKeySignFactory>()) {} + + KeyData::KeyMaterialType key_material_type() const override { + return KeyData::ASYMMETRIC_PRIVATE; + } + + uint32_t get_version() const override { return 0; } + + const std::string& get_key_type() const override { return key_type_; } + + util::Status ValidateKey(const RsaSsaPssPrivateKey& key) const override { + return util::OkStatus(); + } + + util::Status ValidateKeyFormat( + const RsaSsaPssKeyFormat& key_format) const override { + return util::OkStatus(); + } + + util::StatusOr<RsaSsaPssPrivateKey> CreateKey( + const RsaSsaPssKeyFormat& key_format) const override { + return RsaSsaPssPrivateKey(); + } + + util::StatusOr<RsaSsaPssPrivateKey> DeriveKey( + const RsaSsaPssKeyFormat& key_format, + InputStream* input_stream) const override { + return RsaSsaPssPrivateKey(); + } + + util::StatusOr<RsaSsaPssPublicKey> GetPublicKey( + const RsaSsaPssPrivateKey& private_key) const override { + return private_key.public_key(); + } + + private: + const std::string key_type_ = "some.sign.key.type"; +}; + +class FakeVerifyKeyManager + : public KeyTypeManager<RsaSsaPssPublicKey, void, List<PublicKeyVerify>> { + public: + class PublicKeyVerifyFactory : public PrimitiveFactory<PublicKeyVerify> { + public: + util::StatusOr<std::unique_ptr<PublicKeyVerify>> Create( + const RsaSsaPssPublicKey& key) const override { + return { + absl::make_unique<test::DummyPublicKeyVerify>("a public key verify")}; + } + }; + + explicit FakeVerifyKeyManager() + : KeyTypeManager(absl::make_unique<PublicKeyVerifyFactory>()) {} + + KeyData::KeyMaterialType key_material_type() const override { + return KeyData::ASYMMETRIC_PUBLIC; + } + + uint32_t get_version() const override { return 0; } + + const std::string& get_key_type() const override { return key_type_; } + + util::Status ValidateKey(const RsaSsaPssPublicKey& key) const override { + return util::OkStatus(); + } + + util::Status ValidateParams(const RsaSsaPssParams& params) const { + return util::OkStatus(); + } + + private: + const std::string key_type_ = "some.verify.key.type"; +}; + +TEST(ConfigurationImplTest, AddAsymmetricKeyManagers) { + Configuration config; + EXPECT_THAT(ConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<FakeSignKeyManager>(), + absl::make_unique<FakeVerifyKeyManager>(), config), + IsOk()); +} + +TEST(ConfigurationImplTest, GetKeyTypeInfoStoreAsymmetric) { + Configuration config; + ASSERT_THAT(ConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<FakeSignKeyManager>(), + absl::make_unique<FakeVerifyKeyManager>(), config), + IsOk()); + + { + std::string type_url = FakeSignKeyManager().get_key_type(); + util::StatusOr<const KeyTypeInfoStore*> store = + ConfigurationImpl::GetKeyTypeInfoStore(config); + ASSERT_THAT(store, IsOk()); + util::StatusOr<const KeyTypeInfoStore::Info*> info = + (*store)->Get(type_url); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<const KeyManager<PublicKeySign>*> key_manager = + (*info)->get_key_manager<PublicKeySign>(type_url); + ASSERT_THAT(key_manager, IsOk()); + EXPECT_EQ((*key_manager)->get_key_type(), type_url); + } + { + std::string type_url = FakeVerifyKeyManager().get_key_type(); + util::StatusOr<const KeyTypeInfoStore*> store = + ConfigurationImpl::GetKeyTypeInfoStore(config); + ASSERT_THAT(store, IsOk()); + util::StatusOr<const KeyTypeInfoStore::Info*> info = + (*store)->Get(type_url); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<const KeyManager<PublicKeyVerify>*> key_manager = + (*info)->get_key_manager<PublicKeyVerify>(type_url); + ASSERT_THAT(key_manager, IsOk()); + EXPECT_EQ((*key_manager)->get_key_type(), type_url); + } +} + +TEST(ConfigurationImplTest, GlobalRegistryMode) { + Registry::Reset(); + Configuration config; + ASSERT_THAT(ConfigurationImpl::SetGlobalRegistryMode(config), IsOk()); + EXPECT_TRUE(ConfigurationImpl::GetGlobalRegistryMode(config)); + + // Check that ConfigurationImpl functions return kFailedPrecondition. + EXPECT_THAT(ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<FakePrimitiveWrapper>(), config), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_THAT(ConfigurationImpl::AddKeyTypeManager( + absl::make_unique<FakeKeyTypeManager>(), config), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_THAT(ConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<FakeSignKeyManager>(), + absl::make_unique<FakeVerifyKeyManager>(), config), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_THAT(ConfigurationImpl::GetKeyTypeInfoStore(config).status(), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_THAT(ConfigurationImpl::GetKeysetWrapperStore(config).status(), + StatusIs(absl::StatusCode::kFailedPrecondition)); + + Keyset keyset; + std::string raw_key = AddAesGcmKeyToKeyset( + keyset, /*key_id=*/13, OutputPrefixType::TINK, KeyStatusType::ENABLED); + keyset.set_primary_key_id(13); + std::unique_ptr<KeysetHandle> handle = + CleartextKeysetHandle::GetKeysetHandle(keyset); + // TODO(b/265705174): Replace with GetPrimitive(config) once implemented. + EXPECT_THAT(handle->GetPrimitive<FakePrimitive>().status(), + StatusIs(absl::StatusCode::kNotFound)); + + ASSERT_THAT(Registry::RegisterPrimitiveWrapper( + absl::make_unique<FakePrimitiveWrapper>()), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<FakeKeyTypeManager>(), + /*new_key_allowed=*/true), + IsOk()); + // TODO(b/265705174): Replace with GetPrimitive(config) once implemented. + EXPECT_THAT(handle->GetPrimitive<FakePrimitive>(), IsOk()); +} + +TEST(ConfigurationImplTest, GlobalRegistryModeWithNonEmptyConfigFails) { + Configuration config; + ASSERT_THAT(ConfigurationImpl::AddPrimitiveWrapper( + absl::make_unique<FakePrimitiveWrapper>(), config), + IsOk()); + EXPECT_THAT(ConfigurationImpl::SetGlobalRegistryMode(config), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_FALSE(ConfigurationImpl::GetGlobalRegistryMode(config)); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/ec_util.cc b/cc/internal/ec_util.cc index 1dac7b0..f832227 100644 --- a/cc/internal/ec_util.cc +++ b/cc/internal/ec_util.cc
@@ -21,14 +21,16 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include "absl/memory/memory.h" #include "absl/status/status.h" -#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "openssl/bn.h" #include "openssl/ec.h" +#include "openssl/crypto.h" #include "openssl/ecdsa.h" #include "openssl/evp.h" #include "tink/internal/bn_util.h"
diff --git a/cc/internal/ec_util.h b/cc/internal/ec_util.h index 155cd7b..3a47b36 100644 --- a/cc/internal/ec_util.h +++ b/cc/internal/ec_util.h
@@ -16,8 +16,12 @@ #ifndef TINK_INTERNAL_EC_UTIL_H_ #define TINK_INTERNAL_EC_UTIL_H_ +#include <stdint.h> + +#include <memory> #include <string> +#include "absl/strings/string_view.h" #include "openssl/ec.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/subtle/common_enums.h"
diff --git a/cc/internal/ec_util_test.cc b/cc/internal/ec_util_test.cc index 4f4b1ba..5354e9a 100644 --- a/cc/internal/ec_util_test.cc +++ b/cc/internal/ec_util_test.cc
@@ -15,20 +15,25 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/ec_util.h" +#include <stdint.h> + #include <memory> #include <string> #include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/status/status.h" #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" +#include "openssl/bn.h" #include "openssl/ec.h" #include "openssl/ecdsa.h" #include "openssl/evp.h" +#include "include/rapidjson/document.h" #include "tink/internal/bn_util.h" #include "tink/internal/fips_utils.h" #include "tink/internal/ssl_unique_ptr.h" @@ -37,6 +42,7 @@ #include "tink/subtle/subtle_util.h" #include "tink/subtle/wycheproof_util.h" #include "tink/util/secret_data.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" @@ -425,7 +431,7 @@ EllipticCurveType curve; }; -const std::vector<EncodingTestVector> GetEncodingTestVectors() { +std::vector<EncodingTestVector> GetEncodingTestVectors() { return { {EcPointFormat::UNCOMPRESSED, "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec6" @@ -826,7 +832,6 @@ others = ReadEcdhWycheproofTestVectors( /*file_name=*/"ecdh_secp521r1_test.json"); test_vectors.insert(test_vectors.end(), others.begin(), others.end()); -// placeholder_disabled_subtle_test, please ignore others = ReadEcdhWycheproofTestVectors( /*file_name=*/"ecdh_test.json"); test_vectors.insert(test_vectors.end(), others.begin(), others.end());
diff --git a/cc/internal/err_util.cc b/cc/internal/err_util.cc index 3f5c0ad..06dc10d 100644 --- a/cc/internal/err_util.cc +++ b/cc/internal/err_util.cc
@@ -15,6 +15,8 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/err_util.h" +#include <stddef.h> + #include <string> #include "openssl/err.h"
diff --git a/cc/internal/err_util_test.cc b/cc/internal/err_util_test.cc index 6da867f..7343fd4 100644 --- a/cc/internal/err_util_test.cc +++ b/cc/internal/err_util_test.cc
@@ -16,6 +16,7 @@ #include "tink/internal/err_util.h" #include <string> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/internal/fips_utils.cc b/cc/internal/fips_utils.cc index 2384a26..b08e7c7 100644 --- a/cc/internal/fips_utils.cc +++ b/cc/internal/fips_utils.cc
@@ -18,8 +18,10 @@ #include <atomic> +#include "absl/base/attributes.h" #include "absl/status/status.h" #include "openssl/crypto.h" +#include "tink/util/status.h" namespace crypto { namespace tink { @@ -37,8 +39,17 @@ void UnSetFipsRestricted() { is_fips_restricted = false; } -crypto::tink::util::Status ChecksFipsCompatibility( - FipsCompatibility fips_status) { +bool IsFipsModeEnabled() { return kUseOnlyFips || is_fips_restricted; } + +bool IsFipsEnabledInSsl() { +#ifdef OPENSSL_IS_BORINGSSL + return FIPS_mode(); +#else + return false; +#endif +} + +util::Status ChecksFipsCompatibility(FipsCompatibility fips_status) { switch (fips_status) { case FipsCompatibility::kNotFips: if (IsFipsModeEnabled()) { @@ -48,7 +59,7 @@ return util::OkStatus(); } case FipsCompatibility::kRequiresBoringCrypto: - if ((IsFipsModeEnabled()) && !FIPS_mode()) { + if ((IsFipsModeEnabled()) && !IsFipsEnabledInSsl()) { return util::Status( absl::StatusCode::kInternal, "BoringSSL not built with the BoringCrypto module. If you want to " @@ -63,8 +74,6 @@ } } -bool IsFipsModeEnabled() { return kUseOnlyFips || is_fips_restricted; } - } // namespace internal } // namespace tink } // namespace crypto
diff --git a/cc/internal/fips_utils.h b/cc/internal/fips_utils.h index a1867cd..165aa33 100644 --- a/cc/internal/fips_utils.h +++ b/cc/internal/fips_utils.h
@@ -34,6 +34,9 @@ // the FIPS restrictions have been enabled at runtime. bool IsFipsModeEnabled(); +// Returns true if the Ssl layer (BoringSSL or OpenSSL) has FIPS mode enabled. +bool IsFipsEnabledInSsl(); + // Enable FIPS restrictions. If Tink has been built in FIPS mode this is // redundant. void SetFipsRestricted();
diff --git a/cc/internal/fips_utils_test.cc b/cc/internal/fips_utils_test.cc index e16f635..c852c30 100644 --- a/cc/internal/fips_utils_test.cc +++ b/cc/internal/fips_utils_test.cc
@@ -24,7 +24,7 @@ namespace crypto { namespace tink { - +namespace internal { namespace { using ::crypto::tink::test::IsOk; @@ -32,58 +32,52 @@ class FipsIncompatible { public: - static constexpr crypto::tink::internal::FipsCompatibility kFipsStatus = - crypto::tink::internal::FipsCompatibility::kNotFips; + static constexpr FipsCompatibility kFipsStatus = FipsCompatibility::kNotFips; }; class FipsCompatibleWithBoringCrypto { public: - static constexpr crypto::tink::internal::FipsCompatibility kFipsStatus = - crypto::tink::internal::FipsCompatibility::kRequiresBoringCrypto; + static constexpr FipsCompatibility kFipsStatus = + FipsCompatibility::kRequiresBoringCrypto; }; TEST(FipsUtilsTest, CompatibilityInNonFipsMode) { - if (internal::kUseOnlyFips) { + if (kUseOnlyFips) { GTEST_SKIP() << "Not supported in FIPS-only mode"; } - EXPECT_THAT(internal::CheckFipsCompatibility<FipsIncompatible>(), IsOk()); - EXPECT_THAT( - internal::CheckFipsCompatibility<FipsCompatibleWithBoringCrypto>(), - IsOk()); + EXPECT_THAT(CheckFipsCompatibility<FipsIncompatible>(), IsOk()); + EXPECT_THAT(CheckFipsCompatibility<FipsCompatibleWithBoringCrypto>(), IsOk()); } TEST(FipsUtilsTest, CompatibilityInFipsMode) { - if (!internal::kUseOnlyFips || !FIPS_mode()) { + if (!kUseOnlyFips || !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should only run in FIPS mode with Boringcrypto available."; } - EXPECT_THAT(internal::CheckFipsCompatibility<FipsIncompatible>(), + EXPECT_THAT(CheckFipsCompatibility<FipsIncompatible>(), StatusIs(absl::StatusCode::kInternal)); - EXPECT_THAT( - internal::CheckFipsCompatibility<FipsCompatibleWithBoringCrypto>(), - IsOk()); + EXPECT_THAT(CheckFipsCompatibility<FipsCompatibleWithBoringCrypto>(), IsOk()); } TEST(TinkFipsTest, CompatibilityInFipsModeWithoutBoringCrypto) { - if (!internal::kUseOnlyFips || FIPS_mode()) { + if (!kUseOnlyFips || IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test only run if BoringCrypto module is not available."; } // In FIPS only mode compatibility checks should disallow algorithms // with the FipsCompatibility::kNone flag. - EXPECT_THAT(internal::CheckFipsCompatibility<FipsIncompatible>(), + EXPECT_THAT(CheckFipsCompatibility<FipsIncompatible>(), StatusIs(absl::StatusCode::kInternal)); // FIPS validated implementations are not allowed if BoringCrypto is not // available. - EXPECT_THAT( - internal::CheckFipsCompatibility<FipsCompatibleWithBoringCrypto>(), - StatusIs(absl::StatusCode::kInternal)); + EXPECT_THAT(CheckFipsCompatibility<FipsCompatibleWithBoringCrypto>(), + StatusIs(absl::StatusCode::kInternal)); } } // namespace - +} // namespace internal } // namespace tink } // namespace crypto
diff --git a/cc/internal/key_gen_configuration_impl.h b/cc/internal/key_gen_configuration_impl.h new file mode 100644 index 0000000..2d3d602 --- /dev/null +++ b/cc/internal/key_gen_configuration_impl.h
@@ -0,0 +1,91 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_KEY_GEN_CONFIGURATION_IMPL_H_ +#define TINK_INTERNAL_KEY_GEN_CONFIGURATION_IMPL_H_ + +#include "tink/internal/key_type_info_store.h" +#include "tink/key_gen_configuration.h" + +namespace crypto { +namespace tink { +namespace internal { + +constexpr absl::string_view kKeyGenConfigurationImplErr = + "Use crypto::tink::Registry instead when in global registry mode."; + +class KeyGenConfigurationImpl { + public: + template <class KM> + static crypto::tink::util::Status AddKeyTypeManager( + std::unique_ptr<KM> key_manager, + crypto::tink::KeyGenConfiguration& config) { + if (config.global_registry_mode_) { + return crypto::tink::util::Status(absl::StatusCode::kFailedPrecondition, + kKeyGenConfigurationImplErr); + } + return config.key_type_info_store_.AddKeyTypeManager( + std::move(key_manager), /*new_key_allowed=*/true); + } + + template <class PrivateKM, class PublicKM> + static crypto::tink::util::Status AddAsymmetricKeyManagers( + std::unique_ptr<PrivateKM> private_key_manager, + std::unique_ptr<PublicKM> public_key_manager, + crypto::tink::KeyGenConfiguration& config) { + if (config.global_registry_mode_) { + return crypto::tink::util::Status(absl::StatusCode::kFailedPrecondition, + kKeyGenConfigurationImplErr); + } + return config.key_type_info_store_.AddAsymmetricKeyTypeManagers( + std::move(private_key_manager), std::move(public_key_manager), + /*new_key_allowed=*/true); + } + + static crypto::tink::util::StatusOr< + const crypto::tink::internal::KeyTypeInfoStore*> + GetKeyTypeInfoStore(const crypto::tink::KeyGenConfiguration& config) { + if (config.global_registry_mode_) { + return crypto::tink::util::Status(absl::StatusCode::kFailedPrecondition, + kKeyGenConfigurationImplErr); + } + return &config.key_type_info_store_; + } + + // `config` can be set to global registry mode only if empty. + static crypto::tink::util::Status SetGlobalRegistryMode( + crypto::tink::KeyGenConfiguration& config) { + if (!config.key_type_info_store_.IsEmpty()) { + return crypto::tink::util::Status( + absl::StatusCode::kFailedPrecondition, + "Using the global registry is only allowed when KeyGenConfiguration " + "is empty."); + } + config.global_registry_mode_ = true; + return crypto::tink::util::OkStatus(); + } + + static bool GetGlobalRegistryMode( + const crypto::tink::KeyGenConfiguration& config) { + return config.global_registry_mode_; + } +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_KEY_GEN_CONFIGURATION_IMPL_H_
diff --git a/cc/internal/key_gen_configuration_impl_test.cc b/cc/internal/key_gen_configuration_impl_test.cc new file mode 100644 index 0000000..d9a9126 --- /dev/null +++ b/cc/internal/key_gen_configuration_impl_test.cc
@@ -0,0 +1,312 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/key_gen_configuration_impl.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/aead/aead_key_templates.h" +#include "tink/key_gen_configuration.h" +#include "tink/util/test_matchers.h" +#include "tink/util/test_util.h" +#include "proto/aes_gcm.pb.h" +#include "proto/rsa_ssa_pss.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesGcmKey; +using ::google::crypto::tink::AesGcmKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::RsaSsaPssKeyFormat; +using ::google::crypto::tink::RsaSsaPssParams; +using ::google::crypto::tink::RsaSsaPssPrivateKey; +using ::google::crypto::tink::RsaSsaPssPublicKey; + +class FakePrimitive { + public: + explicit FakePrimitive(std::string s) : s_(s) {} + std::string get() { return s_; } + + private: + std::string s_; +}; + +class FakeKeyTypeManager + : public KeyTypeManager<AesGcmKey, AesGcmKeyFormat, List<FakePrimitive>> { + public: + class FakePrimitiveFactory : public PrimitiveFactory<FakePrimitive> { + public: + util::StatusOr<std::unique_ptr<FakePrimitive>> Create( + const AesGcmKey& key) const override { + return absl::make_unique<FakePrimitive>(key.key_value()); + } + }; + + FakeKeyTypeManager() + : KeyTypeManager(absl::make_unique<FakePrimitiveFactory>()) {} + + KeyData::KeyMaterialType key_material_type() const override { + return KeyData::SYMMETRIC; + } + + uint32_t get_version() const override { return 0; } + + const std::string& get_key_type() const override { return key_type_; } + + util::Status ValidateKey(const AesGcmKey& key) const override { + return util::OkStatus(); + } + + util::Status ValidateKeyFormat( + const AesGcmKeyFormat& key_format) const override { + return util::OkStatus(); + } + + util::StatusOr<AesGcmKey> CreateKey( + const AesGcmKeyFormat& key_format) const override { + return AesGcmKey(); + } + + util::StatusOr<AesGcmKey> DeriveKey( + const AesGcmKeyFormat& key_format, + InputStream* input_stream) const override { + return AesGcmKey(); + } + + private: + const std::string key_type_ = + "type.googleapis.com/google.crypto.tink.AesGcmKey"; +}; + +TEST(KeyGenConfigurationImplTest, AddKeyTypeManager) { + KeyGenConfiguration config; + EXPECT_THAT(KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<FakeKeyTypeManager>(), config), + IsOk()); +} + +TEST(KeyGenConfigurationImplTest, GetKeyTypeInfoStore) { + KeyGenConfiguration config; + ASSERT_THAT(KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<FakeKeyTypeManager>(), config), + IsOk()); + + std::string type_url = FakeKeyTypeManager().get_key_type(); + util::StatusOr<const KeyTypeInfoStore*> store = + KeyGenConfigurationImpl::GetKeyTypeInfoStore(config); + ASSERT_THAT(store, IsOk()); + util::StatusOr<const KeyTypeInfoStore::Info*> info = (*store)->Get(type_url); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<const KeyManager<FakePrimitive>*> key_manager = + (*info)->get_key_manager<FakePrimitive>(type_url); + ASSERT_THAT(key_manager, IsOk()); + EXPECT_EQ((*key_manager)->get_key_type(), type_url); +} + +TEST(KeyGenConfigurationImplTest, GetKeyTypeInfoStoreMissingInfoFails) { + KeyGenConfiguration config; + util::StatusOr<const KeyTypeInfoStore*> store = + KeyGenConfigurationImpl::GetKeyTypeInfoStore(config); + ASSERT_THAT(store, IsOk()); + EXPECT_THAT((*store)->Get("i.do.not.exist").status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +class FakeSignKeyManager + : public PrivateKeyTypeManager<RsaSsaPssPrivateKey, RsaSsaPssKeyFormat, + RsaSsaPssPublicKey, List<PublicKeySign>> { + public: + class PublicKeySignFactory : public PrimitiveFactory<PublicKeySign> { + public: + util::StatusOr<std::unique_ptr<PublicKeySign>> Create( + const RsaSsaPssPrivateKey& key) const override { + return {absl::make_unique<test::DummyPublicKeySign>("a public key sign")}; + } + }; + + explicit FakeSignKeyManager() + : PrivateKeyTypeManager(absl::make_unique<PublicKeySignFactory>()) {} + + KeyData::KeyMaterialType key_material_type() const override { + return KeyData::ASYMMETRIC_PRIVATE; + } + + uint32_t get_version() const override { return 0; } + + const std::string& get_key_type() const override { return key_type_; } + + util::Status ValidateKey(const RsaSsaPssPrivateKey& key) const override { + return util::OkStatus(); + } + + util::Status ValidateKeyFormat( + const RsaSsaPssKeyFormat& key_format) const override { + return util::OkStatus(); + } + + util::StatusOr<RsaSsaPssPrivateKey> CreateKey( + const RsaSsaPssKeyFormat& key_format) const override { + return RsaSsaPssPrivateKey(); + } + + util::StatusOr<RsaSsaPssPrivateKey> DeriveKey( + const RsaSsaPssKeyFormat& key_format, + InputStream* input_stream) const override { + return RsaSsaPssPrivateKey(); + } + + util::StatusOr<RsaSsaPssPublicKey> GetPublicKey( + const RsaSsaPssPrivateKey& private_key) const override { + return private_key.public_key(); + } + + private: + const std::string key_type_ = "some.sign.key.type"; +}; + +class FakeVerifyKeyManager + : public KeyTypeManager<RsaSsaPssPublicKey, void, List<PublicKeyVerify>> { + public: + class PublicKeyVerifyFactory : public PrimitiveFactory<PublicKeyVerify> { + public: + util::StatusOr<std::unique_ptr<PublicKeyVerify>> Create( + const RsaSsaPssPublicKey& key) const override { + return { + absl::make_unique<test::DummyPublicKeyVerify>("a public key verify")}; + } + }; + + explicit FakeVerifyKeyManager() + : KeyTypeManager(absl::make_unique<PublicKeyVerifyFactory>()) {} + + KeyData::KeyMaterialType key_material_type() const override { + return KeyData::ASYMMETRIC_PUBLIC; + } + + uint32_t get_version() const override { return 0; } + + const std::string& get_key_type() const override { return key_type_; } + + util::Status ValidateKey(const RsaSsaPssPublicKey& key) const override { + return util::OkStatus(); + } + + util::Status ValidateParams(const RsaSsaPssParams& params) const { + return util::OkStatus(); + } + + private: + const std::string key_type_ = "some.verify.key.type"; +}; + +TEST(KeyGenConfigurationImplTest, AddAsymmetricKeyManagers) { + KeyGenConfiguration config; + EXPECT_THAT(KeyGenConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<FakeSignKeyManager>(), + absl::make_unique<FakeVerifyKeyManager>(), config), + IsOk()); +} + +TEST(KeyGenConfigurationImplTest, GetKeyTypeInfoStoreAsymmetric) { + KeyGenConfiguration config; + ASSERT_THAT(KeyGenConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<FakeSignKeyManager>(), + absl::make_unique<FakeVerifyKeyManager>(), config), + IsOk()); + + { + std::string type_url = FakeSignKeyManager().get_key_type(); + util::StatusOr<const KeyTypeInfoStore*> store = + KeyGenConfigurationImpl::GetKeyTypeInfoStore(config); + ASSERT_THAT(store, IsOk()); + util::StatusOr<const KeyTypeInfoStore::Info*> info = + (*store)->Get(type_url); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<const KeyManager<PublicKeySign>*> key_manager = + (*info)->get_key_manager<PublicKeySign>(type_url); + ASSERT_THAT(key_manager, IsOk()); + EXPECT_EQ((*key_manager)->get_key_type(), type_url); + } + { + std::string type_url = FakeVerifyKeyManager().get_key_type(); + util::StatusOr<const KeyTypeInfoStore*> store = + KeyGenConfigurationImpl::GetKeyTypeInfoStore(config); + ASSERT_THAT(store, IsOk()); + util::StatusOr<const KeyTypeInfoStore::Info*> info = + (*store)->Get(type_url); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<const KeyManager<PublicKeyVerify>*> key_manager = + (*info)->get_key_manager<PublicKeyVerify>(type_url); + ASSERT_THAT(key_manager, IsOk()); + EXPECT_EQ((*key_manager)->get_key_type(), type_url); + } +} + +TEST(KeyGenConfigurationImplTest, GlobalRegistryMode) { + Registry::Reset(); + KeyGenConfiguration config; + ASSERT_THAT(KeyGenConfigurationImpl::SetGlobalRegistryMode(config), IsOk()); + EXPECT_TRUE(KeyGenConfigurationImpl::GetGlobalRegistryMode(config)); + + // Check that KeyGenConfigurationImpl functions return kFailedPrecondition. + EXPECT_THAT(KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<FakeKeyTypeManager>(), config), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_THAT(KeyGenConfigurationImpl::AddAsymmetricKeyManagers( + absl::make_unique<FakeSignKeyManager>(), + absl::make_unique<FakeVerifyKeyManager>(), config), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_THAT(KeyGenConfigurationImpl::GetKeyTypeInfoStore(config).status(), + StatusIs(absl::StatusCode::kFailedPrecondition)); + + // TODO(b/265705174): Replace with KeysetHandle::GenerateNew(config). + EXPECT_THAT(Registry::NewKeyData(AeadKeyTemplates::Aes256Gcm()).status(), + StatusIs(absl::StatusCode::kNotFound)); + + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<FakeKeyTypeManager>(), + /*new_key_allowed=*/true), + IsOk()); + // TODO(b/265705174): Replace with KeysetHandle::GenerateNew(config) once + // implemented. + EXPECT_THAT(Registry::NewKeyData(AeadKeyTemplates::Aes256Gcm()), IsOk()); +} + +TEST(KeyGenConfigurationImplTest, GlobalRegistryModeWithNonEmptyConfigFails) { + KeyGenConfiguration config; + ASSERT_THAT(KeyGenConfigurationImpl::AddKeyTypeManager( + absl::make_unique<FakeKeyTypeManager>(), config), + IsOk()); + EXPECT_THAT(KeyGenConfigurationImpl::SetGlobalRegistryMode(config), + StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_FALSE(KeyGenConfigurationImpl::GetGlobalRegistryMode(config)); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/key_info.cc b/cc/internal/key_info.cc index c864741..04d0c13 100644 --- a/cc/internal/key_info.cc +++ b/cc/internal/key_info.cc
@@ -16,6 +16,8 @@ #include "tink/internal/key_info.h" +#include "proto/tink.pb.h" + namespace crypto { namespace tink {
diff --git a/cc/internal/key_parser.h b/cc/internal/key_parser.h new file mode 100644 index 0000000..c734814 --- /dev/null +++ b/cc/internal/key_parser.h
@@ -0,0 +1,123 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_KEY_PARSER_H_ +#define TINK_INTERNAL_KEY_PARSER_H_ + +#include <functional> +#include <memory> +#include <string> +#include <typeindex> +#include <utility> + +#include "absl/functional/function_ref.h" +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/internal/parser_index.h" +#include "tink/internal/serialization.h" +#include "tink/key.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Non-template base class that can be used with internal registry map. +class KeyParser { + public: + // Parses a `serialization` into a key. + // + // This function is usually called on a `Serialization` subclass matching the + // value returned by `ObjectIdentifier()`. However, implementations should + // check that this is the case. + virtual util::StatusOr<std::unique_ptr<Key>> ParseKey( + const Serialization& serialization, + absl::optional<SecretKeyAccessToken> token) const = 0; + + // Returns the object identifier for `SerializationT`, which is only valid + // for the lifetime of this object. + // + // The object identifier is a unique identifier per registry for this object + // (in the standard proto serialization, it is the type URL). In other words, + // when registering a `KeyParser`, the registry will invoke this to get + // the handled object identifier. In order to parse an object of + // `SerializationT`, the registry will then obtain the object identifier of + // this serialization object, and call the parser corresponding to this + // object. + virtual absl::string_view ObjectIdentifier() const = 0; + + // Returns an index that can be used to look up the `KeyParser` + // object registered for the `KeyT` type in a registry. + virtual ParserIndex Index() const = 0; + + virtual ~KeyParser() = default; +}; + +// Parses `SerializationT` objects into `KeyT` objects. +template <typename SerializationT, typename KeyT> +class KeyParserImpl : public KeyParser { + public: + // Creates a key parser with `object_identifier` and parsing `function`. The + // referenced `function` should outlive the created key parser object. + explicit KeyParserImpl( + absl::string_view object_identifier, + absl::FunctionRef<util::StatusOr<KeyT>( + SerializationT, absl::optional<SecretKeyAccessToken>)> + function) + : object_identifier_(object_identifier), function_(function) {} + + util::StatusOr<std::unique_ptr<Key>> ParseKey( + const Serialization& serialization, + absl::optional<SecretKeyAccessToken> token) const override { + if (serialization.ObjectIdentifier() != object_identifier_) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Invalid object identifier for this key parser."); + } + const SerializationT* st = + dynamic_cast<const SerializationT*>(&serialization); + if (st == nullptr) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Invalid serialization type for this key parser."); + } + util::StatusOr<KeyT> key = function_(*st, token); + if (!key.ok()) return key.status(); + return {absl::make_unique<KeyT>(std::move(*key))}; + } + + absl::string_view ObjectIdentifier() const override { + return object_identifier_; + } + + ParserIndex Index() const override { + return ParserIndex::Create<SerializationT>(object_identifier_); + } + + private: + std::string object_identifier_; + std::function<util::StatusOr<KeyT>(SerializationT, + absl::optional<SecretKeyAccessToken>)> + function_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_KEY_PARSER_H_
diff --git a/cc/internal/key_parser_test.cc b/cc/internal/key_parser_test.cc new file mode 100644 index 0000000..c9dac8e --- /dev/null +++ b/cc/internal/key_parser_test.cc
@@ -0,0 +1,110 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/key_parser.h" + +#include <memory> +#include <optional> +#include <string_view> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/parser_index.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serialization_test_util.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::testing::Eq; + +TEST(KeyParserTest, Create) { + std::unique_ptr<KeyParser> parser = + absl::make_unique<KeyParserImpl<NoIdSerialization, NoIdKey>>( + kNoIdTypeUrl, ParseNoIdKey); + + EXPECT_THAT(parser->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + EXPECT_THAT( + parser->Index(), + Eq(ParserIndex::Create<NoIdSerialization>(kNoIdTypeUrl))); +} + +TEST(KeyParserTest, ParseKey) { + std::unique_ptr<KeyParser> parser = + absl::make_unique<KeyParserImpl<NoIdSerialization, NoIdKey>>( + kNoIdTypeUrl, ParseNoIdKey); + + NoIdSerialization serialization; + util::StatusOr<std::unique_ptr<Key>> key = + parser->ParseKey(serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(absl::nullopt)); + EXPECT_THAT((*key)->GetParameters(), Eq(NoIdParams())); + EXPECT_THAT(**key, Eq(NoIdKey())); +} + +TEST(KeyParserTest, ParsePublicKeyNoAccessToken) { + std::unique_ptr<KeyParser> parser = + absl::make_unique<KeyParserImpl<NoIdSerialization, NoIdKey>>( + kNoIdTypeUrl, ParseNoIdKey); + + NoIdSerialization serialization; + util::StatusOr<std::unique_ptr<Key>> public_key = + parser->ParseKey(serialization, absl::nullopt); + ASSERT_THAT(public_key, IsOk()); + EXPECT_THAT((*public_key)->GetIdRequirement(), Eq(absl::nullopt)); + EXPECT_THAT((*public_key)->GetParameters(), Eq(NoIdParams())); + EXPECT_THAT(**public_key, Eq(NoIdKey())); +} + +TEST(KeyParserTest, ParseKeyWithInvalidSerializationType) { + std::unique_ptr<KeyParser> parser = + absl::make_unique<KeyParserImpl<NoIdSerialization, NoIdKey>>( + "example_type_url", ParseNoIdKey); + + IdKeySerialization serialization(/*id=*/123); + util::StatusOr<std::unique_ptr<Key>> key = + parser->ParseKey(serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(KeyParserTest, ParseKeyWithInvalidObjectIdentifier) { + std::unique_ptr<KeyParser> parser = + absl::make_unique<KeyParserImpl<NoIdSerialization, NoIdKey>>( + "mismatched_type_url", ParseNoIdKey); + + NoIdSerialization serialization; + util::StatusOr<std::unique_ptr<Key>> key = + parser->ParseKey(serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/key_serializer.h b/cc/internal/key_serializer.h new file mode 100644 index 0000000..1d879a6 --- /dev/null +++ b/cc/internal/key_serializer.h
@@ -0,0 +1,92 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_KEY_SERIALIZER_H_ +#define TINK_INTERNAL_KEY_SERIALIZER_H_ + +#include <functional> +#include <memory> +#include <typeindex> +#include <utility> + +#include "absl/functional/function_ref.h" +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serializer_index.h" +#include "tink/key.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Non-template base class that can be used with internal registry map. +class KeySerializer { + public: + // Returns the serialization of `key`. + virtual util::StatusOr<std::unique_ptr<Serialization>> SerializeKey( + const Key& key, absl::optional<SecretKeyAccessToken> token) const = 0; + + // Returns an index that can be used to look up the `KeySerializer` + // object registered for the `KeyT` type in a registry. + virtual SerializerIndex Index() const = 0; + + virtual ~KeySerializer() = default; +}; + +// Serializes `KeyT` objects into `SerializationT` objects. +template <typename KeyT, typename SerializationT> +class KeySerializerImpl : public KeySerializer { + public: + // Creates a key serializer with serialization `function`. The referenced + // `function` should outlive the created key serializer object. + explicit KeySerializerImpl(absl::FunctionRef<util::StatusOr<SerializationT>( + KeyT, absl::optional<SecretKeyAccessToken>)> + function) + : function_(function) {} + + util::StatusOr<std::unique_ptr<Serialization>> SerializeKey( + const Key& key, + absl::optional<SecretKeyAccessToken> token) const override { + const KeyT* kt = dynamic_cast<const KeyT*>(&key); + if (kt == nullptr) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Invalid key type for this key serializer."); + } + util::StatusOr<SerializationT> serialization = function_(*kt, token); + if (!serialization.ok()) return serialization.status(); + return {absl::make_unique<SerializationT>(std::move(*serialization))}; + } + + SerializerIndex Index() const override { + return SerializerIndex::Create<KeyT, SerializationT>(); + } + + private: + std::function<util::StatusOr<SerializationT>( + KeyT, absl::optional<SecretKeyAccessToken>)> + function_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_KEY_SERIALIZER_H_
diff --git a/cc/internal/key_serializer_test.cc b/cc/internal/key_serializer_test.cc new file mode 100644 index 0000000..e67257e --- /dev/null +++ b/cc/internal/key_serializer_test.cc
@@ -0,0 +1,92 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/key_serializer.h" + +#include <memory> +#include <string_view> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serialization_test_util.h" +#include "tink/internal/serializer_index.h" +#include "tink/key.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::testing::Eq; + +TEST(KeySerializerTest, Create) { + std::unique_ptr<KeySerializer> serializer = + absl::make_unique<KeySerializerImpl<NoIdKey, NoIdSerialization>>( + SerializeNoIdKey); + + EXPECT_THAT(serializer->Index(), + Eq(SerializerIndex::Create<NoIdKey, NoIdSerialization>())); +} + +TEST(KeySerializerTest, SerializeKey) { + std::unique_ptr<KeySerializer> serializer = + absl::make_unique<KeySerializerImpl<NoIdKey, NoIdSerialization>>( + SerializeNoIdKey); + + NoIdKey key; + util::StatusOr<std::unique_ptr<Serialization>> serialization = + serializer->SerializeKey(key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); +} + +TEST(KeySerializerTest, SerializePublicKeyNoAccessToken) { + std::unique_ptr<KeySerializer> serializer = + absl::make_unique<KeySerializerImpl<NoIdKey, NoIdSerialization>>( + SerializeNoIdKey); + + NoIdKey public_key; + util::StatusOr<std::unique_ptr<Serialization>> serialization = + serializer->SerializeKey(public_key, absl::nullopt); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); +} + +TEST(KeySerializerTest, SerializeKeyWithInvalidKeyType) { + std::unique_ptr<KeySerializer> serializer = + absl::make_unique<KeySerializerImpl<NoIdKey, NoIdSerialization>>( + SerializeNoIdKey); + + IdKey key(/*id=*/123); + util::StatusOr<std::unique_ptr<Serialization>> serialization = + serializer->SerializeKey(key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/key_status_util.cc b/cc/internal/key_status_util.cc new file mode 100644 index 0000000..a28f418 --- /dev/null +++ b/cc/internal/key_status_util.cc
@@ -0,0 +1,76 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/key_status_util.h" + +#include <string> + +#include "absl/status/status.h" +#include "tink/key_status.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::google::crypto::tink::KeyStatusType; + +util::StatusOr<KeyStatus> FromKeyStatusType(KeyStatusType status_type) { + switch (status_type) { + case KeyStatusType::ENABLED: + return KeyStatus::kEnabled; + case KeyStatusType::DISABLED: + return KeyStatus::kDisabled; + case KeyStatusType::DESTROYED: + return KeyStatus::kDestroyed; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Invalid key status type."); + } +} + +util::StatusOr<KeyStatusType> ToKeyStatusType(KeyStatus status) { + switch (status) { + case KeyStatus::kEnabled: + return KeyStatusType::ENABLED; + case KeyStatus::kDisabled: + return KeyStatusType::DISABLED; + case KeyStatus::kDestroyed: + return KeyStatusType::DESTROYED; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Invalid key status."); + } +} + +std::string ToKeyStatusName(KeyStatus status) { + switch (status) { + case KeyStatus::kEnabled: + return KeyStatusType_Name(KeyStatusType::ENABLED); + case KeyStatus::kDisabled: + return KeyStatusType_Name(KeyStatusType::DISABLED); + case KeyStatus::kDestroyed: + return KeyStatusType_Name(KeyStatusType::DESTROYED); + default: + return KeyStatusType_Name(KeyStatusType::UNKNOWN_STATUS); + } +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/key_status_util.h b/cc/internal/key_status_util.h new file mode 100644 index 0000000..f9736cf --- /dev/null +++ b/cc/internal/key_status_util.h
@@ -0,0 +1,48 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_KEY_STATUS_UTIL_H_ +#define TINK_INTERNAL_KEY_STATUS_UTIL_H_ + +#include <string> + +#include "tink/key_status.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Returns `KeyStatus` C++ enum for a given `KeyStatusType` proto enum. If +// `status_type` is unrecognized (i.e., not handled), then an error is returned. +util::StatusOr<KeyStatus> FromKeyStatusType( + google::crypto::tink::KeyStatusType status_type); + +// Returns `KeyStatusType` proto enum for a given `KeyStatus` C++ enum. If +// `status` is unrecognized (i.e., not handled), then an error is returned. +util::StatusOr<google::crypto::tink::KeyStatusType> ToKeyStatusType( + KeyStatus status); + +// Returns a canonical name for a `KeyStatus` based on the corresponding +// `KeyStatusType` proto enum. +std::string ToKeyStatusName(KeyStatus status); + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_KEY_STATUS_UTIL_H_
diff --git a/cc/internal/key_status_util_test.cc b/cc/internal/key_status_util_test.cc new file mode 100644 index 0000000..6ac0933 --- /dev/null +++ b/cc/internal/key_status_util_test.cc
@@ -0,0 +1,79 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/key_status_util.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/key_status.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyStatusType; + +TEST(KeyStatusUtilTest, FromKeyStatusType) { + util::StatusOr<KeyStatus> enabled = FromKeyStatusType(KeyStatusType::ENABLED); + EXPECT_THAT(enabled, IsOkAndHolds(KeyStatus::kEnabled)); + + util::StatusOr<KeyStatus> disabled = + FromKeyStatusType(KeyStatusType::DISABLED); + EXPECT_THAT(disabled, IsOkAndHolds(KeyStatus::kDisabled)); + + util::StatusOr<KeyStatus> destroyed = + FromKeyStatusType(KeyStatusType::DESTROYED); + EXPECT_THAT(destroyed, IsOkAndHolds(KeyStatus::kDestroyed)); + + util::StatusOr<KeyStatus> unknown = + FromKeyStatusType(KeyStatusType::UNKNOWN_STATUS); + EXPECT_THAT(unknown.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(KeyStatusUtilTest, ToKeyStatusType) { + util::StatusOr<KeyStatusType> enabled = ToKeyStatusType(KeyStatus::kEnabled); + EXPECT_THAT(enabled, IsOkAndHolds(KeyStatusType::ENABLED)); + + util::StatusOr<KeyStatusType> disabled = + ToKeyStatusType(KeyStatus::kDisabled); + EXPECT_THAT(disabled, IsOkAndHolds(KeyStatusType::DISABLED)); + + util::StatusOr<KeyStatusType> destroyed = + ToKeyStatusType(KeyStatus::kDestroyed); + EXPECT_THAT(destroyed, IsOkAndHolds(KeyStatusType::DESTROYED)); + + util::StatusOr<KeyStatusType> unknown = ToKeyStatusType( + KeyStatus::kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements); + EXPECT_THAT(unknown.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(KeyStatusUtilTest, ToKeyStatusName) { + EXPECT_EQ(ToKeyStatusName(KeyStatus::kEnabled), "ENABLED"); + EXPECT_EQ(ToKeyStatusName(KeyStatus::kDisabled), "DISABLED"); + EXPECT_EQ(ToKeyStatusName(KeyStatus::kDestroyed), "DESTROYED"); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/key_type_info_store.cc b/cc/internal/key_type_info_store.cc new file mode 100644 index 0000000..5d041ff --- /dev/null +++ b/cc/internal/key_type_info_store.cc
@@ -0,0 +1,64 @@ +// Copyright 2018 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/key_type_info_store.h" + +#include <memory> +#include <typeindex> +#include <utility> + +#include "absl/status/status.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +util::StatusOr<KeyTypeInfoStore::Info*> KeyTypeInfoStore::Get( + absl::string_view type_url) const { + auto it = type_url_to_info_.find(type_url); + if (it == type_url_to_info_.end()) { + return ToStatusF(absl::StatusCode::kNotFound, + "No manager for type '%s' has been registered.", type_url); + } + return it->second.get(); +} + +util::Status KeyTypeInfoStore::IsInsertable( + absl::string_view type_url, const std::type_index& key_manager_type_index, + bool new_key_allowed) const { + auto it = type_url_to_info_.find(type_url); + if (it == type_url_to_info_.end()) { + return crypto::tink::util::OkStatus(); + } + if (it->second->key_manager_type_index() != key_manager_type_index) { + return ToStatusF(absl::StatusCode::kAlreadyExists, + "A manager for type '%s' has been already registered.", + type_url); + } + if (!it->second->new_key_allowed() && new_key_allowed) { + return ToStatusF(absl::StatusCode::kAlreadyExists, + "A manager for type '%s' has been already registered " + "with forbidden new key operation.", + type_url); + } + return util::OkStatus(); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/key_type_info_store.h b/cc/internal/key_type_info_store.h new file mode 100644 index 0000000..603fb00 --- /dev/null +++ b/cc/internal/key_type_info_store.h
@@ -0,0 +1,425 @@ +// Copyright 2018 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_KEY_TYPE_INFO_STORE_H_ +#define TINK_INTERNAL_KEY_TYPE_INFO_STORE_H_ + +#include <atomic> +#include <functional> +#include <initializer_list> +#include <memory> +#include <string> +#include <typeindex> +#include <utility> + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_join.h" +#include "tink/core/key_manager_impl.h" +#include "tink/core/key_type_manager.h" +#include "tink/core/private_key_manager_impl.h" +#include "tink/core/private_key_type_manager.h" +#include "tink/internal/fips_utils.h" +#include "tink/key_manager.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Stores information about key types constructed from their KeyTypeManager or +// KeyManager. This is used by the Configuration and Registry classes. +// +// Once inserted, Info objects must remain valid for the lifetime of the +// KeyTypeInfoStore object, and the Info object's pointer stability is required. +// Elements in Info, which include the KeyTypeManager or KeyManager, must not +// be replaced. +// +// Example: +// KeyTypeInfoStore store; +// crypto::tink::util::Status status = +// store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), true); +// crypto::tink::util::StatusOr<KeyTypeInfoStore::Info*> info = +// store.Get(AesGcmKeyManager().get_key_type()); +class KeyTypeInfoStore { + public: + KeyTypeInfoStore() = default; + + // Movable, but not copyable. + KeyTypeInfoStore(KeyTypeInfoStore&& other) = default; + KeyTypeInfoStore& operator=(KeyTypeInfoStore&& other) = default; + + // Information about a key type constructed from its KeyTypeManager or + // KeyManager. + class Info { + public: + // Takes ownership of `manager`. + template <typename KeyProto, typename KeyFormatProto, + typename... Primitives> + Info(KeyTypeManager<KeyProto, KeyFormatProto, List<Primitives...>>* manager, + bool new_key_allowed) + : key_manager_type_index_(std::type_index(typeid(*manager))), + public_key_type_manager_type_index_(absl::nullopt), + new_key_allowed_(new_key_allowed), + key_type_manager_(absl::WrapUnique(manager)), + internal_key_factory_( + absl::make_unique<internal::KeyFactoryImpl<KeyTypeManager< + KeyProto, KeyFormatProto, List<Primitives...>>>>(manager)), + key_factory_(internal_key_factory_.get()), + key_deriver_(CreateDeriverFunctionFor(manager)) { + // TODO(C++17): Replace with a fold expression. + (void)std::initializer_list<int>{ + 0, (primitive_to_manager_.emplace( + std::type_index(typeid(Primitives)), + internal::MakeKeyManager<Primitives>(manager)), + 0)...}; + } + + // Takes ownership of `private_manager`, but not of `public_manager`, which + // must only be alive for the duration of the constructor. + template <typename PrivateKeyProto, typename KeyFormatProto, + typename PublicKeyProto, typename PublicPrimitivesList, + typename... PrivatePrimitives> + Info(PrivateKeyTypeManager<PrivateKeyProto, KeyFormatProto, PublicKeyProto, + List<PrivatePrimitives...>>* private_manager, + KeyTypeManager<PublicKeyProto, void, PublicPrimitivesList>* + public_manager, + bool new_key_allowed) + : key_manager_type_index_(std::type_index(typeid(*private_manager))), + public_key_type_manager_type_index_( + std::type_index(typeid(*public_manager))), + new_key_allowed_(new_key_allowed), + key_type_manager_(absl::WrapUnique(private_manager)), + internal_key_factory_( + absl::make_unique<internal::PrivateKeyFactoryImpl< + PrivateKeyProto, KeyFormatProto, PublicKeyProto, + List<PrivatePrimitives...>, PublicPrimitivesList>>( + private_manager, public_manager)), + key_factory_(internal_key_factory_.get()), + key_deriver_(CreateDeriverFunctionFor(private_manager)) { + // TODO(C++17): Replace with a fold expression. + (void)std::initializer_list<int>{ + 0, (primitive_to_manager_.emplace( + std::type_index(typeid(PrivatePrimitives)), + internal::MakePrivateKeyManager<PrivatePrimitives>( + private_manager, public_manager)), + 0)...}; + } + + // Takes ownership of `manager`. KeyManager is the legacy/internal version + // of KeyTypeManager. + template <typename P> + Info(KeyManager<P>* manager, bool new_key_allowed) + : key_manager_type_index_(std::type_index(typeid(*manager))), + public_key_type_manager_type_index_(absl::nullopt), + new_key_allowed_(new_key_allowed), + key_type_manager_(nullptr), + internal_key_factory_(nullptr), + key_factory_(&manager->get_key_factory()) { + primitive_to_manager_.emplace(std::type_index(typeid(P)), + absl::WrapUnique(manager)); + } + + template <typename P> + crypto::tink::util::StatusOr<const KeyManager<P>*> get_key_manager( + absl::string_view requested_type_url) const { + auto it = primitive_to_manager_.find(std::type_index(typeid(P))); + if (it == primitive_to_manager_.end()) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat( + "Primitive type ", typeid(P).name(), + " not among supported primitives ", + absl::StrJoin( + primitive_to_manager_.begin(), primitive_to_manager_.end(), + ", ", + [](std::string* out, + const std::pair<const std::type_index, + std::unique_ptr<KeyManagerBase>>& kv) { + absl::StrAppend(out, kv.first.name()); + }), + " for type URL ", requested_type_url)); + } + return static_cast<const KeyManager<P>*>(it->second.get()); + } + + const std::type_index& key_manager_type_index() const { + return key_manager_type_index_; + } + + const absl::optional<std::type_index>& public_key_type_manager_type_index() + const { + return public_key_type_manager_type_index_; + } + + bool new_key_allowed() const { return new_key_allowed_.load(); } + + void set_new_key_allowed(bool b) { new_key_allowed_.store(b); } + + const KeyFactory& key_factory() const { return *key_factory_; } + + const std::function<crypto::tink::util::StatusOr< + google::crypto::tink::KeyData>(absl::string_view, InputStream*)>& + key_deriver() const { + return key_deriver_; + } + + private: + // Dynamic type_index of the KeyManager or KeyTypeManager for this key type. + std::type_index key_manager_type_index_; + // Dynamic type_index of the public KeyTypeManager for this key type when + // inserted into the registry via RegisterAsymmetricKeyManagers. Otherwise, + // nullopt. + absl::optional<std::type_index> public_key_type_manager_type_index_; + // Whether the key manager allows the creation of new keys. + std::atomic<bool> new_key_allowed_; + + // Map from primitive type_index to KeyManager. + absl::flat_hash_map<std::type_index, std::unique_ptr<KeyManagerBase>> + primitive_to_manager_; + // Key type manager. Equals nullptr if Info was constructed from a + // KeyManager. + const std::shared_ptr<void> key_type_manager_; + + // Key factory. Equals nullptr if Info was constructed from a KeyManager. + std::unique_ptr<const KeyFactory> internal_key_factory_; + // Unowned version of `internal_key_factory_` if Info was constructed from a + // KeyTypeManager. Key factory belonging to the KeyManager if Info was + // constructed from a KeyManager. + const KeyFactory* key_factory_; + + // Derives a key if Info was constructed from a KeyTypeManager with a + // non-void KeyFormat type. Else, this function is empty and casting to a + // bool returns false. + std::function<crypto::tink::util::StatusOr<google::crypto::tink::KeyData>( + absl::string_view, InputStream*)> + key_deriver_; + }; + + // Adds a crypto::tink::KeyTypeManager to KeyTypeInfoStore. `new_key_allowed` + // indicates whether `manager` may create new keys. + template <class KeyTypeManager> + crypto::tink::util::Status AddKeyTypeManager( + std::unique_ptr<KeyTypeManager> manager, bool new_key_allowed); + + // Adds a pair of crypto::tink::PrivateKeyTypeManager and + // crypto::tink::KeyTypeManager to KeyTypeInfoStore. `new_key_allowed` + // indicates whether `private_manager` may create new keys. + template <class PrivateKeyTypeManager, class PublicKeyTypeManager> + crypto::tink::util::Status AddAsymmetricKeyTypeManagers( + std::unique_ptr<PrivateKeyTypeManager> private_manager, + std::unique_ptr<PublicKeyTypeManager> public_manager, + bool new_key_allowed); + + // Adds a crypto::tink::KeyManager to KeyTypeInfoStore. `new_key_allowed` + // indicates whether `manager` may create new keys. KeyManager is the + // legacy/internal version of KeyTypeManager. + template <class P> + crypto::tink::util::Status AddKeyManager( + std::unique_ptr<KeyManager<P>> manager, bool new_key_allowed); + + // Gets Info associated with `type_url`, returning either a valid, non-null + // Info or an error. + crypto::tink::util::StatusOr<Info*> Get(absl::string_view type_url) const; + + bool IsEmpty() const { return type_url_to_info_.empty(); } + + private: + // Whether a key manager with `type_url` and `key_manager_type_index` can be + // inserted. + crypto::tink::util::Status IsInsertable( + absl::string_view type_url, const std::type_index& key_manager_type_index, + bool new_key_allowed) const; + + void Add(std::string type_url, std::unique_ptr<Info> info, + bool new_key_allowed) { + auto it = type_url_to_info_.find(type_url); + if (it != type_url_to_info_.end()) { + it->second->set_new_key_allowed(new_key_allowed); + } else { + type_url_to_info_.insert({type_url, std::move(info)}); + } + } + + // Map from the type_url to Info. + // Elements in Info must not be replaced, and pointer stability is required + // for `Get()`. + absl::flat_hash_map<std::string, std::unique_ptr<Info>> type_url_to_info_; +}; + +template <class P> +crypto::tink::util::Status KeyTypeInfoStore::AddKeyManager( + std::unique_ptr<KeyManager<P>> manager, bool new_key_allowed) { + std::string type_url = manager->get_key_type(); + if (!manager->DoesSupport(type_url)) { + return ToStatusF(absl::StatusCode::kInvalidArgument, + "The manager does not support type '%s'.", type_url); + } + + crypto::tink::util::Status status = IsInsertable( + type_url, std::type_index(typeid(*manager)), new_key_allowed); + if (!status.ok()) { + return status; + } + + auto info = absl::make_unique<Info>(manager.release(), new_key_allowed); + Add(type_url, std::move(info), new_key_allowed); + return crypto::tink::util::OkStatus(); +} + +template <class KeyTypeManager> +crypto::tink::util::Status KeyTypeInfoStore::AddKeyTypeManager( + std::unique_ptr<KeyTypeManager> manager, bool new_key_allowed) { + // Check FIPS status. + internal::FipsCompatibility fips_compatible = manager->FipsStatus(); + auto fips_status = internal::ChecksFipsCompatibility(fips_compatible); + if (!fips_status.ok()) { + return crypto::tink::util::Status( + absl::StatusCode::kInternal, + absl::StrCat("Failed registering the key manager for ", + typeid(*manager).name(), + " as it is not FIPS compatible: ", fips_status.message())); + } + + std::string type_url = manager->get_key_type(); + crypto::tink::util::Status status = IsInsertable( + type_url, std::type_index(typeid(*manager)), new_key_allowed); + if (!status.ok()) { + return status; + } + + auto info = absl::make_unique<Info>(manager.release(), new_key_allowed); + Add(type_url, std::move(info), new_key_allowed); + return crypto::tink::util::OkStatus(); +} + +template <class PrivateKeyTypeManager, class PublicKeyTypeManager> +crypto::tink::util::Status KeyTypeInfoStore::AddAsymmetricKeyTypeManagers( + std::unique_ptr<PrivateKeyTypeManager> private_manager, + std::unique_ptr<PublicKeyTypeManager> public_manager, + bool new_key_allowed) { + std::string private_type_url = private_manager->get_key_type(); + std::string public_type_url = public_manager->get_key_type(); + if (private_type_url == public_type_url) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + "Passed in key managers must have different get_key_type() results."); + } + + // Check FIPS status. + auto private_fips_status = + internal::ChecksFipsCompatibility(private_manager->FipsStatus()); + if (!private_fips_status.ok()) { + return crypto::tink::util::Status( + absl::StatusCode::kInternal, + absl::StrCat( + "Failed registering the key manager for ", + typeid(*private_manager).name(), + " as it is not FIPS compatible: ", private_fips_status.message())); + } + auto public_fips_status = + internal::ChecksFipsCompatibility(public_manager->FipsStatus()); + if (!public_fips_status.ok()) { + return crypto::tink::util::Status( + absl::StatusCode::kInternal, + absl::StrCat( + "Failed registering the key manager for ", + typeid(*public_manager).name(), + " as it is not FIPS compatible: ", public_fips_status.message())); + } + + crypto::tink::util::Status private_status = + IsInsertable(private_type_url, std::type_index(typeid(*private_manager)), + new_key_allowed); + if (!private_status.ok()) { + return private_status; + } + crypto::tink::util::Status public_status = + IsInsertable(public_type_url, std::type_index(typeid(*public_manager)), + new_key_allowed); + if (!public_status.ok()) { + return public_status; + } + + util::StatusOr<KeyTypeInfoStore::Info*> private_found = Get(private_type_url); + util::StatusOr<const KeyTypeInfoStore::Info*> public_found = + Get(public_type_url); + + // Only one of the private and public key type managers is found. + if (private_found.ok() && !public_found.ok()) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat( + "Private key manager corresponding to ", + typeid(*private_manager).name(), + " was previously registered, but key manager corresponding to ", + typeid(*public_manager).name(), + " was not, so it's impossible to register them jointly")); + } + if (!private_found.ok() && public_found.ok()) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Key manager corresponding to ", + typeid(*public_manager).name(), + " was previously registered, but private key manager " + "corresponding to ", + typeid(*private_manager).name(), + " was not, so it's impossible to register them jointly")); + } + + // Both private and public key type managers are found. + if (private_found.ok() && public_found.ok()) { + if (!(*private_found)->public_key_type_manager_type_index().has_value()) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("private key manager corresponding to ", + typeid(*private_manager).name(), + " is already registered without public key manager, " + "cannot be re-registered with public key manager. ")); + } + if ((*private_found)->public_key_type_manager_type_index() != + std::type_index(typeid(*public_manager))) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat( + "private key manager corresponding to ", + typeid(*private_manager).name(), " is already registered with ", + (*private_found)->public_key_type_manager_type_index()->name(), + ", cannot be re-registered with ", + typeid(*public_manager).name())); + } + // Since `private_manager` passed the `IsInsertable` check above, the + // `set_new_key_allowed` operation is permissible. + (*private_found)->set_new_key_allowed(new_key_allowed); + return crypto::tink::util::OkStatus(); + } + + // Both private and public key type managers were not found. + auto private_info = absl::make_unique<Info>( + private_manager.release(), public_manager.get(), new_key_allowed); + Add(private_type_url, std::move(private_info), new_key_allowed); + // TODO(b/265705174): Store public key type managers in an asymmetric pair + // with new_key_allowed = false. + auto public_info = + absl::make_unique<Info>(public_manager.release(), new_key_allowed); + Add(public_type_url, std::move(public_info), new_key_allowed); + + return crypto::tink::util::OkStatus(); +} + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_KEY_TYPE_INFO_STORE_H_
diff --git a/cc/internal/key_type_info_store_test.cc b/cc/internal/key_type_info_store_test.cc new file mode 100644 index 0000000..3768ac9 --- /dev/null +++ b/cc/internal/key_type_info_store_test.cc
@@ -0,0 +1,443 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/key_type_info_store.h" + +#include <memory> +#include <string> +#include <typeindex> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/aead.h" +#include "tink/aead/aes_gcm_key_manager.h" +#include "tink/aead/cord_aead.h" +#include "tink/aead/kms_envelope_aead_key_manager.h" +#include "tink/core/key_manager_impl.h" +#include "tink/internal/fips_utils.h" +#include "tink/key_manager.h" +#include "tink/signature/ecdsa_sign_key_manager.h" +#include "tink/signature/ecdsa_verify_key_manager.h" +#include "tink/util/test_matchers.h" +#include "proto/aes_gcm.pb.h" +#include "proto/common.pb.h" +#include "proto/ecdsa.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesGcmKeyFormat; +using ::google::crypto::tink::EcdsaKeyFormat; +using ::google::crypto::tink::EcdsaParams; +using ::google::crypto::tink::EcdsaSignatureEncoding; +using ::google::crypto::tink::EllipticCurveType; +using ::google::crypto::tink::HashType; + +// TODO(b/265705174): Use fake key managers to avoid relying on key manager +// implementations. +TEST(KeyTypeInfoStoreTest, AddKeyTypeManager) { + KeyTypeInfoStore store; + ASSERT_THAT(store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + std::string type_url = AesGcmKeyManager().get_key_type(); + util::StatusOr<KeyTypeInfoStore::Info*> info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + EXPECT_EQ((*info)->new_key_allowed(), true); + + util::StatusOr<const KeyManager<Aead>*> manager = + (*info)->get_key_manager<Aead>(type_url); + ASSERT_THAT(manager, IsOk()); + EXPECT_EQ((*manager)->get_key_type(), type_url); +} + +TEST(KeyTypeInfoStoreTest, AddKeyTypeManagerNoBoringCrypto) { + if (!kUseOnlyFips || IsFipsEnabledInSsl()) { + GTEST_SKIP() + << "Only supported in FIPS-mode with BoringCrypto not available."; + } + KeyTypeInfoStore store; + EXPECT_THAT( + store.AddKeyTypeManager(absl::make_unique<KmsEnvelopeAeadKeyManager>(), + /*new_key_allowed=*/true), + StatusIs(absl::StatusCode::kInternal)); +} + +TEST(KeyTypeInfoStoreTest, AddKeyTypeManagerAndChangeNewKeyAllowed) { + KeyTypeInfoStore store; + ASSERT_THAT(store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + std::string type_url = AesGcmKeyManager().get_key_type(); + util::StatusOr<KeyTypeInfoStore::Info*> info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + EXPECT_EQ((*info)->new_key_allowed(), true); + + // new_key_allowed true -> true is allowed. + ASSERT_THAT(store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + EXPECT_EQ((*info)->new_key_allowed(), true); + + // new_key_allowed true -> false is allowed. + ASSERT_THAT(store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/false), + IsOk()); + info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + EXPECT_EQ((*info)->new_key_allowed(), false); + + // new_key_allowed false -> false is allowed. + ASSERT_THAT(store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/false), + IsOk()); + info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + EXPECT_EQ((*info)->new_key_allowed(), false); + + // new_key_allowed false -> true is not allowed. + ASSERT_THAT(store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(KeyTypeInfoStoreTest, AddAsymmetricKeyTypeManagers) { + KeyTypeInfoStore store; + ASSERT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + { + std::string private_type_url = EcdsaSignKeyManager().get_key_type(); + util::StatusOr<KeyTypeInfoStore::Info*> info = store.Get(private_type_url); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<const KeyManager<PublicKeySign>*> manager = + (*info)->get_key_manager<PublicKeySign>(private_type_url); + ASSERT_THAT(manager, IsOk()); + EXPECT_EQ((*manager)->get_key_type(), private_type_url); + } + { + std::string public_type_url = EcdsaVerifyKeyManager().get_key_type(); + util::StatusOr<KeyTypeInfoStore::Info*> info = store.Get(public_type_url); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<const KeyManager<PublicKeyVerify>*> manager = + (*info)->get_key_manager<PublicKeyVerify>(public_type_url); + ASSERT_THAT(manager, IsOk()); + EXPECT_EQ((*manager)->get_key_type(), public_type_url); + } +} + +TEST(KeyTypeInfoStoreTest, AddAsymmetricKeyTypeManagersAlreadyExists) { + { + KeyTypeInfoStore store; + ASSERT_THAT( + store.AddKeyTypeManager(absl::make_unique<EcdsaSignKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + EXPECT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/true), + StatusIs(absl::StatusCode::kInvalidArgument)); + } + { + KeyTypeInfoStore store; + ASSERT_THAT( + store.AddKeyTypeManager(absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + EXPECT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/true), + StatusIs(absl::StatusCode::kInvalidArgument)); + } + { + KeyTypeInfoStore store; + EXPECT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + EXPECT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + } +} + +TEST(KeyTypeInfoStoreTest, AddAsymmetricKeyTypeManagersAndChangeNewKeyAllowed) { + KeyTypeInfoStore store; + ASSERT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + std::string private_type_url = EcdsaSignKeyManager().get_key_type(); + std::string public_type_url = EcdsaVerifyKeyManager().get_key_type(); + + util::StatusOr<KeyTypeInfoStore::Info*> private_info = + store.Get(private_type_url); + ASSERT_THAT(private_info, IsOk()); + EXPECT_EQ((*private_info)->new_key_allowed(), true); + util::StatusOr<KeyTypeInfoStore::Info*> public_info = + store.Get(public_type_url); + ASSERT_THAT(public_info, IsOk()); + EXPECT_EQ((*public_info)->new_key_allowed(), true); + + // new_key_allowed true -> true is allowed. + ASSERT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + private_info = store.Get(private_type_url); + ASSERT_THAT(private_info, IsOk()); + EXPECT_EQ((*private_info)->new_key_allowed(), true); + public_info = store.Get(public_type_url); + ASSERT_THAT(public_info, IsOk()); + EXPECT_EQ((*public_info)->new_key_allowed(), true); + + // new_key_allowed true -> false is allowed. + ASSERT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/false), + IsOk()); + private_info = store.Get(private_type_url); + ASSERT_THAT(private_info, IsOk()); + EXPECT_EQ((*private_info)->new_key_allowed(), false); + public_info = store.Get(public_type_url); + ASSERT_THAT(public_info, IsOk()); + EXPECT_EQ((*public_info)->new_key_allowed(), true); + + // new_key_allowed false -> false is allowed. + ASSERT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/false), + IsOk()); + private_info = store.Get(private_type_url); + ASSERT_THAT(private_info, IsOk()); + EXPECT_EQ((*private_info)->new_key_allowed(), false); + public_info = store.Get(public_type_url); + ASSERT_THAT(public_info, IsOk()); + EXPECT_EQ((*public_info)->new_key_allowed(), true); + + // new_key_allowed false -> true is not allowed. + ASSERT_THAT(store.AddAsymmetricKeyTypeManagers( + absl::make_unique<EcdsaSignKeyManager>(), + absl::make_unique<EcdsaVerifyKeyManager>(), + /*new_key_allowed=*/true), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(KeyTypeInfoStoreTest, AddKeyManager) { + KeyTypeInfoStore store; + AesGcmKeyManager manager; + ASSERT_THAT(store.AddKeyManager(MakeKeyManager<Aead>(&manager), + /*new_key_allowed=*/true), + IsOk()); + + std::string type_url = manager.get_key_type(); + util::StatusOr<KeyTypeInfoStore::Info*> info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + + util::StatusOr<const KeyManager<Aead>*> got_manager = + (*info)->get_key_manager<Aead>(type_url); + ASSERT_THAT(got_manager, IsOk()); + EXPECT_EQ((*got_manager)->get_key_type(), type_url); +} + +TEST(KeyTypeInfoStoreTest, AddKeyManagerAndChangeNewKeyAllowed) { + KeyTypeInfoStore store; + AesGcmKeyManager manager; + ASSERT_THAT(store.AddKeyManager(MakeKeyManager<Aead>(&manager), + /*new_key_allowed=*/true), + IsOk()); + + std::string type_url = manager.get_key_type(); + util::StatusOr<KeyTypeInfoStore::Info*> info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + EXPECT_EQ((*info)->new_key_allowed(), true); + + // new_key_allowed true -> true is allowed. + ASSERT_THAT(store.AddKeyManager(MakeKeyManager<Aead>(&manager), + /*new_key_allowed=*/true), + IsOk()); + info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + EXPECT_EQ((*info)->new_key_allowed(), true); + + // new_key_allowed true -> false is allowed. + ASSERT_THAT(store.AddKeyManager(MakeKeyManager<Aead>(&manager), + /*new_key_allowed=*/false), + IsOk()); + info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + EXPECT_EQ((*info)->new_key_allowed(), false); + + // new_key_allowed false -> false is allowed. + ASSERT_THAT(store.AddKeyManager(MakeKeyManager<Aead>(&manager), + /*new_key_allowed=*/false), + IsOk()); + info = store.Get(type_url); + ASSERT_THAT(info, IsOk()); + EXPECT_EQ((*info)->new_key_allowed(), false); + + // new_key_allowed false -> true is not allowed. + ASSERT_THAT(store.AddKeyManager(MakeKeyManager<Aead>(&manager), + /*new_key_allowed=*/true), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(KeyTypeInfoStoreTest, Get) { + KeyTypeInfoStore store; + ASSERT_THAT(store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + util::StatusOr<KeyTypeInfoStore::Info*> info = + store.Get(AesGcmKeyManager().get_key_type()); + EXPECT_THAT(info, IsOk()); + + EXPECT_THAT(store.Get("nonexistent.type.url").status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(KeyTypeInfoStoreTest, IsEmpty) { + KeyTypeInfoStore store; + EXPECT_EQ(store.IsEmpty(), true); + + ASSERT_THAT(store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + EXPECT_THAT(store.IsEmpty(), false); +} + +TEST(KeyTypeInfoStoreInfoTest, ConstructWithKeyTypeManager) { + KeyTypeInfoStore::Info info(absl::make_unique<AesGcmKeyManager>().release(), + /*new_key_allowed=*/false); + + EXPECT_EQ(info.key_manager_type_index(), + std::type_index(typeid(AesGcmKeyManager))); + EXPECT_EQ(info.public_key_type_manager_type_index(), absl::nullopt); + + EXPECT_EQ(info.new_key_allowed(), false); + info.set_new_key_allowed(true); + EXPECT_EQ(info.new_key_allowed(), true); + + std::string type_url = AesGcmKeyManager().get_key_type(); + util::StatusOr<const KeyManager<Aead>*> aead_manager = + info.get_key_manager<Aead>(type_url); + ASSERT_THAT(aead_manager, IsOk()); + EXPECT_EQ((*aead_manager)->DoesSupport(type_url), true); + util::StatusOr<const KeyManager<CordAead>*> cord_aead_manager = + info.get_key_manager<CordAead>(type_url); + ASSERT_THAT(aead_manager, IsOk()); + EXPECT_EQ((*aead_manager)->DoesSupport(type_url), true); + + AesGcmKeyFormat format; + format.set_key_size(32); + EXPECT_THAT(info.key_factory().NewKeyData(format.SerializeAsString()), + IsOk()); + + EXPECT_EQ((bool)info.key_deriver(), true); +} + +TEST(KeyTypeInfoStoreInfoTest, ConstructWithAsymmetricKeyTypeManagers) { + KeyTypeInfoStore::Info info( + absl::make_unique<EcdsaSignKeyManager>().release(), + absl::make_unique<EcdsaVerifyKeyManager>().get(), + /*new_key_allowed=*/false); + + EXPECT_EQ(info.key_manager_type_index(), + std::type_index(typeid(EcdsaSignKeyManager))); + EXPECT_EQ(info.public_key_type_manager_type_index(), + std::type_index(typeid(EcdsaVerifyKeyManager))); + + EXPECT_EQ(info.new_key_allowed(), false); + info.set_new_key_allowed(true); + EXPECT_EQ(info.new_key_allowed(), true); + + std::string type_url = EcdsaSignKeyManager().get_key_type(); + util::StatusOr<const KeyManager<PublicKeySign>*> manager = + info.get_key_manager<PublicKeySign>(type_url); + ASSERT_THAT(manager, IsOk()); + EXPECT_EQ((*manager)->DoesSupport(type_url), true); + + EcdsaKeyFormat format; + EcdsaParams* params = format.mutable_params(); + params->set_hash_type(HashType::SHA256); + params->set_curve(EllipticCurveType::NIST_P256); + params->set_encoding(EcdsaSignatureEncoding::DER); + EXPECT_THAT(info.key_factory().NewKeyData(format.SerializeAsString()), + IsOk()); + + EXPECT_EQ((bool)info.key_deriver(), true); +} + +TEST(KeyTypeInfoStoreInfoTest, ConstructWithKeyManager) { + AesGcmKeyManager key_type_manager; + std::unique_ptr<KeyManager<Aead>> manager = + MakeKeyManager<Aead>(&key_type_manager); + std::type_index type_index = std::type_index(typeid(*manager)); + KeyTypeInfoStore::Info info(manager.release(), + /*new_key_allowed=*/false); + + EXPECT_EQ(info.key_manager_type_index(), type_index); + EXPECT_EQ(info.public_key_type_manager_type_index(), absl::nullopt); + + EXPECT_EQ(info.new_key_allowed(), false); + info.set_new_key_allowed(true); + EXPECT_EQ(info.new_key_allowed(), true); + + std::string type_url = AesGcmKeyManager().get_key_type(); + util::StatusOr<const KeyManager<Aead>*> got_manager = + info.get_key_manager<Aead>(type_url); + ASSERT_THAT(got_manager, IsOk()); + EXPECT_EQ((*got_manager)->DoesSupport(type_url), true); + // Inserted KeyManager only supports Aead, not CordAead. + EXPECT_THAT(info.get_key_manager<CordAead>(type_url).status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + + AesGcmKeyFormat format; + format.set_key_size(32); + EXPECT_THAT(info.key_factory().NewKeyData(format.SerializeAsString()), + IsOk()); + + EXPECT_EQ((bool)info.key_deriver(), false); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/keyset_handle_builder_entry.cc b/cc/internal/keyset_handle_builder_entry.cc new file mode 100644 index 0000000..d407cc9 --- /dev/null +++ b/cc/internal/keyset_handle_builder_entry.cc
@@ -0,0 +1,216 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/keyset_handle_builder_entry.h" + +#include <memory> +#include <string> + +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/key_status_util.h" +#include "tink/internal/legacy_proto_key.h" +#include "tink/internal/legacy_proto_parameters.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/internal/serialization.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/registry.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeyStatusType; + +Keyset::Key ToKeysetKey(int id, KeyStatusType status, + const ProtoKeySerialization& serialization) { + KeyData key_data; + key_data.set_type_url(std::string(serialization.TypeUrl())); + // OSS proto library complains if serialized key is not converted to string. + key_data.set_value(std::string(serialization.SerializedKeyProto().GetSecret( + InsecureSecretKeyAccess::Get()))); + key_data.set_key_material_type(serialization.KeyMaterialType()); + Keyset::Key key; + key.set_status(status); + key.set_key_id(id); + key.set_output_prefix_type(serialization.GetOutputPrefixType()); + *key.mutable_key_data() = key_data; + return key; +} + +util::StatusOr<ProtoParametersSerialization> SerializeParameters( + const Parameters& params) { + util::StatusOr<std::unique_ptr<Serialization>> serialization = + MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<ProtoParametersSerialization>(params); + if (!serialization.ok()) return serialization.status(); + + const ProtoParametersSerialization* proto_serialization = + dynamic_cast<const ProtoParametersSerialization*>(serialization->get()); + if (proto_serialization == nullptr) { + return util::Status(absl::StatusCode::kInternal, + "Failed to serialize proto parameters."); + } + + return *proto_serialization; +} + +util::StatusOr<ProtoParametersSerialization> SerializeLegacyParameters( + const Parameters* params) { + const LegacyProtoParameters* proto_params = + dynamic_cast<const LegacyProtoParameters*>(params); + if (proto_params == nullptr) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to serialize legacy proto parameters."); + } + return proto_params->Serialization(); +} + +util::StatusOr<ProtoKeySerialization> SerializeKey(const Key& key) { + util::StatusOr<std::unique_ptr<Serialization>> serialization = + MutableSerializationRegistry::GlobalInstance() + .SerializeKey<ProtoKeySerialization>(key, + InsecureSecretKeyAccess::Get()); + if (!serialization.ok()) return serialization.status(); + + const ProtoKeySerialization* serialized_proto_key = + dynamic_cast<const ProtoKeySerialization*>(serialization->get()); + if (serialized_proto_key == nullptr) { + return util::Status(absl::StatusCode::kInternal, + "Failed to serialize proto key."); + } + + return *serialized_proto_key; +} + +util::StatusOr<ProtoKeySerialization> SerializeLegacyKey(const Key* key) { + const LegacyProtoKey* proto_key = dynamic_cast<const LegacyProtoKey*>(key); + if (proto_key == nullptr) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to serialize legacy proto key."); + } + util::StatusOr<const ProtoKeySerialization*> serialized_key = + proto_key->Serialization(InsecureSecretKeyAccess::Get()); + if (!serialized_key.ok()) return serialized_key.status(); + + return **serialized_key; +} + +util::StatusOr<Keyset::Key> CreateKeysetKeyFromProtoParametersSerialization( + const ProtoParametersSerialization& serialization, int id, + KeyStatusType status) { + util::StatusOr<std::unique_ptr<KeyData>> key_data = + Registry::NewKeyData(serialization.GetKeyTemplate()); + if (!key_data.ok()) return key_data.status(); + + Keyset::Key key; + key.set_status(status); + key.set_key_id(id); + key.set_output_prefix_type( + serialization.GetKeyTemplate().output_prefix_type()); + *key.mutable_key_data() = **key_data; + return key; +} + +util::StatusOr<Keyset::Key> CreateKeysetKeyFromProtoKeySerialization( + const ProtoKeySerialization& key, int id, KeyStatusType status) { + absl::optional<int> id_requirement = key.IdRequirement(); + if (id_requirement.has_value() && *id_requirement != id) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong ID set for key with ID requirement."); + } + return ToKeysetKey(id, status, key); +} + +} // namespace + +void KeysetHandleBuilderEntry::SetFixedId(int id) { + strategy_.strategy = KeyIdStrategyEnum::kFixedId; + strategy_.id_requirement = id; +} + +void KeysetHandleBuilderEntry::SetRandomId() { + strategy_.strategy = KeyIdStrategyEnum::kRandomId; + strategy_.id_requirement = absl::nullopt; +} + +util::StatusOr<Keyset::Key> KeyEntry::CreateKeysetKey(int id) { + util::StatusOr<KeyStatusType> key_status = ToKeyStatusType(key_status_); + if (!key_status.ok()) return key_status.status(); + + if (GetKeyIdRequirement().has_value() && GetKeyIdRequirement() != id) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Requested id does not match id requirement."); + } + + util::StatusOr<ProtoKeySerialization> serialization = SerializeKey(*key_); + if (!serialization.ok() && + serialization.status().code() != absl::StatusCode::kNotFound) { + return serialization.status(); + } + + if (serialization.status().code() == absl::StatusCode::kNotFound) { + // Fallback to legacy proto key. + serialization = SerializeLegacyKey(key_.get()); + if (!serialization.ok()) return serialization.status(); + } + + return CreateKeysetKeyFromProtoKeySerialization(*serialization, id, + *key_status); +} + +util::StatusOr<Keyset::Key> ParametersEntry::CreateKeysetKey(int id) { + util::StatusOr<KeyStatusType> key_status = ToKeyStatusType(key_status_); + if (!key_status.ok()) return key_status.status(); + + if (GetKeyIdRequirement().has_value() && GetKeyIdRequirement() != id) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Requested id does not match id requirement."); + } + + util::StatusOr<ProtoParametersSerialization> serialization = + SerializeParameters(*parameters_); + if (!serialization.ok() && + serialization.status().code() != absl::StatusCode::kNotFound) { + return serialization.status(); + } + + if (serialization.status().code() == absl::StatusCode::kNotFound) { + // Fallback to legacy proto parameters. + serialization = SerializeLegacyParameters(parameters_.get()); + if (!serialization.ok()) return serialization.status(); + } + + return CreateKeysetKeyFromProtoParametersSerialization(*serialization, id, + *key_status); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/keyset_handle_builder_entry.h b/cc/internal/keyset_handle_builder_entry.h new file mode 100644 index 0000000..a1d0a6c --- /dev/null +++ b/cc/internal/keyset_handle_builder_entry.h
@@ -0,0 +1,132 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_KEYSET_HANDLE_BUILDER_ENTRY_H_ +#define TINK_INTERNAL_KEYSET_HANDLE_BUILDER_ENTRY_H_ + +#include <memory> +#include <utility> + +#include "absl/types/optional.h" +#include "tink/key.h" +#include "tink/key_status.h" +#include "tink/parameters.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +enum class KeyIdStrategyEnum : int { + kFixedId = 1, + kRandomId = 2, + // Added to guard from failures that may be caused by future expansions. + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements = 20, +}; + +struct KeyIdStrategy { + KeyIdStrategyEnum strategy; + absl::optional<int> id_requirement; +}; + +// Internal keyset handle builder entry. The public keyset handle builder +// entry will delegate its method calls to an instance of this class. +class KeysetHandleBuilderEntry { + public: + KeysetHandleBuilderEntry() = default; + virtual ~KeysetHandleBuilderEntry() = default; + + // Sets the key `status` of this entry. + void SetStatus(KeyStatus status) { key_status_ = status; } + // Returns key status of this entry. + KeyStatus GetStatus() const { return key_status_; } + + // Assigns a fixed `id` when this keyset is built. + void SetFixedId(int id); + // Assigns an unused random id when this keyset is built. + void SetRandomId(); + + // Sets this entry as the primary key. + void SetPrimary() { is_primary_ = true; } + // Unsets this entry as the primary key. + void UnsetPrimary() { is_primary_ = false; } + // Returns whether or not this entry has been marked as a primary. + bool IsPrimary() const { return is_primary_; } + + // Returns key id strategy. + KeyIdStrategy GetKeyIdStrategy() { return strategy_; } + // Returns key id strategy enum. + KeyIdStrategyEnum GetKeyIdStrategyEnum() { return strategy_.strategy; } + // Returns key id requirement. + absl::optional<int> GetKeyIdRequirement() { return strategy_.id_requirement; } + + // Creates a Keyset::Key proto with the specified key `id` from either a + // `Key` object or a `Parameters` object. + virtual crypto::tink::util::StatusOr<google::crypto::tink::Keyset::Key> + CreateKeysetKey(int id) = 0; + + protected: + KeyStatus key_status_ = KeyStatus::kDisabled; + + private: + bool is_primary_ = false; + KeyIdStrategy strategy_ = + KeyIdStrategy{KeyIdStrategyEnum::kRandomId, absl::nullopt}; +}; + +// Internal keyset handle builder entry constructed from a `Key` object. +class KeyEntry : public KeysetHandleBuilderEntry { + public: + // Movable, but not copyable. + KeyEntry(KeyEntry&& other) = default; + KeyEntry& operator=(KeyEntry&& other) = default; + KeyEntry(const KeyEntry& other) = delete; + KeyEntry& operator=(const KeyEntry& other) = delete; + + explicit KeyEntry(std::shared_ptr<const Key> key) : key_(std::move(key)) {} + + crypto::tink::util::StatusOr<google::crypto::tink::Keyset::Key> + CreateKeysetKey(int id) override; + + private: + std::shared_ptr<const Key> key_; +}; + +// Internal keyset handle builder entry constructed from a `Parameters` object. +class ParametersEntry : public KeysetHandleBuilderEntry { + public: + // Movable, but not copyable. + ParametersEntry(ParametersEntry&& other) = default; + ParametersEntry& operator=(ParametersEntry&& other) = default; + ParametersEntry(const ParametersEntry& other) = delete; + ParametersEntry& operator=(const ParametersEntry& other) = delete; + + explicit ParametersEntry(std::shared_ptr<const Parameters> parameters) + : parameters_(std::move(parameters)) {} + + crypto::tink::util::StatusOr<google::crypto::tink::Keyset::Key> + CreateKeysetKey(int id) override; + + private: + std::shared_ptr<const Parameters> parameters_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_KEYSET_HANDLE_BUILDER_ENTRY_H_
diff --git a/cc/internal/keyset_handle_builder_entry_test.cc b/cc/internal/keyset_handle_builder_entry_test.cc new file mode 100644 index 0000000..f26b6a1 --- /dev/null +++ b/cc/internal/keyset_handle_builder_entry_test.cc
@@ -0,0 +1,290 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/keyset_handle_builder_entry.h" + +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/config/tink_config.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/legacy_proto_key.h" +#include "tink/internal/legacy_proto_parameters.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/key.h" +#include "tink/key_status.h" +#include "tink/keyset_handle.h" +#include "tink/keyset_handle_builder.h" +#include "tink/mac/aes_cmac_key.h" +#include "tink/mac/aes_cmac_parameters.h" +#include "tink/mac/mac_key_templates.h" +#include "tink/parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsFalse; +using ::testing::IsTrue; +using ::testing::Test; + +util::StatusOr<LegacyProtoParameters> CreateLegacyProtoParameters() { + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create(MacKeyTemplates::AesCmac()); + if (!serialization.ok()) return serialization.status(); + + return LegacyProtoParameters(*serialization); +} + +TEST(KeysetHandleBuilderEntryTest, Status) { + util::StatusOr<LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(); + ASSERT_THAT(parameters, IsOk()); + + ParametersEntry entry = + ParametersEntry(absl::make_unique<LegacyProtoParameters>(*parameters)); + + entry.SetStatus(KeyStatus::kEnabled); + EXPECT_THAT(entry.GetStatus(), KeyStatus::kEnabled); + + entry.SetStatus(KeyStatus::kDisabled); + EXPECT_THAT(entry.GetStatus(), KeyStatus::kDisabled); + + entry.SetStatus(KeyStatus::kDestroyed); + EXPECT_THAT(entry.GetStatus(), KeyStatus::kDestroyed); +} + +TEST(KeysetHandleBuilderEntryTest, IdStrategy) { + util::StatusOr<LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(); + ASSERT_THAT(parameters, IsOk()); + + ParametersEntry entry = + ParametersEntry(absl::make_unique<LegacyProtoParameters>(*parameters)); + + entry.SetFixedId(123); + EXPECT_THAT(entry.GetKeyIdStrategyEnum(), KeyIdStrategyEnum::kFixedId); + EXPECT_THAT(entry.GetKeyIdStrategy().strategy, KeyIdStrategyEnum::kFixedId); + EXPECT_THAT(entry.GetKeyIdStrategy().id_requirement, 123); + EXPECT_THAT(entry.GetKeyIdRequirement(), 123); + + entry.SetRandomId(); + EXPECT_THAT(entry.GetKeyIdStrategyEnum(), KeyIdStrategyEnum::kRandomId); + EXPECT_THAT(entry.GetKeyIdStrategy().strategy, KeyIdStrategyEnum::kRandomId); + EXPECT_THAT(entry.GetKeyIdStrategy().id_requirement, absl::nullopt); + EXPECT_THAT(entry.GetKeyIdRequirement(), absl::nullopt); +} + +TEST(KeysetHandleBuilderEntryTest, Primary) { + util::StatusOr<LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(); + ASSERT_THAT(parameters, IsOk()); + + ParametersEntry entry = + ParametersEntry(absl::make_unique<LegacyProtoParameters>(*parameters)); + + entry.SetPrimary(); + EXPECT_THAT(entry.IsPrimary(), IsTrue()); + + entry.UnsetPrimary(); + EXPECT_THAT(entry.IsPrimary(), IsFalse()); +} + +class CreateKeysetKeyTest : public Test { + protected: + void SetUp() override { ASSERT_THAT(TinkConfig::Register(), IsOk()); } +}; + +TEST_F(CreateKeysetKeyTest, CreateKeysetKeyFromParameters) { + util::StatusOr<LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(); + ASSERT_THAT(parameters, IsOk()); + + ParametersEntry entry = + ParametersEntry(absl::make_unique<LegacyProtoParameters>(*parameters)); + entry.SetStatus(KeyStatus::kEnabled); + entry.SetFixedId(123); + util::StatusOr<Keyset::Key> keyset_key = entry.CreateKeysetKey(/*id=*/123); + ASSERT_THAT(keyset_key, IsOk()); + + EXPECT_THAT(keyset_key->status(), Eq(KeyStatusType::ENABLED)); + EXPECT_THAT(keyset_key->key_id(), Eq(123)); + EXPECT_THAT( + keyset_key->output_prefix_type(), + Eq(parameters->Serialization().GetKeyTemplate().output_prefix_type())); + EXPECT_THAT(keyset_key->key_data().type_url(), + Eq(parameters->Serialization().GetKeyTemplate().type_url())); +} + +TEST_F(CreateKeysetKeyTest, CreateKeysetKeyFromParametersWithDifferentKeyId) { + util::StatusOr<LegacyProtoParameters> parameters = + CreateLegacyProtoParameters(); + ASSERT_THAT(parameters, IsOk()); + + ParametersEntry entry = + ParametersEntry(absl::make_unique<LegacyProtoParameters>(*parameters)); + entry.SetStatus(KeyStatus::kEnabled); + entry.SetFixedId(123); + util::StatusOr<Keyset::Key> keyset_key = entry.CreateKeysetKey(/*id=*/456); + EXPECT_THAT(keyset_key.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(CreateKeysetKeyTest, CreateKeysetKeyFromKey) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/123); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + KeyEntry entry = KeyEntry(absl::make_unique<LegacyProtoKey>(*key)); + entry.SetStatus(KeyStatus::kEnabled); + entry.SetFixedId(123); + util::StatusOr<Keyset::Key> keyset_key = entry.CreateKeysetKey(/*id=*/123); + ASSERT_THAT(keyset_key, IsOk()); + + EXPECT_THAT(keyset_key->status(), Eq(KeyStatusType::ENABLED)); + EXPECT_THAT(keyset_key->key_id(), Eq(123)); + EXPECT_THAT(keyset_key->output_prefix_type(), OutputPrefixType::TINK); + EXPECT_THAT(keyset_key->key_data().type_url(), Eq("type_url")); + EXPECT_THAT(keyset_key->key_data().key_material_type(), + Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(keyset_key->key_data().value(), Eq("serialized_key")); +} + +TEST_F(CreateKeysetKeyTest, CreateKeysetKeyFromKeyWithDifferentEntryKeyId) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/123); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + KeyEntry entry = KeyEntry(absl::make_unique<LegacyProtoKey>(*key)); + entry.SetStatus(KeyStatus::kEnabled); + entry.SetFixedId(123); + util::StatusOr<Keyset::Key> keyset_key = entry.CreateKeysetKey(/*id=*/456); + EXPECT_THAT(keyset_key.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(CreateKeysetKeyTest, + CreateKeysetKeyFromKeyWithDifferentSerializationKeyId) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/123); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + KeyEntry entry = KeyEntry(absl::make_unique<LegacyProtoKey>(*key)); + entry.SetStatus(KeyStatus::kEnabled); + util::StatusOr<Keyset::Key> keyset_key = entry.CreateKeysetKey(/*id=*/456); + EXPECT_THAT(keyset_key.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(CreateKeysetKeyTest, CreateKeysetFromNonLegacyParameters) { + util::StatusOr<AesCmacParameters> aes_cmac_parameters = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(aes_cmac_parameters, IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *aes_cmac_parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/123)) + .Build(); + ASSERT_THAT(handle, IsOk()); +} + +TEST_F(CreateKeysetKeyTest, + CreateKeysetWithAllowedParametersProhibitedByKeyManager) { + util::StatusOr<AesCmacParameters> aes_cmac_parameters = + AesCmacParameters::Create(/*key_size_in_bytes=*/16, + /*cryptographic_tag_size_in_bytes=*/10, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(aes_cmac_parameters, IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *aes_cmac_parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/123)) + .Build(); + ASSERT_THAT(handle.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(CreateKeysetKeyTest, CreateKeysetFromNonLegacyKey) { + util::StatusOr<AesCmacParameters> aes_cmac_parameters = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(aes_cmac_parameters, IsOk()); + util::StatusOr<AesCmacKey> aes_cmac_key = AesCmacKey::Create( + *aes_cmac_parameters, RestrictedData(32), 123, GetPartialKeyAccess()); + ASSERT_THAT(aes_cmac_key.status(), IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableKey( + *aes_cmac_key, KeyStatus::kEnabled, /*is_primary=*/true)) + .Build(); + ASSERT_THAT(handle, IsOk()); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/keyset_wrapper.h b/cc/internal/keyset_wrapper.h index ab10b9e..3258b57 100644 --- a/cc/internal/keyset_wrapper.h +++ b/cc/internal/keyset_wrapper.h
@@ -16,6 +16,7 @@ #ifndef TINK_INTERNAL_KEYSET_WRAPPER_H_ #define TINK_INTERNAL_KEYSET_WRAPPER_H_ +#include <memory> #include <string> #include "absl/container/flat_hash_map.h" @@ -41,7 +42,7 @@ template <typename Primitive> class KeysetWrapper { public: - virtual ~KeysetWrapper() {} + virtual ~KeysetWrapper() = default; // Wraps a given `keyset` with annotations `annotations`. virtual crypto::tink::util::StatusOr<std::unique_ptr<Primitive>> Wrap(
diff --git a/cc/internal/keyset_wrapper_impl.h b/cc/internal/keyset_wrapper_impl.h index 4dc092f..9273e4e 100644 --- a/cc/internal/keyset_wrapper_impl.h +++ b/cc/internal/keyset_wrapper_impl.h
@@ -16,13 +16,17 @@ #ifndef TINK_INTERNAL_KEYSET_WRAPPER_IMPL_H_ #define TINK_INTERNAL_KEYSET_WRAPPER_IMPL_H_ +#include <functional> +#include <memory> #include <string> +#include <utility> #include "absl/container/flat_hash_map.h" #include "tink/internal/key_info.h" #include "tink/internal/keyset_wrapper.h" #include "tink/primitive_set.h" #include "tink/primitive_wrapper.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/validation.h" #include "proto/tink.pb.h" @@ -50,22 +54,27 @@ const override { crypto::tink::util::Status status = ValidateKeyset(keyset); if (!status.ok()) return status; - auto primitives = absl::make_unique<PrimitiveSet<P>>(annotations); + typename PrimitiveSet<P>::Builder primitives_builder; + primitives_builder.AddAnnotations(annotations); for (const google::crypto::tink::Keyset::Key& key : keyset.key()) { if (key.status() != google::crypto::tink::KeyStatusType::ENABLED) { continue; } auto primitive = primitive_getter_(key.key_data()); if (!primitive.ok()) return primitive.status(); - auto entry = primitives->AddPrimitive(std::move(primitive.value()), - KeyInfoFromKey(key)); - if (!entry.ok()) return entry.status(); if (key.key_id() == keyset.primary_key_id()) { - auto primary_result = primitives->set_primary(entry.value()); - if (!primary_result.ok()) return primary_result; + primitives_builder.AddPrimaryPrimitive(std::move(primitive.value()), + KeyInfoFromKey(key)); + } else { + primitives_builder.AddPrimitive(std::move(primitive.value()), + KeyInfoFromKey(key)); } } - return transforming_wrapper_.Wrap(std::move(primitives)); + crypto::tink::util::StatusOr<PrimitiveSet<P>> primitives = + std::move(primitives_builder).Build(); + if (!primitives.ok()) return primitives.status(); + return transforming_wrapper_.Wrap( + absl::make_unique<PrimitiveSet<P>>(*std::move(primitives))); } private:
diff --git a/cc/internal/keyset_wrapper_impl_test.cc b/cc/internal/keyset_wrapper_impl_test.cc index 0606aa1..7c2955b 100644 --- a/cc/internal/keyset_wrapper_impl_test.cc +++ b/cc/internal/keyset_wrapper_impl_test.cc
@@ -15,9 +15,13 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/keyset_wrapper_impl.h" +#include <stdint.h> + #include <memory> #include <string> +#include <tuple> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -25,8 +29,10 @@ #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "tink/primitive_set.h" #include "tink/primitive_wrapper.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h"
diff --git a/cc/internal/keyset_wrapper_store.h b/cc/internal/keyset_wrapper_store.h new file mode 100644 index 0000000..dab9f66 --- /dev/null +++ b/cc/internal/keyset_wrapper_store.h
@@ -0,0 +1,208 @@ +// Copyright 2018 Google Inc. +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_KEYSET_WRAPPER_STORE_H_ +#define TINK_INTERNAL_KEYSET_WRAPPER_STORE_H_ + +#include <memory> +#include <typeindex> + +#include "tink/internal/keyset_wrapper.h" +#include "tink/internal/keyset_wrapper_impl.h" +#include "tink/primitive_wrapper.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Stores KeysetWrappers constructed from their PrimitiveWrapper. This is used +// by the Configuration and Registry classes. +// +// Once inserted, elements in Info, which include the PrimitiveWrapper, must not +// be replaced. +// +// Example: +// KeysetWrapperStore store; +// crypto::tink::util::Status status = store.Add<Aead, Aead>( +// absl::make_unique<AeadWrapper>(), primitive_getter); +// crypto::tink::util::StatusOr<const KeysetWrapper<Aead>*> wrapper = +// store.Get<Aead>(); +class KeysetWrapperStore { + public: + KeysetWrapperStore() = default; + + // Movable, but not copyable. + KeysetWrapperStore(KeysetWrapperStore&& other) = default; + KeysetWrapperStore& operator=(KeysetWrapperStore&& other) = default; + + // Adds a crypto::tink::PrimitiveWrapper and `primitive_getter` function to + // KeysetWrapperStore. + template <class P, class Q> + crypto::tink::util::Status Add( + std::unique_ptr<PrimitiveWrapper<P, Q>> wrapper, + std::function<crypto::tink::util::StatusOr<std::unique_ptr<P>>( + const google::crypto::tink::KeyData& key_data)> + primitive_getter); + + // Gets the PrimitiveWrapper that produces primitive P. This is a legacy + // function. + template <class P> + crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*> + GetPrimitiveWrapper() const; + + // Gets the KeysetWrapper that produces primitive Q. + template <class Q> + crypto::tink::util::StatusOr<const KeysetWrapper<Q>*> Get() const; + + bool IsEmpty() const { return primitive_to_info_.empty(); } + + private: + class Info { + public: + template <typename P, typename Q> + explicit Info( + std::unique_ptr<PrimitiveWrapper<P, Q>> wrapper, + std::function<crypto::tink::util::StatusOr<std::unique_ptr<P>>( + const google::crypto::tink::KeyData& key_data)> + primitive_getter) + : is_same_primitive_wrapping_(std::is_same<P, Q>::value), + wrapper_type_index_(std::type_index(typeid(*wrapper))), + q_type_index_(std::type_index(typeid(Q))) { + keyset_wrapper_ = absl::make_unique<KeysetWrapperImpl<P, Q>>( + wrapper.get(), primitive_getter); + original_wrapper_ = std::move(wrapper); + } + + template <typename Q> + crypto::tink::util::StatusOr<const KeysetWrapper<Q>*> Get() const { + if (q_type_index_ != std::type_index(typeid(Q))) { + return crypto::tink::util::Status( + absl::StatusCode::kInternal, + "RegistryImpl::KeysetWrapper() called with wrong type"); + } + return static_cast<KeysetWrapper<Q>*>(keyset_wrapper_.get()); + } + + // TODO(b/171021679): Deprecate this and upstream functions. + template <typename P> + crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*> + GetPrimitiveWrapper() const { + if (!is_same_primitive_wrapping_) { + // This happens if a user uses a legacy method (like Registry::Wrap) + // directly or has a custom key manager for a primitive which has a + // PrimitiveWrapper<P,Q> with P != Q. + return crypto::tink::util::Status( + absl::StatusCode::kFailedPrecondition, + absl::StrCat("Cannot use primitive type ", typeid(P).name(), + " with a custom key manager.")); + } + if (q_type_index_ != std::type_index(typeid(P))) { + return crypto::tink::util::Status( + absl::StatusCode::kInternal, + "RegistryImpl::LegacyWrapper() called with wrong type"); + } + return static_cast<const PrimitiveWrapper<P, P>*>( + original_wrapper_.get()); + } + + // Returns true if the PrimitiveWrapper is the same class as the one used + // to construct this Info. + template <typename P, typename Q> + bool HasSameType(const PrimitiveWrapper<P, Q>& wrapper) { + return wrapper_type_index_ == std::type_index(typeid(wrapper)); + } + + private: + bool is_same_primitive_wrapping_; + // dynamic std::type_index of the actual PrimitiveWrapper<P,Q> class for + // which this key was inserted. + std::type_index wrapper_type_index_; + // dynamic std::type_index of Q, when PrimitiveWrapper<P,Q> was inserted. + std::type_index q_type_index_; + // The primitive_wrapper passed in. We use a shared_ptr because + // unique_ptr<void> is invalid. + std::shared_ptr<void> original_wrapper_; + // The keyset_wrapper_. We use a shared_ptr because unique_ptr<void> is + // invalid. + std::shared_ptr<void> keyset_wrapper_; + }; + + // Map from primitive type_index to Info. + absl::flat_hash_map<std::type_index, Info> primitive_to_info_; +}; + +template <class P, class Q> +crypto::tink::util::Status KeysetWrapperStore::Add( + std::unique_ptr<PrimitiveWrapper<P, Q>> wrapper, + std::function<crypto::tink::util::StatusOr<std::unique_ptr<P>>( + const google::crypto::tink::KeyData& key_data)> + primitive_getter) { + if (wrapper == nullptr) { + return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, + "Parameter 'wrapper' must be non-null."); + } + if (primitive_getter == nullptr) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + "Parameter 'primitive_getter' must be non-null."); + } + + auto it = primitive_to_info_.find(std::type_index(typeid(Q))); + if (it != primitive_to_info_.end()) { + if (!it->second.HasSameType(*wrapper)) { + return util::Status(absl::StatusCode::kAlreadyExists, + "A wrapper named for this primitive already exists."); + } + return crypto::tink::util::OkStatus(); + } + + primitive_to_info_.insert( + {std::type_index(typeid(Q)), Info(std::move(wrapper), primitive_getter)}); + + return crypto::tink::util::OkStatus(); +} + +template <class P> +crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*> +KeysetWrapperStore::GetPrimitiveWrapper() const { + auto it = primitive_to_info_.find(std::type_index(typeid(P))); + if (it == primitive_to_info_.end()) { + return util::Status( + absl::StatusCode::kNotFound, + absl::StrCat("No wrapper registered for type ", typeid(P).name())); + } + return it->second.GetPrimitiveWrapper<P>(); +} + +template <class P> +crypto::tink::util::StatusOr<const KeysetWrapper<P>*> KeysetWrapperStore::Get() + const { + auto it = primitive_to_info_.find(std::type_index(typeid(P))); + if (it == primitive_to_info_.end()) { + return util::Status( + absl::StatusCode::kNotFound, + absl::StrCat("No wrapper registered for type ", typeid(P).name())); + } + return it->second.Get<P>(); +} + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_KEYSET_WRAPPER_STORE_H_
diff --git a/cc/internal/keyset_wrapper_store_test.cc b/cc/internal/keyset_wrapper_store_test.cc new file mode 100644 index 0000000..9c7231b --- /dev/null +++ b/cc/internal/keyset_wrapper_store_test.cc
@@ -0,0 +1,408 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/keyset_wrapper_store.h" + +#include <functional> +#include <memory> +#include <string> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/internal/registry_impl.h" +#include "tink/mac/mac_wrapper.h" +#include "tink/primitive_set.h" +#include "tink/primitive_wrapper.h" +#include "tink/subtle/random.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "tink/util/test_util.h" +#include "proto/aes_gcm.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesGcmKey; +using ::google::crypto::tink::AesGcmKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeysetInfo; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; + +class FakePrimitive { + public: + explicit FakePrimitive(std::string s) : s_(s) {} + std::string get() { return s_; } + + private: + std::string s_; +}; + +class FakeKeyTypeManager + : public KeyTypeManager<AesGcmKey, AesGcmKeyFormat, List<FakePrimitive>> { + public: + class FakePrimitiveFactory : public PrimitiveFactory<FakePrimitive> { + public: + util::StatusOr<std::unique_ptr<FakePrimitive>> Create( + const AesGcmKey& key) const override { + return absl::make_unique<FakePrimitive>(key.key_value()); + } + }; + + FakeKeyTypeManager() + : KeyTypeManager(absl::make_unique<FakePrimitiveFactory>()) {} + + KeyData::KeyMaterialType key_material_type() const override { + return KeyData::SYMMETRIC; + } + + uint32_t get_version() const override { return 0; } + + const std::string& get_key_type() const override { return key_type_; } + + util::Status ValidateKey(const AesGcmKey& key) const override { + return util::OkStatus(); + } + + util::Status ValidateKeyFormat( + const AesGcmKeyFormat& key_format) const override { + return util::OkStatus(); + } + + util::StatusOr<AesGcmKey> CreateKey( + const AesGcmKeyFormat& key_format) const override { + return AesGcmKey(); + } + + util::StatusOr<AesGcmKey> DeriveKey( + const AesGcmKeyFormat& key_format, + InputStream* input_stream) const override { + return AesGcmKey(); + } + + private: + const std::string key_type_ = + "type.googleapis.com/google.crypto.tink.AesGcmKey"; +}; + +class FakePrimitiveWrapper + : public PrimitiveWrapper<FakePrimitive, FakePrimitive> { + public: + util::StatusOr<std::unique_ptr<FakePrimitive>> Wrap( + std::unique_ptr<PrimitiveSet<FakePrimitive>> primitive_set) + const override { + return absl::make_unique<FakePrimitive>( + primitive_set->get_primary()->get_primitive().get()); + } +}; + +class FakePrimitiveWrapper2 + : public PrimitiveWrapper<FakePrimitive, FakePrimitive> { + public: + util::StatusOr<std::unique_ptr<FakePrimitive>> Wrap( + std::unique_ptr<PrimitiveSet<FakePrimitive>> primitive_set) + const override { + return absl::make_unique<FakePrimitive>( + primitive_set->get_primary()->get_primitive().get()); + } +}; + +std::string AddAesGcmKeyToKeyset(Keyset& keyset, uint32_t key_id, + OutputPrefixType output_prefix_type, + KeyStatusType key_status_type) { + AesGcmKey key; + key.set_version(0); + key.set_key_value(subtle::Random::GetRandomBytes(16)); + KeyData key_data; + key_data.set_value(key.SerializeAsString()); + key_data.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey"); + test::AddKeyData(key_data, key_id, output_prefix_type, key_status_type, + &keyset); + return key.key_value(); +} + +// Returns the function that relies on `registry` to transform `key_data` into +// FakePrimitive. +util::StatusOr<std::function< + util::StatusOr<std::unique_ptr<FakePrimitive>>(const KeyData& key_data)>> +PrimitiveGetter(RegistryImpl& registry) { + util::Status status = + registry.RegisterKeyTypeManager<AesGcmKey, AesGcmKeyFormat, + List<FakePrimitive>>( + absl::make_unique<FakeKeyTypeManager>(), + /*new_key_allowed=*/true); + if (!status.ok()) { + return status; + } + return [®istry](const KeyData& key_data) { + return registry.GetPrimitive<FakePrimitive>(key_data); + }; +} + +TEST(KeysetWrapperStoreTest, Add) { + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + KeysetWrapperStore store; + EXPECT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); +} + +TEST(KeysetWrapperStoreTest, AddNull) { + KeysetWrapperStore store; + EXPECT_THAT((store.Add<FakePrimitive, FakePrimitive>(nullptr, nullptr)), + StatusIs(absl::StatusCode::kInvalidArgument)); + + EXPECT_THAT((store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), nullptr)), + StatusIs(absl::StatusCode::kInvalidArgument)); + + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + EXPECT_THAT( + (store.Add<FakePrimitive, FakePrimitive>(nullptr, *primitive_getter)), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(KeysetWrapperStoreTest, AddWrappersForDifferentPrimitivesSucceeds) { + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + KeysetWrapperStore store; + ASSERT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); + + std::function<util::StatusOr<std::unique_ptr<Mac>>(const KeyData& key_data)> + primitive_getter_mac = [®istry](const KeyData& key_data) { + return registry.GetPrimitive<Mac>(key_data); + }; + EXPECT_THAT((store.Add<Mac, Mac>(absl::make_unique<MacWrapper>(), + primitive_getter_mac)), + IsOk()); +} + +TEST(KeysetWrapperStoreTest, AddSameWrapperTwiceSucceeds) { + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + KeysetWrapperStore store; + ASSERT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); + EXPECT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); +} + +TEST(KeysetWrapperStoreTest, AddDifferentWrappersForSamePrimitiveFails) { + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + KeysetWrapperStore store; + ASSERT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); + EXPECT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper2>(), *primitive_getter)), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(KeysetWrapperStoreTest, GetPrimitiveWrapper) { + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + KeysetWrapperStore store; + ASSERT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); + + util::StatusOr<const PrimitiveWrapper<FakePrimitive, FakePrimitive>*> + legacy_wrapper = store.GetPrimitiveWrapper<FakePrimitive>(); + ASSERT_THAT(legacy_wrapper, IsOk()); + + Keyset keyset; + std::string raw_key = AddAesGcmKeyToKeyset(keyset, 13, OutputPrefixType::TINK, + KeyStatusType::ENABLED); + KeysetInfo keyset_info; + keyset_info.add_key_info(); + keyset_info.mutable_key_info(0)->set_output_prefix_type( + OutputPrefixType::TINK); + keyset_info.mutable_key_info(0)->set_key_id(1234543); + keyset_info.mutable_key_info(0)->set_status(KeyStatusType::ENABLED); + keyset_info.set_primary_key_id(1234543); + std::unique_ptr<PrimitiveSet<FakePrimitive>> primitive_set( + new PrimitiveSet<FakePrimitive>()); + auto entry = primitive_set->AddPrimitive( + absl::make_unique<FakePrimitive>(raw_key), keyset_info.key_info(0)); + ASSERT_THAT(entry, IsOk()); + ASSERT_THAT(primitive_set->set_primary(*entry), IsOk()); + + util::StatusOr<std::unique_ptr<FakePrimitive>> legacy_aead = + (*legacy_wrapper)->Wrap(std::move(primitive_set)); + ASSERT_THAT(legacy_aead, IsOk()); + EXPECT_THAT((*legacy_aead)->get(), Eq(raw_key)); +} + +TEST(KeysetWrapperStoreTest, GetPrimitiveWrapperNonexistentWrapperFails) { + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + KeysetWrapperStore store; + ASSERT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); + + EXPECT_THAT(store.GetPrimitiveWrapper<Mac>().status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(KeysetWrapperStoreTest, Get) { + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + KeysetWrapperStore store; + ASSERT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); + + util::StatusOr<const KeysetWrapper<FakePrimitive>*> wrapper = + store.Get<FakePrimitive>(); + ASSERT_THAT(wrapper, IsOk()); + + Keyset keyset; + std::string raw_key = AddAesGcmKeyToKeyset(keyset, 13, OutputPrefixType::TINK, + KeyStatusType::ENABLED); + keyset.set_primary_key_id(13); + + util::StatusOr<std::unique_ptr<FakePrimitive>> aead = + (*wrapper)->Wrap(keyset, /*annotations=*/{}); + ASSERT_THAT(aead, IsOk()); + EXPECT_THAT((*aead)->get(), Eq(raw_key)); +} + +TEST(KeysetWrapperStoreTest, GetNonexistentWrapperFails) { + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + KeysetWrapperStore store; + ASSERT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); + + EXPECT_THAT(store.Get<Mac>().status(), StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(KeysetWrapperStoreTest, IsEmpty) { + KeysetWrapperStore store; + EXPECT_EQ(store.IsEmpty(), true); + + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + ASSERT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); + EXPECT_THAT(store.IsEmpty(), false); +} + +TEST(KeysetWrapperStoreTest, Move) { + RegistryImpl registry; + util::StatusOr<std::function<util::StatusOr<std::unique_ptr<FakePrimitive>>( + const KeyData& key_data)>> + primitive_getter = PrimitiveGetter(registry); + ASSERT_THAT(primitive_getter, IsOk()); + + KeysetWrapperStore store; + ASSERT_THAT( + (store.Add<FakePrimitive, FakePrimitive>( + absl::make_unique<FakePrimitiveWrapper>(), *primitive_getter)), + IsOk()); + + util::StatusOr<const KeysetWrapper<FakePrimitive>*> wrapper = + store.Get<FakePrimitive>(); + ASSERT_THAT(wrapper, IsOk()); + + KeysetWrapperStore new_store = std::move(store); + wrapper = new_store.Get<FakePrimitive>(); + ASSERT_THAT(wrapper, IsOk()); + + Keyset keyset; + std::string raw_key = AddAesGcmKeyToKeyset(keyset, 13, OutputPrefixType::TINK, + KeyStatusType::ENABLED); + keyset.set_primary_key_id(13); + + util::StatusOr<std::unique_ptr<FakePrimitive>> aead = + (*wrapper)->Wrap(keyset, /*annotations=*/{}); + ASSERT_THAT(aead, IsOk()); + EXPECT_THAT((*aead)->get(), Eq(raw_key)); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/legacy_proto_key.cc b/cc/internal/legacy_proto_key.cc new file mode 100644 index 0000000..6a95107 --- /dev/null +++ b/cc/internal/legacy_proto_key.cc
@@ -0,0 +1,94 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/legacy_proto_key.h" + +#include <string> + +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::google::crypto::tink::KeyData; + +util::Status CheckKeyAccess(KeyData::KeyMaterialType key_material_type, + absl::optional<SecretKeyAccessToken> token) { + if (key_material_type == KeyData::SYMMETRIC || + key_material_type == KeyData::ASYMMETRIC_PRIVATE) { + if (!token.has_value()) { + return util::Status( + absl::StatusCode::kPermissionDenied, + "Missing secret key access token for legacy proto key."); + } + } + return util::OkStatus(); +} + +} // namespace + +bool UnusableLegacyProtoParameters::operator==(const Parameters& other) const { + const UnusableLegacyProtoParameters* that = + dynamic_cast<const UnusableLegacyProtoParameters*>(&other); + if (that == nullptr) { + return false; + } + return type_url_ == that->type_url_ && + output_prefix_type_ == that->output_prefix_type_; +} + +util::StatusOr<LegacyProtoKey> LegacyProtoKey::Create( + ProtoKeySerialization serialization, + absl::optional<SecretKeyAccessToken> token) { + util::Status access_check_status = + CheckKeyAccess(serialization.KeyMaterialType(), token); + if (!access_check_status.ok()) { + return access_check_status; + } + return LegacyProtoKey(serialization); +} + +bool LegacyProtoKey::operator==(const Key& other) const { + const LegacyProtoKey* that = dynamic_cast<const LegacyProtoKey*>(&other); + if (that == nullptr) { + return false; + } + return serialization_.EqualsWithPotentialFalseNegatives(that->serialization_); +} + +util::StatusOr<const ProtoKeySerialization*> LegacyProtoKey::Serialization( + absl::optional<SecretKeyAccessToken> token) const { + util::Status access_check_status = + CheckKeyAccess(serialization_.KeyMaterialType(), token); + if (!access_check_status.ok()) { + return access_check_status; + } + return &serialization_; +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/legacy_proto_key.h b/cc/internal/legacy_proto_key.h new file mode 100644 index 0000000..9372cdf --- /dev/null +++ b/cc/internal/legacy_proto_key.h
@@ -0,0 +1,109 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_LEGACY_PROTO_KEY_H_ +#define TINK_INTERNAL_LEGACY_PROTO_KEY_H_ + +#include <string> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Parameters returned by `LegacyProtoKey::GetParameters()` that cannot be used +// to create other LegacyProtoKey instances. +class UnusableLegacyProtoParameters : public Parameters { + public: + // Copyable and movable. + UnusableLegacyProtoParameters(const UnusableLegacyProtoParameters& other) = + default; + UnusableLegacyProtoParameters& operator=( + const UnusableLegacyProtoParameters& other) = default; + UnusableLegacyProtoParameters(UnusableLegacyProtoParameters&& other) = + default; + UnusableLegacyProtoParameters& operator=( + UnusableLegacyProtoParameters&& other) = default; + + explicit UnusableLegacyProtoParameters( + absl::string_view type_url, + google::crypto::tink::OutputPrefixType output_prefix_type) + : type_url_(type_url), output_prefix_type_(output_prefix_type) {} + + bool HasIdRequirement() const override { + return output_prefix_type_ != google::crypto::tink::OutputPrefixType::RAW; + } + + bool operator==(const Parameters& other) const override; + + private: + std::string type_url_; + google::crypto::tink::OutputPrefixType output_prefix_type_; +}; + +// Key type for legacy proto keys. +class LegacyProtoKey : public Key { + public: + // Copyable and movable. + LegacyProtoKey(const LegacyProtoKey& other) = default; + LegacyProtoKey& operator=(const LegacyProtoKey& other) = default; + LegacyProtoKey(LegacyProtoKey&& other) = default; + LegacyProtoKey& operator=(LegacyProtoKey&& other) = default; + + // Creates `LegacyProtoKey` object from `serialization`. Requires `token` if + // the key material type is either SYMMETRIC or ASYMMETRIC_PRIVATE. + static util::StatusOr<LegacyProtoKey> Create( + ProtoKeySerialization serialization, + absl::optional<SecretKeyAccessToken> token); + + const Parameters& GetParameters() const override { + return unusable_proto_parameters_; + } + + absl::optional<int> GetIdRequirement() const override { + return serialization_.IdRequirement(); + } + + bool operator==(const Key& other) const override; + + // Returns `ProtoKeySerialization` pointer for this object. Requires `token` + // if the key material type is either SYMMETRIC or ASYMMETRIC_PRIVATE. + util::StatusOr<const ProtoKeySerialization*> Serialization( + absl::optional<SecretKeyAccessToken> token) const; + + private: + explicit LegacyProtoKey(ProtoKeySerialization serialization) + : serialization_(serialization), + unusable_proto_parameters_(serialization.TypeUrl(), + serialization.GetOutputPrefixType()) {} + + ProtoKeySerialization serialization_; + UnusableLegacyProtoParameters unusable_proto_parameters_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_LEGACY_PROTO_KEY_H_
diff --git a/cc/internal/legacy_proto_key_test.cc b/cc/internal/legacy_proto_key_test.cc new file mode 100644 index 0000000..800d630 --- /dev/null +++ b/cc/internal/legacy_proto_key_test.cc
@@ -0,0 +1,417 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/legacy_proto_key.h" + +#include <string> +#include <tuple> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::TestWithParam; +using ::testing::Values; + +class LegacyProtoKeyTest : public ::testing::Test { + protected: + // Although this is a friend class, this utility function is necessary to + // access `ProtoKeySerialization::EqualsWithPotentialFalseNegatives()` + // since the test fixtures are subclasses that would not have direct access. + bool Equals(ProtoKeySerialization serialization, + ProtoKeySerialization other) { + return serialization.EqualsWithPotentialFalseNegatives(other); + } +}; + +TEST_F(LegacyProtoKeyTest, CreateAndSerialization) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + EXPECT_THAT(key->GetIdRequirement(), Eq(12345)); + EXPECT_THAT(key->GetParameters().HasIdRequirement(), IsTrue()); + EXPECT_THAT(key->Serialization(InsecureSecretKeyAccess::Get()), IsOk()); + + util::StatusOr<const ProtoKeySerialization*> key_serialization = + key->Serialization(InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key_serialization.status(), IsOk()); + EXPECT_THAT(Equals(**key_serialization, *serialization), IsTrue()); +} + +TEST_F(LegacyProtoKeyTest, Equals) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(other_serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> other_key = LegacyProtoKey::Create( + *other_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key == *other_key); + EXPECT_TRUE(*other_key == *key); + EXPECT_FALSE(*key != *other_key); + EXPECT_FALSE(*other_key != *key); +} + +TEST_F(LegacyProtoKeyTest, TypeUrlNotEqual) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("other_type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(other_serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> other_key = LegacyProtoKey::Create( + *other_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST_F(LegacyProtoKeyTest, SerializedKeyNotEqual) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + RestrictedData other_serialized_key = + RestrictedData("other_serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("type_url", other_serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(other_serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> other_key = LegacyProtoKey::Create( + *other_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST_F(LegacyProtoKeyTest, KeyMaterialTypeNotEqual) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("type_url", serialized_key, KeyData::REMOTE, + OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(other_serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> other_key = LegacyProtoKey::Create( + *other_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST_F(LegacyProtoKeyTest, OutputPrefixTypeNotEqual) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, + OutputPrefixType::CRUNCHY, + /*id_requirement=*/12345); + ASSERT_THAT(other_serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> other_key = LegacyProtoKey::Create( + *other_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST_F(LegacyProtoKeyTest, IdRequirementNotEqual) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/6789); + ASSERT_THAT(other_serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> other_key = LegacyProtoKey::Create( + *other_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +using AllOutputPrefixTypesTest = + TestWithParam<std::tuple<OutputPrefixType, absl::optional<int>>>; + +INSTANTIATE_TEST_SUITE_P( + AllOutputPrefixTypesTestSuite, AllOutputPrefixTypesTest, + Values(std::make_tuple(OutputPrefixType::RAW, absl::nullopt), + std::make_tuple(OutputPrefixType::TINK, 123), + std::make_tuple(OutputPrefixType::CRUNCHY, 456), + std::make_tuple(OutputPrefixType::LEGACY, 789))); + +TEST_P(AllOutputPrefixTypesTest, GetIdRequirement) { + OutputPrefixType output_prefix_type; + absl::optional<int> id_requirement; + std::tie(output_prefix_type, id_requirement) = GetParam(); + + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, output_prefix_type, + id_requirement); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + EXPECT_THAT(key->GetIdRequirement(), Eq(id_requirement)); +} + +using AllKeyMaterialTypesTest = TestWithParam<KeyData::KeyMaterialType>; + +INSTANTIATE_TEST_SUITE_P(AllKeyMaterialTypesTestSuite, AllKeyMaterialTypesTest, + Values(KeyData::SYMMETRIC, KeyData::ASYMMETRIC_PRIVATE, + KeyData::ASYMMETRIC_PUBLIC, KeyData::REMOTE)); + +TEST_P(AllKeyMaterialTypesTest, CreateAndSerializationWithSecretAccessToken) { + KeyData::KeyMaterialType key_material_type = GetParam(); + + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + key_material_type, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<const ProtoKeySerialization*> key_serialization = + key->Serialization(InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key_serialization.status(), IsOk()); +} + +using SecretKeyMaterialTypesTest = TestWithParam<KeyData::KeyMaterialType>; + +INSTANTIATE_TEST_SUITE_P(SecretKeyMaterialTypesTestSuite, + SecretKeyMaterialTypesTest, + Values(KeyData::SYMMETRIC, + KeyData::ASYMMETRIC_PRIVATE)); + +TEST_P(SecretKeyMaterialTypesTest, CreateWithoutSecretAccessToken) { + KeyData::KeyMaterialType key_material_type = GetParam(); + + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + key_material_type, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, /*token=*/absl::nullopt); + ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kPermissionDenied)); +} + +TEST_P(SecretKeyMaterialTypesTest, SerializationWithoutSecretAccessToken) { + KeyData::KeyMaterialType key_material_type = GetParam(); + + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + key_material_type, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + // Must use token for key creation. + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<const ProtoKeySerialization*> key_serialization = + key->Serialization(/*token=*/absl::nullopt); + ASSERT_THAT(key_serialization.status(), + StatusIs(absl::StatusCode::kPermissionDenied)); +} + +using NonSecretKeyMaterialTypesTest = TestWithParam<KeyData::KeyMaterialType>; + +INSTANTIATE_TEST_SUITE_P(NonSecretKeyMaterialTypesTestSuite, + NonSecretKeyMaterialTypesTest, + Values(KeyData::ASYMMETRIC_PUBLIC, KeyData::REMOTE)); + +TEST_P(NonSecretKeyMaterialTypesTest, CreateWithoutSecretAccessToken) { + KeyData::KeyMaterialType key_material_type = GetParam(); + + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + key_material_type, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, /*token=*/absl::nullopt); + ASSERT_THAT(key.status(), IsOk()); +} + +TEST_P(NonSecretKeyMaterialTypesTest, SerializationWithoutSecretAccessToken) { + KeyData::KeyMaterialType key_material_type = GetParam(); + + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + key_material_type, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + // Must use token for key creation. + util::StatusOr<LegacyProtoKey> key = + LegacyProtoKey::Create(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<const ProtoKeySerialization*> key_serialization = + key->Serialization(/*token=*/absl::nullopt); + ASSERT_THAT(key_serialization.status(), IsOk()); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/legacy_proto_parameters.cc b/cc/internal/legacy_proto_parameters.cc new file mode 100644 index 0000000..bf512f7 --- /dev/null +++ b/cc/internal/legacy_proto_parameters.cc
@@ -0,0 +1,37 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/legacy_proto_parameters.h" + +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/parameters.h" + +namespace crypto { +namespace tink { +namespace internal { + +bool LegacyProtoParameters::operator==(const Parameters& other) const { + const LegacyProtoParameters* that = + dynamic_cast<const LegacyProtoParameters*>(&other); + if (that == nullptr) { + return false; + } + return serialization_.EqualsWithPotentialFalseNegatives(that->serialization_); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/legacy_proto_parameters.h b/cc/internal/legacy_proto_parameters.h new file mode 100644 index 0000000..e204c95 --- /dev/null +++ b/cc/internal/legacy_proto_parameters.h
@@ -0,0 +1,61 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_LEGACY_PROTO_PARAMETERS_H_ +#define TINK_INTERNAL_LEGACY_PROTO_PARAMETERS_H_ + +#include <utility> + +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/parameters.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +class LegacyProtoParameters : public Parameters { + public: + // Copyable and movable. + LegacyProtoParameters(const LegacyProtoParameters& other) = default; + LegacyProtoParameters& operator=(const LegacyProtoParameters& other) = + default; + LegacyProtoParameters(LegacyProtoParameters&& other) = default; + LegacyProtoParameters& operator=(LegacyProtoParameters&& other) = default; + + explicit LegacyProtoParameters(ProtoParametersSerialization serialization) + : serialization_(std::move(serialization)) {} + + bool HasIdRequirement() const override { + return serialization_.GetKeyTemplate().output_prefix_type() != + google::crypto::tink::OutputPrefixType::RAW; + } + + bool operator==(const Parameters& other) const override; + + const ProtoParametersSerialization& Serialization() const { + return serialization_; + } + + private: + ProtoParametersSerialization serialization_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_LEGACY_PROTO_PARAMETERS_H_
diff --git a/cc/internal/legacy_proto_parameters_test.cc b/cc/internal/legacy_proto_parameters_test.cc new file mode 100644 index 0000000..ad720c1 --- /dev/null +++ b/cc/internal/legacy_proto_parameters_test.cc
@@ -0,0 +1,176 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/legacy_proto_parameters.h" + +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/parameters.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "proto/test_proto.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::crypto::tink::test::IsOk; +using ::google::crypto::tink::OutputPrefixType; +using ::google::crypto::tink::TestProto; +using ::testing::IsFalse; +using ::testing::IsTrue; + +class LegacyProtoParametersTest : public ::testing::Test { + protected: + // Although this is a friend class, this utility function is necessary to + // access `ProtoParametersSerialization::EqualsWithPotentialFalseNegatives()` + // since the test fixtures are subclasses that would not have direct access. + bool Equals(ProtoParametersSerialization serialization, + ProtoParametersSerialization other) { + return serialization.EqualsWithPotentialFalseNegatives(other); + } +}; + +TEST_F(LegacyProtoParametersTest, CreateWithIdRequirement) { + TestProto test_proto; + test_proto.set_num(12345); + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::TINK, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + LegacyProtoParameters parameters(*serialization); + + EXPECT_THAT(parameters.HasIdRequirement(), IsTrue()); + EXPECT_THAT(Equals(*serialization, parameters.Serialization()), IsTrue()); +} + +TEST_F(LegacyProtoParametersTest, CreateWithoutIdRequirement) { + TestProto test_proto; + test_proto.set_num(12345); + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + LegacyProtoParameters parameters(*serialization); + + EXPECT_THAT(parameters.HasIdRequirement(), IsFalse()); + EXPECT_THAT(Equals(*serialization, parameters.Serialization()), IsTrue()); +} + +TEST_F(LegacyProtoParametersTest, Equals) { + TestProto test_proto; + test_proto.set_num(12345); + + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoParametersSerialization> other_serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(other_serialization.status(), IsOk()); + + LegacyProtoParameters parameters(*serialization); + LegacyProtoParameters other_parameters(*other_serialization); + + EXPECT_TRUE(parameters == other_parameters); + EXPECT_TRUE(other_parameters == parameters); + EXPECT_FALSE(parameters != other_parameters); + EXPECT_FALSE(other_parameters != parameters); +} + +TEST_F(LegacyProtoParametersTest, TypeUrlNotEqual) { + TestProto test_proto; + test_proto.set_num(12345); + + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoParametersSerialization> other_serialization = + ProtoParametersSerialization::Create("other_type_url", + OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(other_serialization.status(), IsOk()); + + LegacyProtoParameters parameters(*serialization); + LegacyProtoParameters other_parameters(*other_serialization); + + EXPECT_TRUE(parameters != other_parameters); + EXPECT_TRUE(other_parameters != parameters); + EXPECT_FALSE(parameters == other_parameters); + EXPECT_FALSE(other_parameters == parameters); +} + +TEST_F(LegacyProtoParametersTest, OutputPrefixTypeNotEqual) { + TestProto test_proto; + test_proto.set_num(12345); + + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoParametersSerialization> other_serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::TINK, + test_proto.SerializeAsString()); + ASSERT_THAT(other_serialization.status(), IsOk()); + + LegacyProtoParameters parameters(*serialization); + LegacyProtoParameters other_parameters(*other_serialization); + + EXPECT_TRUE(parameters != other_parameters); + EXPECT_TRUE(other_parameters != parameters); + EXPECT_FALSE(parameters == other_parameters); + EXPECT_FALSE(other_parameters == parameters); +} + +TEST_F(LegacyProtoParametersTest, DifferentValueNotEqual) { + TestProto test_proto; + test_proto.set_num(12345); + TestProto other_proto; + other_proto.set_num(67890); + + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoParametersSerialization> other_serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + other_proto.SerializeAsString()); + ASSERT_THAT(other_serialization.status(), IsOk()); + + LegacyProtoParameters parameters(*serialization); + LegacyProtoParameters other_parameters(*other_serialization); + + EXPECT_TRUE(parameters != other_parameters); + EXPECT_TRUE(other_parameters != parameters); + EXPECT_FALSE(parameters == other_parameters); + EXPECT_FALSE(other_parameters == parameters); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/md_util.cc b/cc/internal/md_util.cc index 36d36a0..acca9fe 100644 --- a/cc/internal/md_util.cc +++ b/cc/internal/md_util.cc
@@ -15,6 +15,8 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/md_util.h" +#include <stdint.h> + #include <string> #include "absl/status/status.h" @@ -26,6 +28,7 @@ #include "tink/subtle/common_enums.h" #include "tink/subtle/subtle_util.h" #include "tink/util/status.h" +#include "tink/util/statusor.h" namespace crypto { namespace tink {
diff --git a/cc/internal/md_util_test.cc b/cc/internal/md_util_test.cc index e40f390..0802070 100644 --- a/cc/internal/md_util_test.cc +++ b/cc/internal/md_util_test.cc
@@ -15,15 +15,16 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/md_util.h" -#include <cstdint> #include <string> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/strings/escaping.h" -#include "absl/types/span.h" +#include "absl/strings/string_view.h" #include "openssl/evp.h" #include "tink/subtle/common_enums.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h"
diff --git a/cc/internal/monitoring_util.h b/cc/internal/monitoring_util.h index dbf5e81..a2b2c13 100644 --- a/cc/internal/monitoring_util.h +++ b/cc/internal/monitoring_util.h
@@ -22,8 +22,12 @@ #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" +#include "absl/strings/strip.h" +#include "tink/internal/key_status_util.h" +#include "tink/key_status.h" #include "tink/monitoring/monitoring.h" #include "tink/primitive_set.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" #include "proto/tink.pb.h" @@ -31,6 +35,8 @@ namespace tink { namespace internal { +constexpr char kKeyTypePrefix[] = "type.googleapis.com/google.crypto."; + // Constructs a MonitoringKeySetInfo object from a PrimitiveSet `primitive_set` // for a given primitive P. template <class P> @@ -48,30 +54,14 @@ } std::vector<MonitoringKeySetInfo::Entry> keyset_info_entries = {}; for (const auto& entry : primitive_set_entries) { - MonitoringKeySetInfo::Entry::KeyStatus key_status; - switch (entry->get_status()) { - case google::crypto::tink::KeyStatusType::ENABLED: { - key_status = MonitoringKeySetInfo::Entry::KeyStatus::kEnabled; - break; - } - case google::crypto::tink::KeyStatusType::DISABLED: { - key_status = MonitoringKeySetInfo::Entry::KeyStatus::kDisabled; - break; - } - case google::crypto::tink::KeyStatusType::DESTROYED: { - key_status = MonitoringKeySetInfo::Entry::KeyStatus::kDestroyed; - break; - } - default: - return util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("Unknown key status ", entry->get_status())); - } + util::StatusOr<KeyStatus> key_status = + FromKeyStatusType(entry->get_status()); + if (!key_status.ok()) return key_status.status(); - // TODO(b/222245356): Populate key_format_as_string with the actual key - // format when available. For now, we use the key type URL. auto keyset_info_entry = MonitoringKeySetInfo::Entry( - key_status, entry->get_key_id(), entry->get_key_type_url()); + *key_status, entry->get_key_id(), + absl::StripPrefix(entry->get_key_type_url(), kKeyTypePrefix), + OutputPrefixType_Name(entry->get_output_prefix_type())); keyset_info_entries.push_back(keyset_info_entry); } MonitoringKeySetInfo keyset_info(primitive_set.get_annotations(),
diff --git a/cc/internal/monitoring_util_test.cc b/cc/internal/monitoring_util_test.cc index dd81870..c08b4fa 100644 --- a/cc/internal/monitoring_util_test.cc +++ b/cc/internal/monitoring_util_test.cc
@@ -15,17 +15,24 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/monitoring_util.h" +#include <stdint.h> + #include <memory> #include <string> -#include <tuple> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/container/flat_hash_map.h" +#include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "tink/key_status.h" #include "tink/monitoring/monitoring.h" #include "tink/primitive_set.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" #include "tink/util/test_matchers.h" #include "proto/tink.pb.h" @@ -89,7 +96,8 @@ MATCHER_P(MonitoringKeySetInfoEntryEq, other, "") { return arg.GetStatus() == other.GetStatus() && arg.GetKeyId() == other.GetKeyId() && - arg.GetParametersAsString() == other.GetParametersAsString(); + arg.GetKeyPrefix() == other.GetKeyPrefix() && + arg.GetKeyType() == other.GetKeyType(); } TEST(MonitoringUtilTest, MonitoringKeySetInfoFromPrimitiveSetValid) { @@ -134,14 +142,15 @@ UnorderedElementsAreArray(kAnnotations)); const std::vector<MonitoringKeySetInfo::Entry> &monitoring_entries = monitoring_keyset_info->GetEntries(); - EXPECT_THAT(monitoring_entries, - UnorderedElementsAre( - MonitoringKeySetInfoEntryEq(MonitoringKeySetInfo::Entry( - MonitoringKeySetInfo::Entry::KeyStatus::kEnabled, - /*key_id=*/1, kPrimitive1KeyTyepUrl)), - MonitoringKeySetInfoEntryEq(MonitoringKeySetInfo::Entry( - MonitoringKeySetInfo::Entry::KeyStatus::kEnabled, - /*key_id=*/2, kPrimitive2KeyTypeUrl)))); + EXPECT_THAT( + monitoring_entries, + UnorderedElementsAre( + MonitoringKeySetInfoEntryEq(MonitoringKeySetInfo::Entry( + KeyStatus::kEnabled, + /*key_id=*/1, "tink.SomePrimitiveInstance", "TINK")), + MonitoringKeySetInfoEntryEq(MonitoringKeySetInfo::Entry( + KeyStatus::kEnabled, + /*key_id=*/2, "tink.SomeOtherPrimitiveInstance", "TINK")))); } } // namespace
diff --git a/cc/internal/mutable_serialization_registry.cc b/cc/internal/mutable_serialization_registry.cc new file mode 100644 index 0000000..250d026 --- /dev/null +++ b/cc/internal/mutable_serialization_registry.cc
@@ -0,0 +1,121 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/mutable_serialization_registry.h" + +#include <memory> + +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/synchronization/mutex.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/legacy_proto_key.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serialization_registry.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +MutableSerializationRegistry& MutableSerializationRegistry::GlobalInstance() { + static MutableSerializationRegistry* instance = + new MutableSerializationRegistry(); + return *instance; +} + +util::Status MutableSerializationRegistry::RegisterParametersParser( + ParametersParser* parser) { + absl::MutexLock lock(®istry_mutex_); + SerializationRegistry::Builder builder(registry_); + util::Status status = builder.RegisterParametersParser(parser); + if (!status.ok()) return status; + registry_ = builder.Build(); + return util::OkStatus(); +} + +util::Status MutableSerializationRegistry::RegisterParametersSerializer( + ParametersSerializer* serializer) { + absl::MutexLock lock(®istry_mutex_); + SerializationRegistry::Builder builder(registry_); + util::Status status = builder.RegisterParametersSerializer(serializer); + if (!status.ok()) return status; + registry_ = builder.Build(); + return util::OkStatus(); +} + +util::Status MutableSerializationRegistry::RegisterKeyParser( + KeyParser* parser) { + absl::MutexLock lock(®istry_mutex_); + SerializationRegistry::Builder builder(registry_); + util::Status status = builder.RegisterKeyParser(parser); + if (!status.ok()) return status; + registry_ = builder.Build(); + return util::OkStatus(); +} + +util::Status MutableSerializationRegistry::RegisterKeySerializer( + KeySerializer* serializer) { + absl::MutexLock lock(®istry_mutex_); + SerializationRegistry::Builder builder(registry_); + util::Status status = builder.RegisterKeySerializer(serializer); + if (!status.ok()) return status; + registry_ = builder.Build(); + return util::OkStatus(); +} + +util::StatusOr<std::unique_ptr<Parameters>> +MutableSerializationRegistry::ParseParameters( + const Serialization& serialization) { + absl::MutexLock lock(®istry_mutex_); + return registry_.ParseParameters(serialization); +} + +util::StatusOr<std::unique_ptr<Key>> MutableSerializationRegistry::ParseKey( + const Serialization& serialization, + absl::optional<SecretKeyAccessToken> token) { + absl::MutexLock lock(®istry_mutex_); + return registry_.ParseKey(serialization, token); +} + +util::StatusOr<std::unique_ptr<Key>> +MutableSerializationRegistry::ParseKeyWithLegacyFallback( + const Serialization& serialization, SecretKeyAccessToken token) { + util::StatusOr<std::unique_ptr<Key>> key = ParseKey(serialization, token); + if (key.status().code() == absl::StatusCode::kNotFound) { + const ProtoKeySerialization* proto_serialization = + dynamic_cast<const ProtoKeySerialization*>(&serialization); + util::StatusOr<LegacyProtoKey> proto_key = internal::LegacyProtoKey::Create( + *proto_serialization, InsecureSecretKeyAccess::Get()); + if (!proto_key.ok()) return proto_key.status(); + return {absl::make_unique<LegacyProtoKey>(*proto_key)}; + } + if (!key.ok()) return key.status(); + return key; +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/mutable_serialization_registry.h b/cc/internal/mutable_serialization_registry.h new file mode 100644 index 0000000..a9914dd --- /dev/null +++ b/cc/internal/mutable_serialization_registry.h
@@ -0,0 +1,117 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_MUTABLE_SERIALIZATION_REGISTRY_H_ +#define TINK_INTERNAL_MUTABLE_SERIALIZATION_REGISTRY_H_ + +#include <memory> + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/optional.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serialization_registry.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +// This class provides a global, mutable serialization registry by wrapping an +// instance of an immutable `SerializationRegistry`. This registry will enable +// the Tink 2.0 C++ Keyset API in the near term. +class MutableSerializationRegistry { + public: + // Returns the global serialization registry. + static MutableSerializationRegistry& GlobalInstance(); + + // Registers parameters `parser`. Returns an error if a different parameters + // parser with the same parser index has already been registered. + util::Status RegisterParametersParser(ParametersParser* parser) + ABSL_LOCKS_EXCLUDED(registry_mutex_); + + // Registers parameters `serializer`. Returns an error if a different + // parameters serializer with the same serializer index has already been + // registered. + util::Status RegisterParametersSerializer(ParametersSerializer* serializer) + ABSL_LOCKS_EXCLUDED(registry_mutex_); + + // Registers key `parser`. Returns an error if a different key parser with the + // same parser index has already been registered. + util::Status RegisterKeyParser(KeyParser* parser) + ABSL_LOCKS_EXCLUDED(registry_mutex_); + + // Registers key `serializer`. Returns an error if a different key serializer + // with the same serializer index has already been registered. + util::Status RegisterKeySerializer(KeySerializer* serializer) + ABSL_LOCKS_EXCLUDED(registry_mutex_); + + // Parses `serialization` into a `Parameters` instance. + util::StatusOr<std::unique_ptr<Parameters>> ParseParameters( + const Serialization& serialization) ABSL_LOCKS_EXCLUDED(registry_mutex_); + + // Serializes `parameters` into a `Serialization` instance. + template <typename SerializationT> + util::StatusOr<std::unique_ptr<Serialization>> SerializeParameters( + const Parameters& parameters) ABSL_LOCKS_EXCLUDED(registry_mutex_) { + absl::MutexLock lock(®istry_mutex_); + return registry_.SerializeParameters<SerializationT>(parameters); + } + + // Parses `serialization` into a `Key` instance. + util::StatusOr<std::unique_ptr<Key>> ParseKey( + const Serialization& serialization, + absl::optional<SecretKeyAccessToken> token) + ABSL_LOCKS_EXCLUDED(registry_mutex_); + + // Similar to `ParseKey` but falls back to legacy proto key serialization if + // the corresponding key parser is not found. + util::StatusOr<std::unique_ptr<Key>> ParseKeyWithLegacyFallback( + const Serialization& serialization, SecretKeyAccessToken token); + + // Serializes `parameters` into a `Serialization` instance. + template <typename SerializationT> + util::StatusOr<std::unique_ptr<Serialization>> SerializeKey( + const Key& key, absl::optional<SecretKeyAccessToken> token) + ABSL_LOCKS_EXCLUDED(registry_mutex_) { + absl::MutexLock lock(®istry_mutex_); + return registry_.SerializeKey<SerializationT>(key, token); + } + + // Resets to a new empty registry. + void Reset() ABSL_LOCKS_EXCLUDED(registry_mutex_) { + absl::MutexLock lock(®istry_mutex_); + registry_ = SerializationRegistry(); + } + + private: + mutable absl::Mutex registry_mutex_; + SerializationRegistry registry_ ABSL_GUARDED_BY(registry_mutex_); +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_MUTABLE_SERIALIZATION_REGISTRY_H_
diff --git a/cc/internal/mutable_serialization_registry_test.cc b/cc/internal/mutable_serialization_registry_test.cc new file mode 100644 index 0000000..7bea8b8 --- /dev/null +++ b/cc/internal/mutable_serialization_registry_test.cc
@@ -0,0 +1,379 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/mutable_serialization_registry.h" + +#include <memory> +#include <string_view> +#include <typeindex> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serialization_test_util.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsFalse; +using ::testing::IsTrue; + +TEST(MutableSerializationRegistryTest, ParseParameters) { + MutableSerializationRegistry registry; + ParametersParserImpl<NoIdSerialization, NoIdParams> parser1(kNoIdTypeUrl, + ParseNoIdParams); + ParametersParserImpl<IdParamsSerialization, IdParams> parser2(kIdTypeUrl, + ParseIdParams); + ASSERT_THAT(registry.RegisterParametersParser(&parser1), IsOk()); + ASSERT_THAT(registry.RegisterParametersParser(&parser2), IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> no_id_params = + registry.ParseParameters(NoIdSerialization()); + ASSERT_THAT(no_id_params, IsOk()); + EXPECT_THAT((*no_id_params)->HasIdRequirement(), IsFalse()); + EXPECT_THAT(std::type_index(typeid(**no_id_params)), + std::type_index(typeid(NoIdParams))); + + util::StatusOr<std::unique_ptr<Parameters>> id_params = + registry.ParseParameters(IdParamsSerialization()); + ASSERT_THAT(id_params, IsOk()); + EXPECT_THAT((*id_params)->HasIdRequirement(), IsTrue()); + EXPECT_THAT(std::type_index(typeid(**id_params)), + std::type_index(typeid(IdParams))); +} + +TEST(MutableSerializationRegistryTest, ParseParametersWithoutRegistration) { + MutableSerializationRegistry registry; + + ASSERT_THAT(registry.ParseParameters(NoIdSerialization()).status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(MutableSerializationRegistryTest, RegisterSameParametersParser) { + MutableSerializationRegistry registry; + ParametersParserImpl<NoIdSerialization, NoIdParams> parser(kNoIdTypeUrl, + ParseNoIdParams); + + EXPECT_THAT(registry.RegisterParametersParser(&parser), IsOk()); + EXPECT_THAT(registry.RegisterParametersParser(&parser), IsOk()); +} + +TEST(MutableSerializationRegistryTest, + RegisterDifferentParametersParserWithSameIndex) { + MutableSerializationRegistry registry; + ParametersParserImpl<NoIdSerialization, NoIdParams> parser1(kNoIdTypeUrl, + ParseNoIdParams); + ParametersParserImpl<NoIdSerialization, NoIdParams> parser2(kNoIdTypeUrl, + ParseNoIdParams); + + EXPECT_THAT(registry.RegisterParametersParser(&parser1), IsOk()); + EXPECT_THAT(registry.RegisterParametersParser(&parser2), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(MutableSerializationRegistryTest, SerializeParameters) { + MutableSerializationRegistry registry; + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer1( + kNoIdTypeUrl, SerializeNoIdParams); + ParametersSerializerImpl<IdParams, IdParamsSerialization> serializer2( + kIdTypeUrl, SerializeIdParams); + ASSERT_THAT(registry.RegisterParametersSerializer(&serializer1), IsOk()); + ASSERT_THAT(registry.RegisterParametersSerializer(&serializer2), IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization1 = + registry.SerializeParameters<NoIdSerialization>(NoIdParams()); + ASSERT_THAT(serialization1, IsOk()); + EXPECT_THAT((*serialization1)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + + util::StatusOr<std::unique_ptr<Serialization>> serialization2 = + registry.SerializeParameters<IdParamsSerialization>(IdParams()); + ASSERT_THAT(serialization2, IsOk()); + EXPECT_THAT((*serialization2)->ObjectIdentifier(), Eq(kIdTypeUrl)); +} + +TEST(MutableSerializationRegistryTest, SerializeParametersWithoutRegistration) { + MutableSerializationRegistry registry; + + ASSERT_THAT( + registry.SerializeParameters<NoIdSerialization>(NoIdParams()).status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(MutableSerializationRegistryTest, RegisterSameParametersSerializer) { + MutableSerializationRegistry registry; + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer( + kNoIdTypeUrl, SerializeNoIdParams); + + EXPECT_THAT(registry.RegisterParametersSerializer(&serializer), IsOk()); + EXPECT_THAT(registry.RegisterParametersSerializer(&serializer), IsOk()); +} + +TEST(MutableSerializationRegistryTest, + RegisterDifferentParametersSerializerWithSameIndex) { + MutableSerializationRegistry registry; + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer1( + kNoIdTypeUrl, SerializeNoIdParams); + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer2( + kNoIdTypeUrl, SerializeNoIdParams); + + EXPECT_THAT(registry.RegisterParametersSerializer(&serializer1), IsOk()); + EXPECT_THAT(registry.RegisterParametersSerializer(&serializer2), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(MutableSerializationRegistryTest, ParseKey) { + MutableSerializationRegistry registry; + KeyParserImpl<NoIdSerialization, NoIdKey> parser1(kNoIdTypeUrl, ParseNoIdKey); + KeyParserImpl<IdKeySerialization, IdKey> parser2(kIdTypeUrl, ParseIdKey); + ASSERT_THAT(registry.RegisterKeyParser(&parser1), IsOk()); + ASSERT_THAT(registry.RegisterKeyParser(&parser2), IsOk()); + + util::StatusOr<std::unique_ptr<Key>> no_id_key = + registry.ParseKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(no_id_key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**no_id_key)), + std::type_index(typeid(NoIdKey))); + + util::StatusOr<std::unique_ptr<Key>> id_key = registry.ParseKey( + IdKeySerialization(/*id=*/123), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(id_key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**id_key)), + std::type_index(typeid(IdKey))); + EXPECT_THAT((*id_key)->GetIdRequirement(), Eq(123)); +} + +TEST(MutableSerializationRegistryTest, ParseKeyNoSecretAccess) { + MutableSerializationRegistry registry; + KeyParserImpl<NoIdSerialization, NoIdKey> parser(kNoIdTypeUrl, ParseNoIdKey); + ASSERT_THAT(registry.RegisterKeyParser(&parser), IsOk()); + + util::StatusOr<std::unique_ptr<Key>> no_id_public_key = + registry.ParseKey(NoIdSerialization(), absl::nullopt); + ASSERT_THAT(no_id_public_key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**no_id_public_key)), + std::type_index(typeid(NoIdKey))); +} + +TEST(MutableSerializationRegistryTest, ParseKeyWithLegacyFallback) { + MutableSerializationRegistry registry; + KeyParserImpl<IdKeySerialization, IdKey> parser(kIdTypeUrl, ParseIdKey); + ASSERT_THAT(registry.RegisterKeyParser(&parser), IsOk()); + + // Parse key with registered key parser. + util::StatusOr<std::unique_ptr<Key>> id_key = + registry.ParseKeyWithLegacyFallback(IdKeySerialization(/*id=*/123), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(id_key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**id_key)), + std::type_index(typeid(IdKey))); + EXPECT_THAT((*id_key)->GetIdRequirement(), Eq(123)); + + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/456); + ASSERT_THAT(serialization.status(), IsOk()); + + // Fall back to legacy proto key. + util::StatusOr<std::unique_ptr<Key>> proto_key = + registry.ParseKeyWithLegacyFallback(*serialization, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(proto_key, IsOk()); + EXPECT_THAT((*proto_key)->GetIdRequirement(), Eq(456)); +} + +TEST(MutableSerializationRegistryTest, ParseKeyWithoutRegistration) { + MutableSerializationRegistry registry; + + ASSERT_THAT( + registry.ParseKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()) + .status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(MutableSerializationRegistryTest, RegisterSameKeyParser) { + MutableSerializationRegistry registry; + KeyParserImpl<NoIdSerialization, NoIdKey> parser(kNoIdTypeUrl, ParseNoIdKey); + + EXPECT_THAT(registry.RegisterKeyParser(&parser), IsOk()); + EXPECT_THAT(registry.RegisterKeyParser(&parser), IsOk()); +} + +TEST(MutableSerializationRegistryTest, + RegisterDifferentKeyParserWithSameIndex) { + MutableSerializationRegistry registry; + KeyParserImpl<NoIdSerialization, NoIdKey> parser1(kNoIdTypeUrl, ParseNoIdKey); + KeyParserImpl<NoIdSerialization, NoIdKey> parser2(kNoIdTypeUrl, ParseNoIdKey); + + EXPECT_THAT(registry.RegisterKeyParser(&parser1), IsOk()); + EXPECT_THAT(registry.RegisterKeyParser(&parser2), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(MutableSerializationRegistryTest, SerializeKey) { + MutableSerializationRegistry registry; + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer1(SerializeNoIdKey); + KeySerializerImpl<IdKey, IdKeySerialization> serializer2(SerializeIdKey); + ASSERT_THAT(registry.RegisterKeySerializer(&serializer1), IsOk()); + ASSERT_THAT(registry.RegisterKeySerializer(&serializer2), IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization1 = + registry.SerializeKey<NoIdSerialization>(NoIdKey(), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization1, IsOk()); + EXPECT_THAT((*serialization1)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + + util::StatusOr<std::unique_ptr<Serialization>> serialization2 = + registry.SerializeKey<IdKeySerialization>(IdKey(123), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization2, IsOk()); + EXPECT_THAT((*serialization2)->ObjectIdentifier(), Eq(kIdTypeUrl)); +} + +TEST(MutableSerializationRegistryTest, SerializeKeyNoSecretAccess) { + MutableSerializationRegistry registry; + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer(SerializeNoIdKey); + ASSERT_THAT(registry.RegisterKeySerializer(&serializer), IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + registry.SerializeKey<NoIdSerialization>(NoIdKey(), absl::nullopt); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); +} + +TEST(MutableSerializationRegistryTest, SerializeKeyWithoutRegistration) { + MutableSerializationRegistry registry; + + ASSERT_THAT(registry + .SerializeKey<NoIdSerialization>( + NoIdKey(), InsecureSecretKeyAccess::Get()) + .status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(MutableSerializationRegistryTest, RegisterSameKeySerializer) { + MutableSerializationRegistry registry; + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer(SerializeNoIdKey); + + EXPECT_THAT(registry.RegisterKeySerializer(&serializer), IsOk()); + EXPECT_THAT(registry.RegisterKeySerializer(&serializer), IsOk()); +} + +TEST(MutableSerializationRegistryTest, + RegisterDifferentKeySerializerWithSameIndex) { + MutableSerializationRegistry registry; + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer1(SerializeNoIdKey); + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer2(SerializeNoIdKey); + + EXPECT_THAT(registry.RegisterKeySerializer(&serializer1), IsOk()); + EXPECT_THAT(registry.RegisterKeySerializer(&serializer2), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(MutableSerializationRegistryTest, Reset) { + MutableSerializationRegistry registry; + ParametersParserImpl<NoIdSerialization, NoIdParams> params_parser( + kNoIdTypeUrl, ParseNoIdParams); + ParametersSerializerImpl<NoIdParams, NoIdSerialization> params_serializer( + kNoIdTypeUrl, SerializeNoIdParams); + KeyParserImpl<NoIdSerialization, NoIdKey> key_parser(kNoIdTypeUrl, + ParseNoIdKey); + KeySerializerImpl<NoIdKey, NoIdSerialization> key_serializer( + SerializeNoIdKey); + + ASSERT_THAT(registry.RegisterParametersParser(¶ms_parser), IsOk()); + ASSERT_THAT(registry.RegisterParametersSerializer(¶ms_serializer), + IsOk()); + ASSERT_THAT(registry.RegisterKeyParser(&key_parser), IsOk()); + ASSERT_THAT(registry.RegisterKeySerializer(&key_serializer), IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + registry.ParseParameters(NoIdSerialization()); + ASSERT_THAT(params, IsOk()); + util::StatusOr<std::unique_ptr<Serialization>> serialization1 = + registry.SerializeParameters<NoIdSerialization>(NoIdParams()); + ASSERT_THAT(serialization1, IsOk()); + util::StatusOr<std::unique_ptr<Key>> key = + registry.ParseKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + util::StatusOr<std::unique_ptr<Serialization>> serialization2 = + registry.SerializeKey<NoIdSerialization>(NoIdKey(), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization2, IsOk()); + + registry.Reset(); + + ASSERT_THAT(registry.ParseParameters(NoIdSerialization()).status(), + StatusIs(absl::StatusCode::kNotFound)); + ASSERT_THAT( + registry.SerializeParameters<NoIdSerialization>(NoIdParams()).status(), + StatusIs(absl::StatusCode::kNotFound)); + ASSERT_THAT( + registry.ParseKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()) + .status(), + StatusIs(absl::StatusCode::kNotFound)); + ASSERT_THAT(registry + .SerializeKey<NoIdSerialization>( + NoIdKey(), InsecureSecretKeyAccess::Get()) + .status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(MutableSerializationRegistryTest, GlobalInstance) { + MutableSerializationRegistry::GlobalInstance().Reset(); + ParametersParserImpl<NoIdSerialization, NoIdParams> parser(kNoIdTypeUrl, + ParseNoIdParams); + ASSERT_THAT( + MutableSerializationRegistry::GlobalInstance().RegisterParametersParser( + &parser), + IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + MutableSerializationRegistry::GlobalInstance().ParseParameters( + NoIdSerialization()); + ASSERT_THAT(params, IsOk()); + EXPECT_THAT((*params)->HasIdRequirement(), IsFalse()); + EXPECT_THAT(std::type_index(typeid(**params)), + std::type_index(typeid(NoIdParams))); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/parameters_parser.h b/cc/internal/parameters_parser.h new file mode 100644 index 0000000..81c8eb0 --- /dev/null +++ b/cc/internal/parameters_parser.h
@@ -0,0 +1,114 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_PARAMETERS_PARSER_H_ +#define TINK_INTERNAL_PARAMETERS_PARSER_H_ + +#include <functional> +#include <memory> +#include <string> +#include <typeindex> +#include <utility> + +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "tink/internal/parser_index.h" +#include "tink/internal/serialization.h" +#include "tink/parameters.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Non-template base class that can be used with internal registry map. +class ParametersParser { + public: + // Parses `serialization` into a parameters object. + // + // This function is usually called on a `Serialization` subclass matching the + // value returned by `ObjectIdentifier()`. However, implementations should + // verify that this is the case. + virtual util::StatusOr<std::unique_ptr<Parameters>> ParseParameters( + const Serialization& serialization) const = 0; + + // Returns the object identifier for `SerializationT`, which is only valid + // for the lifetime of this object. + // + // The object identifier is a unique identifier per registry for this object + // (in the standard proto serialization, it is the type URL). In other words, + // when registering a `ParametersParser`, the registry will invoke this to get + // the handled object identifier. In order to parse an object of + // `SerializationT`, the registry will then obtain the object identifier of + // this serialization object, and call the parser corresponding to this + // object. + virtual absl::string_view ObjectIdentifier() const = 0; + + // Returns an index that can be used to look up the `ParametersParser` + // object registered for the `ParametersT` type in a registry. + virtual ParserIndex Index() const = 0; + + virtual ~ParametersParser() = default; +}; + +// Parses `SerializationT` objects into `ParametersT` objects. +template <typename SerializationT, typename ParametersT> +class ParametersParserImpl : public ParametersParser { + public: + explicit ParametersParserImpl( + absl::string_view object_identifier, + const std::function<util::StatusOr<ParametersT>(SerializationT)>& + function) + : object_identifier_(object_identifier), function_(function) {} + + util::StatusOr<std::unique_ptr<Parameters>> ParseParameters( + const Serialization& serialization) const override { + if (serialization.ObjectIdentifier() != object_identifier_) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Invalid object identifier for this parameters parser."); + } + const SerializationT* st = + dynamic_cast<const SerializationT*>(&serialization); + if (st == nullptr) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Invalid serialization type for this parameters parser."); + } + util::StatusOr<ParametersT> parameters = function_(*st); + if (!parameters.ok()) return parameters.status(); + return {absl::make_unique<ParametersT>(std::move(*parameters))}; + } + + absl::string_view ObjectIdentifier() const override { + return object_identifier_; + } + + ParserIndex Index() const override { + return ParserIndex::Create<SerializationT>(object_identifier_); + } + + private: + std::string object_identifier_; + std::function<util::StatusOr<ParametersT>(SerializationT)> function_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_PARAMETERS_PARSER_H_
diff --git a/cc/internal/parameters_parser_test.cc b/cc/internal/parameters_parser_test.cc new file mode 100644 index 0000000..9480b91 --- /dev/null +++ b/cc/internal/parameters_parser_test.cc
@@ -0,0 +1,90 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/parameters_parser.h" + +#include <memory> +#include <string_view> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "tink/internal/parser_index.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serialization_test_util.h" +#include "tink/parameters.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::testing::Eq; +using ::testing::IsFalse; + +TEST(ParametersParserTest, Create) { + std::unique_ptr<ParametersParser> parser = + absl::make_unique<ParametersParserImpl<NoIdSerialization, NoIdParams>>( + kNoIdTypeUrl, ParseNoIdParams); + + EXPECT_THAT(parser->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + EXPECT_THAT(parser->Index(), + Eq(ParserIndex::Create<NoIdSerialization>(kNoIdTypeUrl))); +} + +TEST(ParametersParserTest, ParseParameters) { + std::unique_ptr<ParametersParser> parser = + absl::make_unique<ParametersParserImpl<NoIdSerialization, NoIdParams>>( + kNoIdTypeUrl, ParseNoIdParams); + + NoIdSerialization serialization; + util::StatusOr<std::unique_ptr<Parameters>> params = + parser->ParseParameters(serialization); + ASSERT_THAT(params, IsOk()); + EXPECT_THAT((*params)->HasIdRequirement(), IsFalse()); +} + +TEST(ParametersParserTest, ParseParametersWithInvalidSerializationType) { + std::unique_ptr<ParametersParser> parser = + absl::make_unique<ParametersParserImpl<NoIdSerialization, NoIdParams>>( + kNoIdTypeUrl, ParseNoIdParams); + + IdParamsSerialization serialization; + util::StatusOr<std::unique_ptr<Parameters>> params = + parser->ParseParameters(serialization); + ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(ParametersParserTest, ParseParametersWithInvalidObjectIdentifier) { + std::unique_ptr<ParametersParser> parser = + absl::make_unique<ParametersParserImpl<NoIdSerialization, NoIdParams>>( + "mismatched_type_url", ParseNoIdParams); + + IdParamsSerialization serialization; + util::StatusOr<std::unique_ptr<Parameters>> params = + parser->ParseParameters(serialization); + ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/parameters_serializer.h b/cc/internal/parameters_serializer.h new file mode 100644 index 0000000..a26c803 --- /dev/null +++ b/cc/internal/parameters_serializer.h
@@ -0,0 +1,104 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_PARAMETERS_SERIALIZER_H_ +#define TINK_INTERNAL_PARAMETERS_SERIALIZER_H_ + +#include <functional> +#include <memory> +#include <string> +#include <typeindex> +#include <utility> + +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serializer_index.h" +#include "tink/parameters.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Non-template base class that can be used with internal registry map. +class ParametersSerializer { + public: + // Returns the serialization of `parameters`. + virtual util::StatusOr<std::unique_ptr<Serialization>> SerializeParameters( + const Parameters& parameters) const = 0; + + // Returns the object identifier for this serialization, which is only valid + // for the lifetime of this object. + // + // The object identifier is a unique identifier per registry for this object + // (in the standard proto serialization, it is the type URL). In other words, + // when registering a `ParametersSerializer`, the registry will invoke this to + // get the handled object identifier. In order to serialize an object of + // `ParametersT`, the registry will then obtain the object identifier of + // this serialization object, and call the serializer corresponding to this + // object. + virtual absl::string_view ObjectIdentifier() const = 0; + + // Returns an index that can be used to look up the `ParametersSerializer` + // object registered for the `ParametersT` type in a registry. + virtual SerializerIndex Index() const = 0; + + virtual ~ParametersSerializer() = default; +}; + +// Serializes `ParametersT` objects into `SerializationT` objects. +template <typename ParametersT, typename SerializationT> +class ParametersSerializerImpl : public ParametersSerializer { + public: + explicit ParametersSerializerImpl( + absl::string_view object_identifier, + const std::function<util::StatusOr<SerializationT>(ParametersT)>& + function) + : object_identifier_(object_identifier), function_(function) {} + + util::StatusOr<std::unique_ptr<Serialization>> SerializeParameters( + const Parameters& parameters) const override { + const ParametersT* pt = dynamic_cast<const ParametersT*>(¶meters); + if (pt == nullptr) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Invalid parameters type for this parameters serializer."); + } + util::StatusOr<SerializationT> serialization = function_(*pt); + if (!serialization.ok()) return serialization.status(); + return {absl::make_unique<SerializationT>(std::move(*serialization))}; + } + + absl::string_view ObjectIdentifier() const override { + return object_identifier_; + } + + SerializerIndex Index() const override { + return SerializerIndex::Create<ParametersT, SerializationT>(); + } + + private: + std::string object_identifier_; + std::function<util::StatusOr<SerializationT>(ParametersT)> function_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_PARAMETERS_SERIALIZER_H_
diff --git a/cc/internal/parameters_serializer_test.cc b/cc/internal/parameters_serializer_test.cc new file mode 100644 index 0000000..0c76e08 --- /dev/null +++ b/cc/internal/parameters_serializer_test.cc
@@ -0,0 +1,79 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/parameters_serializer.h" + +#include <memory> +#include <string_view> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serialization_test_util.h" +#include "tink/internal/serializer_index.h" +#include "tink/parameters.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::testing::Eq; + +TEST(ParametersSerializerTest, Create) { + std::unique_ptr<ParametersSerializer> serializer = absl::make_unique< + ParametersSerializerImpl<NoIdParams, NoIdSerialization>>( + kNoIdTypeUrl, SerializeNoIdParams); + + EXPECT_THAT(serializer->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + EXPECT_THAT(serializer->Index(), + Eq(SerializerIndex::Create<NoIdParams, NoIdSerialization>())); +} + +TEST(ParametersSerializerTest, SerializeParameters) { + std::unique_ptr<ParametersSerializer> serializer = absl::make_unique< + ParametersSerializerImpl<NoIdParams, NoIdSerialization>>( + kNoIdTypeUrl, SerializeNoIdParams); + + NoIdParams parameters; + util::StatusOr<std::unique_ptr<Serialization>> serialization = + serializer->SerializeParameters(parameters); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); +} + +TEST(ParametersSerializerTest, SerializeParametersWithInvalidParametersType) { + std::unique_ptr<ParametersSerializer> serializer = absl::make_unique< + ParametersSerializerImpl<NoIdParams, NoIdSerialization>>( + kNoIdTypeUrl, SerializeNoIdParams); + + IdParams parameters; + util::StatusOr<std::unique_ptr<Serialization>> serialization = + serializer->SerializeParameters(parameters); + ASSERT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/parser_index.h b/cc/internal/parser_index.h new file mode 100644 index 0000000..2ddb52d --- /dev/null +++ b/cc/internal/parser_index.h
@@ -0,0 +1,71 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_PARSER_INDEX_H_ +#define TINK_INTERNAL_PARSER_INDEX_H_ + +#include <string> +#include <typeindex> + +#include "absl/strings/string_view.h" +#include "tink/internal/serialization.h" + +namespace crypto { +namespace tink { +namespace internal { + +class ParserIndex { + public: + // Create registry lookup key for a `SerializationT` type object with + // `object_identifier`. Useful for key and parameters parsers. + template <typename SerializationT> + static ParserIndex Create(absl::string_view object_identifier) { + return ParserIndex(std::type_index(typeid(SerializationT)), + object_identifier); + } + + // Create registry lookup key for `serialization`. Useful for the + // serialization registry. + static ParserIndex Create(const Serialization& serialization) { + return ParserIndex(std::type_index(typeid(serialization)), + serialization.ObjectIdentifier()); + } + + // Returns true if serialization type index and object identifier match. + bool operator==(const ParserIndex& other) const { + return index_ == other.index_ && + object_identifier_ == other.object_identifier_; + } + + // Required function to make `ParserIndex` hashable for Abseil hash maps. + template <typename H> + friend H AbslHashValue(H h, const ParserIndex& index) { + return H::combine(std::move(h), index.index_, index.object_identifier_); + } + + private: + ParserIndex(std::type_index index, absl::string_view object_identifier) + : index_(index), object_identifier_(object_identifier) {} + + std::type_index index_; + std::string object_identifier_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_PARSER_INDEX_H_
diff --git a/cc/internal/parser_index_test.cc b/cc/internal/parser_index_test.cc new file mode 100644 index 0000000..583b0ff --- /dev/null +++ b/cc/internal/parser_index_test.cc
@@ -0,0 +1,79 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/parser_index.h" + +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "tink/internal/serialization.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::testing::Eq; +using ::testing::Not; + +class ExampleSerialization : public Serialization { + public: + explicit ExampleSerialization(absl::string_view object_identifier) + : object_identifier_(object_identifier) {} + + absl::string_view ObjectIdentifier() const override { + return object_identifier_; + } + + protected: + std::string object_identifier_; +}; + +class DifferentSerialization : public ExampleSerialization { + public: + explicit DifferentSerialization(absl::string_view object_identifier) + : ExampleSerialization(object_identifier) {} +}; + +TEST(ParserIndex, CreateEquivalent) { + ASSERT_THAT(ParserIndex::Create<ExampleSerialization>("id"), + Eq(ParserIndex::Create<ExampleSerialization>("id"))); + ASSERT_THAT(ParserIndex::Create<ExampleSerialization>("id"), + Eq(ParserIndex::Create(ExampleSerialization("id")))); + ASSERT_THAT(ParserIndex::Create(ExampleSerialization("id")), + Eq(ParserIndex::Create(ExampleSerialization("id")))); +} + +TEST(ParserIndex, CreateWithDifferentObjectIdentifier) { + ASSERT_THAT( + ParserIndex::Create<ExampleSerialization>("id"), + Not(Eq(ParserIndex::Create<ExampleSerialization>("different id")))); + ASSERT_THAT( + ParserIndex::Create(ExampleSerialization("id")), + Not(Eq(ParserIndex::Create(ExampleSerialization("different id"))))); +} + +TEST(ParserIndex, CreateWithDifferentSerializationType) { + ASSERT_THAT(ParserIndex::Create<ExampleSerialization>("id"), + Not(Eq(ParserIndex::Create<DifferentSerialization>("id")))); + ASSERT_THAT(ParserIndex::Create(ExampleSerialization("id")), + Not(Eq(ParserIndex::Create(DifferentSerialization("id"))))); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/proto_key_serialization.cc b/cc/internal/proto_key_serialization.cc new file mode 100644 index 0000000..f92eb02 --- /dev/null +++ b/cc/internal/proto_key_serialization.cc
@@ -0,0 +1,76 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/proto_key_serialization.h" + +#include <string> +#include <utility> + +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/internal/util.h" +#include "tink/restricted_data.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; + +util::StatusOr<ProtoKeySerialization> ProtoKeySerialization::Create( + absl::string_view type_url, RestrictedData serialized_key, + KeyData::KeyMaterialType key_material_type, + OutputPrefixType output_prefix_type, absl::optional<int> id_requirement) { + if (!IsPrintableAscii(type_url)) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Non-printable ASCII character in type URL."); + } + if (output_prefix_type == OutputPrefixType::RAW && + id_requirement.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Keys with a RAW output prefix type should not have an " + "ID requirement."); + } + if (output_prefix_type != OutputPrefixType::RAW && + !id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Keys without a RAW output prefix type should have an ID requirement."); + } + return ProtoKeySerialization(type_url, type_url, std::move(serialized_key), + key_material_type, output_prefix_type, + id_requirement); +} + +bool ProtoKeySerialization::EqualsWithPotentialFalseNegatives( + const ProtoKeySerialization& other) const { + if (type_url_ != other.type_url_) return false; + if (object_identifier_ != other.object_identifier_) return false; + if (key_material_type_ != other.key_material_type_) return false; + if (output_prefix_type_ != other.output_prefix_type_) return false; + if (id_requirement_ != other.id_requirement_) return false; + // RestrictedData::operator== is a constant-time comparison. + return serialized_key_ == other.serialized_key_; +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/proto_key_serialization.h b/cc/internal/proto_key_serialization.h new file mode 100644 index 0000000..7c3972a --- /dev/null +++ b/cc/internal/proto_key_serialization.h
@@ -0,0 +1,110 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_PROTO_KEY_SERIALIZATION_H_ +#define TINK_INTERNAL_PROTO_KEY_SERIALIZATION_H_ + +#include <string> +#include <utility> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/internal/serialization.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Represents a `Key` object serialized with binary protocol buffer +// serialization. +class ProtoKeySerialization : public Serialization { + public: + // Copyable and movable. + ProtoKeySerialization(const ProtoKeySerialization& other) = default; + ProtoKeySerialization& operator=(const ProtoKeySerialization& other) = + default; + ProtoKeySerialization(ProtoKeySerialization&& other) = default; + ProtoKeySerialization& operator=(ProtoKeySerialization&& other) = default; + + // Creates a `ProtoKeySerialization` object from individual components. + static util::StatusOr<ProtoKeySerialization> Create( + absl::string_view type_url, RestrictedData serialized_key, + google::crypto::tink::KeyData::KeyMaterialType key_material_type, + google::crypto::tink::OutputPrefixType output_prefix_type, + absl::optional<int> id_requirement); + + // Returned value is only valid for the lifetime of this object. + absl::string_view TypeUrl() const { return type_url_; } + + // Returned value is only valid for the lifetime of this object. + absl::string_view ObjectIdentifier() const override { + return object_identifier_; + } + + // Returned value is only valid for the lifetime of this object. + RestrictedData SerializedKeyProto() const { return serialized_key_; } + + google::crypto::tink::KeyData::KeyMaterialType KeyMaterialType() const { + return key_material_type_; + } + + google::crypto::tink::OutputPrefixType GetOutputPrefixType() const { + return output_prefix_type_; + } + + absl::optional<int> IdRequirement() const { return id_requirement_; } + + private: + friend class ProtoKeySerializationTest; + friend class LegacyProtoKey; + friend class LegacyProtoKeyTest; + + ProtoKeySerialization( + absl::string_view type_url, absl::string_view object_identifier, + RestrictedData serialized_key, + google::crypto::tink::KeyData::KeyMaterialType key_material_type, + google::crypto::tink::OutputPrefixType output_prefix_type, + absl::optional<int> id_requirement) + : type_url_(type_url), + object_identifier_(object_identifier), + serialized_key_(std::move(serialized_key)), + key_material_type_(key_material_type), + output_prefix_type_(output_prefix_type), + id_requirement_(id_requirement) {} + + // Returns `true` if this `ProtoKeySerialization` object is equal to + // `other` (with the possibility of false negatives due to lack of + // determinism during serialization). Should only be used temporarily by the + // `LegacyKeyParameters` class. + bool EqualsWithPotentialFalseNegatives( + const ProtoKeySerialization& other) const; + + std::string type_url_; + std::string object_identifier_; + RestrictedData serialized_key_; + google::crypto::tink::KeyData::KeyMaterialType key_material_type_; + google::crypto::tink::OutputPrefixType output_prefix_type_; + absl::optional<int> id_requirement_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_PROTO_KEY_SERIALIZATION_H_
diff --git a/cc/internal/proto_key_serialization_test.cc b/cc/internal/proto_key_serialization_test.cc new file mode 100644 index 0000000..89eff96 --- /dev/null +++ b/cc/internal/proto_key_serialization_test.cc
@@ -0,0 +1,213 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/proto_key_serialization.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsFalse; +using ::testing::IsTrue; + +class ProtoKeySerializationTest : public ::testing::Test { + protected: + bool Equals(ProtoKeySerialization serialization, + ProtoKeySerialization other) { + return serialization.EqualsWithPotentialFalseNegatives(other); + } +}; + +TEST_F(ProtoKeySerializationTest, CreateWithIdRequirement) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + EXPECT_THAT(serialization->TypeUrl(), Eq("type_url")); + EXPECT_THAT(serialization->SerializedKeyProto(), Eq(serialized_key)); + EXPECT_THAT(serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(serialization->GetOutputPrefixType(), Eq(OutputPrefixType::TINK)); + EXPECT_THAT(serialization->IdRequirement(), Eq(12345)); + EXPECT_THAT(serialization->ObjectIdentifier(), Eq("type_url")); +} + +TEST_F(ProtoKeySerializationTest, CreateWithoutIdRequirement) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::RAW, + /*id_requirement=*/absl::nullopt); + ASSERT_THAT(serialization.status(), IsOk()); + + EXPECT_THAT(serialization->TypeUrl(), Eq("type_url")); + EXPECT_THAT(serialization->SerializedKeyProto(), Eq(serialized_key)); + EXPECT_THAT(serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(serialization->GetOutputPrefixType(), Eq(OutputPrefixType::RAW)); + EXPECT_THAT(serialization->IdRequirement(), Eq(absl::nullopt)); + EXPECT_THAT(serialization->ObjectIdentifier(), Eq("type_url")); +} + +TEST_F(ProtoKeySerializationTest, OutputPrefixIncompatibleWithIdRequirement) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> tink_without_id = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/absl::nullopt); + ASSERT_THAT(tink_without_id.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + + util::StatusOr<ProtoKeySerialization> raw_with_id = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::RAW, + /*id_requirement=*/12345); + ASSERT_THAT(raw_with_id.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(ProtoKeySerializationTest, Equals) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(other_serialization.status(), IsOk()); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsTrue()); +} + +TEST_F(ProtoKeySerializationTest, TypeUrlAndObjectIdentifierNotEqual) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("different_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(other_serialization.status(), IsOk()); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsFalse()); +} + +TEST_F(ProtoKeySerializationTest, SerializedKeyNotEqual) { + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create( + "type_url", + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()), + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create( + "type_url", + RestrictedData("different_key", InsecureSecretKeyAccess::Get()), + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(other_serialization.status(), IsOk()); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsFalse()); +} + +TEST_F(ProtoKeySerializationTest, KeyMaterialTypeNotEqual) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("type_url", serialized_key, KeyData::REMOTE, + OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(other_serialization.status(), IsOk()); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsFalse()); +} + +TEST_F(ProtoKeySerializationTest, OutputPrefixTypeNotEqual) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, + OutputPrefixType::CRUNCHY, + /*id_requirement=*/12345); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsFalse()); +} + +TEST_F(ProtoKeySerializationTest, IdRequirementNotEqual) { + RestrictedData serialized_key = + RestrictedData("serialized_key", InsecureSecretKeyAccess::Get()); + util::StatusOr<ProtoKeySerialization> serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/12345); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoKeySerialization> other_serialization = + ProtoKeySerialization::Create("type_url", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/6789); + ASSERT_THAT(other_serialization.status(), IsOk()); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsFalse()); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/proto_parameters_serialization.cc b/cc/internal/proto_parameters_serialization.cc new file mode 100644 index 0000000..f3bd4bd --- /dev/null +++ b/cc/internal/proto_parameters_serialization.cc
@@ -0,0 +1,84 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/proto_parameters_serialization.h" + +#include <string> + +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "tink/internal/util.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; + +util::StatusOr<ProtoParametersSerialization> +ProtoParametersSerialization::Create(absl::string_view type_url, + OutputPrefixType output_prefix_type, + absl::string_view serialized_proto) { + if (!IsPrintableAscii(type_url)) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Non-printable ASCII character in type URL."); + } + KeyTemplate key_template; + key_template.set_type_url(std::string(type_url)); + key_template.set_output_prefix_type(output_prefix_type); + key_template.set_value(std::string(serialized_proto)); + return ProtoParametersSerialization(key_template); +} + +util::StatusOr<ProtoParametersSerialization> +ProtoParametersSerialization::Create(KeyTemplate key_template) { + if (!IsPrintableAscii(key_template.type_url())) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Non-printable ASCII character in type URL."); + } + return ProtoParametersSerialization(key_template); +} + +bool ProtoParametersSerialization::EqualsWithPotentialFalseNegatives( + const ProtoParametersSerialization& other) const { + const ProtoParametersSerialization* that = + dynamic_cast<const ProtoParametersSerialization*>(&other); + if (that == nullptr) { + return false; + } + if (key_template_.type_url() != that->key_template_.type_url()) { + return false; + } + if (key_template_.output_prefix_type() != + that->key_template_.output_prefix_type()) { + return false; + } + if (key_template_.value() != that->key_template_.value()) { + return false; + } + if (object_identifier_ != that->object_identifier_) { + return false; + } + return true; +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/proto_parameters_serialization.h b/cc/internal/proto_parameters_serialization.h new file mode 100644 index 0000000..cb32d8f --- /dev/null +++ b/cc/internal/proto_parameters_serialization.h
@@ -0,0 +1,89 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_PROTO_PARAMETERS_SERIALIZATION_H_ +#define TINK_INTERNAL_PROTO_PARAMETERS_SERIALIZATION_H_ + +#include <string> + +#include "absl/strings/string_view.h" +#include "tink/internal/serialization.h" +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Represents a `Parameters` object serialized with binary protocol buffer +// serialization. +class ProtoParametersSerialization : public Serialization { + public: + // Copyable and movable. + ProtoParametersSerialization(const ProtoParametersSerialization& other) = + default; + ProtoParametersSerialization& operator=( + const ProtoParametersSerialization& other) = default; + ProtoParametersSerialization(ProtoParametersSerialization&& other) = default; + ProtoParametersSerialization& operator=( + ProtoParametersSerialization&& other) = default; + + // Creates a `ProtoParametersSerialization` object from individual components. + static util::StatusOr<ProtoParametersSerialization> Create( + absl::string_view type_url, + google::crypto::tink::OutputPrefixType output_prefix_type, + absl::string_view serialized_proto); + + // Creates a `ProtoParametersSerialization` object from a key template. + static util::StatusOr<ProtoParametersSerialization> Create( + google::crypto::tink::KeyTemplate key_template); + + const google::crypto::tink::KeyTemplate& GetKeyTemplate() const { + return key_template_; + } + + absl::string_view ObjectIdentifier() const override { + return object_identifier_; + } + + private: + // The following friend classes require access to + // `EqualsWithPotentialFalseNegatives()`. + friend class ProtoParametersSerializationTest; + friend class LegacyProtoParameters; + friend class LegacyProtoParametersTest; + + explicit ProtoParametersSerialization( + google::crypto::tink::KeyTemplate key_template) + : key_template_(key_template), + object_identifier_(key_template.type_url()) {} + + // Returns `true` if this `ProtoParametersSerialization` object is equal to + // `other` (with the possibility of false negatives due to lack of + // determinism during serialization). Should only be used temporarily by the + // `LegacyProtoParameters` class. + bool EqualsWithPotentialFalseNegatives( + const ProtoParametersSerialization& other) const; + + google::crypto::tink::KeyTemplate key_template_; + std::string object_identifier_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_PROTO_PARAMETERS_SERIALIZATION_H_
diff --git a/cc/internal/proto_parameters_serialization_test.cc b/cc/internal/proto_parameters_serialization_test.cc new file mode 100644 index 0000000..1b13411 --- /dev/null +++ b/cc/internal/proto_parameters_serialization_test.cc
@@ -0,0 +1,162 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/proto_parameters_serialization.h" + +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "proto/test_proto.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::crypto::tink::test::IsOk; +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; +using ::google::crypto::tink::TestProto; +using ::testing::Eq; +using ::testing::IsFalse; +using ::testing::IsTrue; + +class ProtoParametersSerializationTest : public ::testing::Test { + protected: + bool Equals(ProtoParametersSerialization serialization, + ProtoParametersSerialization other) { + return serialization.EqualsWithPotentialFalseNegatives(other); + } +}; + +TEST_F(ProtoParametersSerializationTest, CreateFromIndividualComponents) { + TestProto test_proto; + test_proto.set_num(12345); + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + EXPECT_THAT(serialization->ObjectIdentifier(), "type_url"); + EXPECT_THAT(serialization->GetKeyTemplate().type_url(), "type_url"); + EXPECT_THAT(serialization->GetKeyTemplate().output_prefix_type(), + OutputPrefixType::RAW); + EXPECT_THAT(serialization->GetKeyTemplate().value(), + test_proto.SerializeAsString()); + TestProto parsed_proto; + parsed_proto.ParseFromString(serialization->GetKeyTemplate().value()); + EXPECT_THAT(parsed_proto.num(), Eq(12345)); +} + +TEST_F(ProtoParametersSerializationTest, CreateFromKeyTemplate) { + TestProto test_proto; + test_proto.set_num(12345); + KeyTemplate key_template; + key_template.set_value(test_proto.SerializeAsString()); + key_template.set_output_prefix_type(OutputPrefixType::TINK); + key_template.set_type_url("type_url"); + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create(key_template); + ASSERT_THAT(serialization.status(), IsOk()); + + EXPECT_THAT(serialization->ObjectIdentifier(), "type_url"); + EXPECT_THAT(serialization->GetKeyTemplate().type_url(), "type_url"); + EXPECT_THAT(serialization->GetKeyTemplate().output_prefix_type(), + OutputPrefixType::TINK); + EXPECT_THAT(serialization->GetKeyTemplate().value(), + test_proto.SerializeAsString()); + TestProto parsed_proto; + parsed_proto.ParseFromString(serialization->GetKeyTemplate().value()); + EXPECT_THAT(parsed_proto.num(), Eq(12345)); +} + +TEST_F(ProtoParametersSerializationTest, Equals) { + TestProto test_proto; + test_proto.set_num(12345); + + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoParametersSerialization> other_serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(other_serialization.status(), IsOk()); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsTrue()); +} + +TEST_F(ProtoParametersSerializationTest, TypeUrlNotEqual) { + TestProto test_proto; + test_proto.set_num(12345); + + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + + util::StatusOr<ProtoParametersSerialization> other_serialization = + ProtoParametersSerialization::Create("other_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(other_serialization.status(), IsOk()); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsFalse()); +} + +TEST_F(ProtoParametersSerializationTest, OutputPrefixTypeNotEqual) { + TestProto test_proto; + test_proto.set_num(12345); + + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoParametersSerialization> other_serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::TINK, + test_proto.SerializeAsString()); + ASSERT_THAT(other_serialization.status(), IsOk()); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsFalse()); +} + +TEST_F(ProtoParametersSerializationTest, DifferentValueNotEqual) { + TestProto test_proto; + test_proto.set_num(12345); + TestProto other_proto; + other_proto.set_num(67890); + + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + test_proto.SerializeAsString()); + ASSERT_THAT(serialization.status(), IsOk()); + + util::StatusOr<ProtoParametersSerialization> other_serialization = + ProtoParametersSerialization::Create("type_url", OutputPrefixType::RAW, + other_proto.SerializeAsString()); + ASSERT_THAT(other_serialization.status(), IsOk()); + + EXPECT_THAT(Equals(*serialization, *other_serialization), IsFalse()); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/registry_impl.cc b/cc/internal/registry_impl.cc index 2b68992..ba74919 100644 --- a/cc/internal/registry_impl.cc +++ b/cc/internal/registry_impl.cc
@@ -13,58 +13,66 @@ // limitations under the License. // /////////////////////////////////////////////////////////////////////////////// + #include "tink/internal/registry_impl.h" +#include <functional> +#include <memory> #include <utility> #include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "tink/input_stream.h" +#include "tink/internal/keyset_wrapper_store.h" +#include "tink/key_manager.h" #include "tink/monitoring/monitoring.h" #include "tink/util/errors.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" #include "proto/tink.pb.h" -using crypto::tink::util::StatusOr; -using google::crypto::tink::KeyData; -using google::crypto::tink::KeyTemplate; - namespace crypto { namespace tink { namespace internal { using ::crypto::tink::MonitoringClientFactory; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::KeyTemplate; -StatusOr<const RegistryImpl::KeyTypeInfo*> RegistryImpl::get_key_type_info( +util::StatusOr<const KeyTypeInfoStore::Info*> RegistryImpl::get_key_type_info( absl::string_view type_url) const { absl::MutexLock lock(&maps_mutex_); - auto it = type_url_to_info_.find(type_url); - if (it == type_url_to_info_.end()) { - return ToStatusF(absl::StatusCode::kNotFound, - "No manager for type '%s' has been registered.", type_url); - } - return &it->second; + return key_type_info_store_.Get(type_url); } -StatusOr<std::unique_ptr<KeyData>> RegistryImpl::NewKeyData( +util::StatusOr<std::unique_ptr<KeyData>> RegistryImpl::NewKeyData( const KeyTemplate& key_template) const { - auto key_type_info_or = get_key_type_info(key_template.type_url()); - if (!key_type_info_or.ok()) return key_type_info_or.status(); - if (!key_type_info_or.value()->new_key_allowed()) { + util::StatusOr<const internal::KeyTypeInfoStore::Info*> info = + get_key_type_info(key_template.type_url()); + if (!info.ok()) { + return info.status(); + } + if (!(*info)->new_key_allowed()) { return crypto::tink::util::Status( absl::StatusCode::kInvalidArgument, absl::StrCat("KeyManager for type ", key_template.type_url(), " does not allow for creation of new keys.")); } - return key_type_info_or.value()->key_factory().NewKeyData( - key_template.value()); + return (*info)->key_factory().NewKeyData(key_template.value()); } -StatusOr<std::unique_ptr<KeyData>> RegistryImpl::GetPublicKeyData( +util::StatusOr<std::unique_ptr<KeyData>> RegistryImpl::GetPublicKeyData( absl::string_view type_url, absl::string_view serialized_private_key) const { - auto key_type_info_or = get_key_type_info(type_url); - if (!key_type_info_or.ok()) return key_type_info_or.status(); - auto factory = dynamic_cast<const PrivateKeyFactory*>( - &key_type_info_or.value()->key_factory()); + util::StatusOr<const internal::KeyTypeInfoStore::Info*> info = + get_key_type_info(type_url); + if (!info.ok()) { + return info.status(); + } + auto factory = + dynamic_cast<const PrivateKeyFactory*>(&(*info)->key_factory()); if (factory == nullptr) { return ToStatusF(absl::StatusCode::kInvalidArgument, "KeyManager for type '%s' does not have " @@ -75,41 +83,20 @@ return result; } -crypto::tink::util::Status RegistryImpl::CheckInsertable( - absl::string_view type_url, const std::type_index& key_manager_type_index, - bool new_key_allowed) const { - auto it = type_url_to_info_.find(type_url); - - if (it == type_url_to_info_.end()) { - return crypto::tink::util::OkStatus(); +util::StatusOr<KeyData> RegistryImpl::DeriveKey(const KeyTemplate& key_template, + InputStream* randomness) const { + util::StatusOr<const internal::KeyTypeInfoStore::Info*> info = + get_key_type_info(key_template.type_url()); + if (!info.ok()) { + return info.status(); } - if (it->second.key_manager_type_index() != key_manager_type_index) { - return ToStatusF(absl::StatusCode::kAlreadyExists, - "A manager for type '%s' has been already registered.", - type_url); - } - if (!it->second.new_key_allowed() && new_key_allowed) { - return ToStatusF(absl::StatusCode::kAlreadyExists, - "A manager for type '%s' has been already registered " - "with forbidden new key operation.", - type_url); - } - return crypto::tink::util::OkStatus(); -} - -crypto::tink::util::StatusOr<google::crypto::tink::KeyData> -RegistryImpl::DeriveKey(const google::crypto::tink::KeyTemplate& key_template, - InputStream* randomness) const { - auto key_type_info_or = get_key_type_info(key_template.type_url()); - if (!key_type_info_or.ok()) return key_type_info_or.status(); - if (!key_type_info_or.value()->key_deriver()) { + if (!(*info)->key_deriver()) { return crypto::tink::util::Status( absl::StatusCode::kInvalidArgument, absl::StrCat("Manager for type '", key_template.type_url(), "' cannot derive keys.")); } - return key_type_info_or.value()->key_deriver()(key_template.value(), - randomness); + return (*info)->key_deriver()(key_template.value(), randomness); } util::Status RegistryImpl::RegisterMonitoringClientFactory( @@ -126,9 +113,8 @@ void RegistryImpl::Reset() { { absl::MutexLock lock(&maps_mutex_); - type_url_to_info_.clear(); - name_to_catalogue_map_.clear(); - primitive_to_wrapper_.clear(); + key_type_info_store_ = KeyTypeInfoStore(); + keyset_wrapper_store_ = KeysetWrapperStore(); } { absl::MutexLock lock(&monitoring_factory_mutex_);
diff --git a/cc/internal/registry_impl.h b/cc/internal/registry_impl.h index 5992b82..4bf79c1 100644 --- a/cc/internal/registry_impl.h +++ b/cc/internal/registry_impl.h
@@ -13,44 +13,34 @@ // limitations under the License. // /////////////////////////////////////////////////////////////////////////////// + #ifndef TINK_INTERNAL_REGISTRY_IMPL_H_ #define TINK_INTERNAL_REGISTRY_IMPL_H_ #include <algorithm> -#include <functional> -#include <initializer_list> #include <memory> #include <string> -#include <tuple> -#include <typeindex> -#include <typeinfo> #include <utility> #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/memory/memory.h" #include "absl/status/status.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" -#include "absl/types/optional.h" -#include "tink/catalogue.h" -#include "tink/core/key_manager_impl.h" #include "tink/core/key_type_manager.h" -#include "tink/core/private_key_manager_impl.h" #include "tink/core/private_key_type_manager.h" +#include "tink/input_stream.h" #include "tink/internal/fips_utils.h" +#include "tink/internal/key_type_info_store.h" #include "tink/internal/keyset_wrapper.h" -#include "tink/internal/keyset_wrapper_impl.h" +#include "tink/internal/keyset_wrapper_store.h" #include "tink/key_manager.h" #include "tink/monitoring/monitoring.h" #include "tink/primitive_set.h" #include "tink/primitive_wrapper.h" -#include "tink/util/errors.h" -#include "tink/util/protobuf_helper.h" #include "tink/util/status.h" -#include "tink/util/validation.h" +#include "tink/util/statusor.h" #include "proto/tink.pb.h" namespace crypto { @@ -68,17 +58,9 @@ RegistryImpl(const RegistryImpl&) = delete; RegistryImpl& operator=(const RegistryImpl&) = delete; - template <class P> - crypto::tink::util::StatusOr<const Catalogue<P>*> get_catalogue( - absl::string_view catalogue_name) const ABSL_LOCKS_EXCLUDED(maps_mutex_); - - template <class P> - crypto::tink::util::Status AddCatalogue(absl::string_view catalogue_name, - Catalogue<P>* catalogue) - ABSL_LOCKS_EXCLUDED(maps_mutex_); - // Registers the given 'manager' for the key type 'manager->get_key_type()'. - // Takes ownership of 'manager', which must be non-nullptr. + // Takes ownership of 'manager', which must be non-nullptr. KeyManager is the + // legacy/internal version of KeyTypeManager. template <class P> crypto::tink::util::Status RegisterKeyManager(KeyManager<P>* manager, bool new_key_allowed = true) @@ -91,15 +73,15 @@ manager, bool new_key_allowed) ABSL_LOCKS_EXCLUDED(maps_mutex_); - // Takes ownership of 'private_key_manager' and 'public_key_manager'. Both - // must be non-nullptr. + // Takes ownership of 'private_manager' and 'public_manager'. Both must be + // non-nullptr. template <class PrivateKeyProto, class KeyFormatProto, class PublicKeyProto, class PrivatePrimitivesList, class PublicPrimitivesList> crypto::tink::util::Status RegisterAsymmetricKeyManagers( PrivateKeyTypeManager<PrivateKeyProto, KeyFormatProto, PublicKeyProto, - PrivatePrimitivesList>* private_key_manager, + PrivatePrimitivesList>* private_manager, KeyTypeManager<PublicKeyProto, void, PublicPrimitivesList>* - public_key_manager, + public_manager, bool new_key_allowed) ABSL_LOCKS_EXCLUDED(maps_mutex_); template <class P> @@ -116,11 +98,6 @@ const google::crypto::tink::KeyData& key_data) const ABSL_LOCKS_EXCLUDED(maps_mutex_); - template <class P> - crypto::tink::util::StatusOr<std::unique_ptr<P>> GetPrimitive( - absl::string_view type_url, const portable_proto::MessageLite& key) const - ABSL_LOCKS_EXCLUDED(maps_mutex_); - crypto::tink::util::StatusOr<std::unique_ptr<google::crypto::tink::KeyData>> NewKeyData(const google::crypto::tink::KeyTemplate& key_template) const ABSL_LOCKS_EXCLUDED(maps_mutex_); @@ -166,279 +143,23 @@ } private: - // All information for a given type url. - class KeyTypeInfo { - public: - // Takes ownership of the 'key_manager'. - template <typename P> - KeyTypeInfo(KeyManager<P>* key_manager, bool new_key_allowed) - : key_manager_type_index_(std::type_index(typeid(*key_manager))), - public_key_manager_type_index_(absl::nullopt), - new_key_allowed_(new_key_allowed), - internal_key_factory_(nullptr), - key_factory_(&key_manager->get_key_factory()), - key_type_manager_(nullptr) { - primitive_to_manager_.emplace(std::type_index(typeid(P)), - absl::WrapUnique(key_manager)); - } - - // Takes ownership of the 'key_manager'. - template <typename KeyProto, typename KeyFormatProto, - typename... Primitives> - KeyTypeInfo(KeyTypeManager<KeyProto, KeyFormatProto, List<Primitives...>>* - key_manager, - bool new_key_allowed) - : key_manager_type_index_(std::type_index(typeid(*key_manager))), - public_key_manager_type_index_(absl::nullopt), - new_key_allowed_(new_key_allowed), - internal_key_factory_( - absl::make_unique<internal::KeyFactoryImpl<KeyTypeManager< - KeyProto, KeyFormatProto, List<Primitives...>>>>( - key_manager)), - key_factory_(internal_key_factory_.get()), - key_deriver_(CreateDeriverFunctionFor(key_manager)), - key_type_manager_(absl::WrapUnique(key_manager)) { - // TODO(C++17) replace with a fold expression - (void)std::initializer_list<int>{ - 0, (primitive_to_manager_.emplace( - std::type_index(typeid(Primitives)), - internal::MakeKeyManager<Primitives>(key_manager)), - 0)...}; - } - - // Takes ownership of the 'private_key_manager', but *not* of the - // 'public_key_manager'. The public_key_manager must only be alive for the - // duration of the constructor. - template <typename PrivateKeyProto, typename KeyFormatProto, - typename PublicKeyProto, typename PublicPrimitivesList, - typename... PrivatePrimitives> - KeyTypeInfo( - PrivateKeyTypeManager<PrivateKeyProto, KeyFormatProto, PublicKeyProto, - List<PrivatePrimitives...>>* private_key_manager, - KeyTypeManager<PublicKeyProto, void, PublicPrimitivesList>* - public_key_manager, - bool new_key_allowed) - : key_manager_type_index_( - std::type_index(typeid(*private_key_manager))), - public_key_manager_type_index_( - std::type_index(typeid(*public_key_manager))), - new_key_allowed_(new_key_allowed), - internal_key_factory_( - absl::make_unique<internal::PrivateKeyFactoryImpl< - PrivateKeyProto, KeyFormatProto, PublicKeyProto, - List<PrivatePrimitives...>, PublicPrimitivesList>>( - private_key_manager, public_key_manager)), - key_factory_(internal_key_factory_.get()), - key_deriver_(CreateDeriverFunctionFor(private_key_manager)), - key_type_manager_(absl::WrapUnique(private_key_manager)) { - // TODO(C++17) replace with a fold expression - (void)std::initializer_list<int>{ - 0, (primitive_to_manager_.emplace( - std::type_index(typeid(PrivatePrimitives)), - internal::MakePrivateKeyManager<PrivatePrimitives>( - private_key_manager, public_key_manager)), - 0)...}; - } - - template <typename P> - crypto::tink::util::StatusOr<const KeyManager<P>*> get_key_manager( - absl::string_view requested_type_url) const { - auto it = primitive_to_manager_.find(std::type_index(typeid(P))); - if (it == primitive_to_manager_.end()) { - return crypto::tink::util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat( - "Primitive type ", typeid(P).name(), - " not among supported primitives ", - absl::StrJoin( - primitive_to_manager_.begin(), primitive_to_manager_.end(), - ", ", - [](std::string* out, - const std::pair<const std::type_index, - std::unique_ptr<KeyManagerBase>>& kv) { - absl::StrAppend(out, kv.first.name()); - }), - " for type URL ", requested_type_url)); - } - return static_cast<const KeyManager<P>*>(it->second.get()); - } - - const std::type_index& key_manager_type_index() const { - return key_manager_type_index_; - } - - const absl::optional<std::type_index>& public_key_manager_type_index() - const { - return public_key_manager_type_index_; - } - - bool new_key_allowed() const { return new_key_allowed_; } - void set_new_key_allowed(bool b) { new_key_allowed_ = b; } - - const KeyFactory& key_factory() const { return *key_factory_; } - - const std::function<crypto::tink::util::StatusOr< - google::crypto::tink::KeyData>(absl::string_view, InputStream*)>& - key_deriver() const { - return key_deriver_; - } - - private: - // dynamic std::type_index of the actual key manager class for which this - // key was inserted. - std::type_index key_manager_type_index_; - // dynamic std::type_index of the public key manager corresponding to this - // class, in case it was inserted using RegisterAsymmetricKeyManagers, - // nullopt otherwise. - absl::optional<std::type_index> public_key_manager_type_index_; - - // For each primitive, the corresponding names and key_manager. - absl::flat_hash_map<std::type_index, std::unique_ptr<KeyManagerBase>> - primitive_to_manager_; - // Whether the key manager allows creating new keys. - bool new_key_allowed_; - // A factory constructed from an internal key manager. Owned version of - // key_factory if constructed with a KeyTypeManager. This is nullptr if - // constructed with a KeyManager. - std::unique_ptr<const KeyFactory> internal_key_factory_; - // Unowned copy of internal_key_factory, always different from - // nullptr. - const KeyFactory* key_factory_; - // A function to call to derive a key. If the container was constructed with - // a KeyTypeManager which has non-void keyformat type, this will forward to - // the function DeriveKey of this container. Otherwise, the function is - // 'empty', i.e., "key_deriver_" will cast to false when cast to a bool. - std::function<crypto::tink::util::StatusOr<google::crypto::tink::KeyData>( - absl::string_view, InputStream*)> - key_deriver_; - // The owned pointer in case we use a KeyTypeManager, nullptr if - // constructed with a KeyManager. - const std::shared_ptr<void> key_type_manager_; - }; - - class WrapperInfo { - public: - template <typename P, typename Q> - explicit WrapperInfo(std::unique_ptr<PrimitiveWrapper<P, Q>> wrapper) - : is_same_primitive_wrapping_(std::is_same<P, Q>::value), - wrapper_type_index_(std::type_index(typeid(*wrapper))), - q_type_index_(std::type_index(typeid(Q))) { - auto keyset_wrapper_unique_ptr = - absl::make_unique<KeysetWrapperImpl<P, Q>>( - wrapper.get(), [](const google::crypto::tink::KeyData& key_data) { - return RegistryImpl::GlobalInstance().GetPrimitive<P>(key_data); - }); - keyset_wrapper_ = std::move(keyset_wrapper_unique_ptr); - original_wrapper_ = std::move(wrapper); - } - - template <typename Q> - crypto::tink::util::StatusOr<const KeysetWrapper<Q>*> GetKeysetWrapper() - const { - if (q_type_index_ != std::type_index(typeid(Q))) { - return crypto::tink::util::Status( - absl::StatusCode::kInternal, - "RegistryImpl::KeysetWrapper() called with wrong type"); - } - return static_cast<KeysetWrapper<Q>*>(keyset_wrapper_.get()); - } - - template <typename P> - crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*> - GetLegacyWrapper() const { - if (!is_same_primitive_wrapping_) { - // This happens if a user uses a legacy method (like Registry::Wrap) - // directly or has a custom key manager for a primitive which has a - // PrimitiveWrapper<P,Q> with P != Q. - return crypto::tink::util::Status( - absl::StatusCode::kFailedPrecondition, - absl::StrCat("Cannot use primitive type ", typeid(P).name(), - " with a custom key manager.")); - } - if (q_type_index_ != std::type_index(typeid(P))) { - return crypto::tink::util::Status( - absl::StatusCode::kInternal, - "RegistryImpl::LegacyWrapper() called with wrong type"); - } - return static_cast<const PrimitiveWrapper<P, P>*>( - original_wrapper_.get()); - } - - // Returns true if the PrimitiveWrapper is the same class as the one used - // to construct this WrapperInfo - template <typename P, typename Q> - bool HasSameType(const PrimitiveWrapper<P, Q>& wrapper) { - return wrapper_type_index_ == std::type_index(typeid(wrapper)); - } - - private: - bool is_same_primitive_wrapping_; - // dynamic std::type_index of the actual PrimitiveWrapper<P,Q> class for - // which this key was inserted. - std::type_index wrapper_type_index_; - // dynamic std::type_index of Q, when PrimitiveWrapper<P,Q> was inserted. - std::type_index q_type_index_; - // The primitive_wrapper passed in. We use a shared_ptr because - // unique_ptr<void> is invalid. - std::shared_ptr<void> original_wrapper_; - // The keyset_wrapper_. We use a shared_ptr because unique_ptr<void> is - // invalid. - std::shared_ptr<void> keyset_wrapper_; - }; - - // All information for a given primitive label. - struct LabelInfo { - LabelInfo(std::shared_ptr<void> catalogue, std::type_index type_index, - const char* type_id_name) - : catalogue(std::move(catalogue)), - type_index(type_index), - type_id_name(type_id_name) {} - // A pointer to the underlying Catalogue<P>. We use a shared_ptr because - // shared_ptr<void> is valid (as opposed to unique_ptr<void>). - const std::shared_ptr<void> catalogue; - // std::type_index of the primitive for which this key was inserted. - std::type_index type_index; - // TypeId name of the primitive for which this key was inserted. - const std::string type_id_name; - }; - - template <class P> - crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*> GetLegacyWrapper() - const ABSL_LOCKS_EXCLUDED(maps_mutex_); - - template <class P> - crypto::tink::util::StatusOr<const KeysetWrapper<P>*> GetKeysetWrapper() const - ABSL_LOCKS_EXCLUDED(maps_mutex_); - // Returns the key type info for a given type URL. Since we never replace // key type infos, the pointers will stay valid for the lifetime of the // binary. - crypto::tink::util::StatusOr<const KeyTypeInfo*> get_key_type_info( + crypto::tink::util::StatusOr<const KeyTypeInfoStore::Info*> get_key_type_info( absl::string_view type_url) const ABSL_LOCKS_EXCLUDED(maps_mutex_); - // Returns OK if the key manager with the given type index can be inserted - // for type url type_url and parameter new_key_allowed. Otherwise returns - // an error to be returned to the user. - crypto::tink::util::Status CheckInsertable( - absl::string_view type_url, const std::type_index& key_manager_type_index, - bool new_key_allowed) const ABSL_SHARED_LOCKS_REQUIRED(maps_mutex_); - mutable absl::Mutex maps_mutex_; - // A map from the type_url to the given KeyTypeInfo. Once emplaced KeyTypeInfo - // objects must remain valid throughout the life time of the binary. Hence, - // one should /never/ replace any element of the KeyTypeInfo. This is because - // get_key_type_manager() needs to guarantee that the returned - // key_type_manager remains valid. - // NOTE: We require pointer stability of the value, as get_key_type_info - // returns a pointer which needs to stay alive. - absl::flat_hash_map<std::string, KeyTypeInfo> type_url_to_info_ - ABSL_GUARDED_BY(maps_mutex_); - // A map from the type_id to the corresponding wrapper. - absl::flat_hash_map<std::type_index, WrapperInfo> primitive_to_wrapper_ - ABSL_GUARDED_BY(maps_mutex_); - - absl::flat_hash_map<std::string, LabelInfo> name_to_catalogue_map_ - ABSL_GUARDED_BY(maps_mutex_); + // Stores information about key types constructed from their KeyTypeManager or + // KeyManager. + // Once inserted, KeyTypeInfoStore::Info objects must remain valid for the + // lifetime of the binary, and the Info object's pointer stability is + // required. Elements in Info, which include the KeyTypeManager or KeyManager, + // must not be replaced. + KeyTypeInfoStore key_type_info_store_ ABSL_GUARDED_BY(maps_mutex_); + // Stores information about keyset wrappers constructed from their + // PrimitiveWrapper. + KeysetWrapperStore keyset_wrapper_store_ ABSL_GUARDED_BY(maps_mutex_); mutable absl::Mutex monitoring_factory_mutex_; std::unique_ptr<crypto::tink::MonitoringClientFactory> monitoring_factory_ @@ -446,248 +167,57 @@ }; template <class P> -crypto::tink::util::Status RegistryImpl::AddCatalogue( - absl::string_view catalogue_name, Catalogue<P>* catalogue) { - if (catalogue == nullptr) { - return crypto::tink::util::Status( - absl::StatusCode::kInvalidArgument, - "Parameter 'catalogue' must be non-null."); - } - std::shared_ptr<void> entry(catalogue); - absl::MutexLock lock(&maps_mutex_); - auto curr_catalogue = name_to_catalogue_map_.find(catalogue_name); - if (curr_catalogue != name_to_catalogue_map_.end()) { - auto existing = - static_cast<Catalogue<P>*>(curr_catalogue->second.catalogue.get()); - if (std::type_index(typeid(*existing)) != - std::type_index(typeid(*catalogue))) { - return ToStatusF(absl::StatusCode::kAlreadyExists, - "A catalogue named '%s' has been already added.", - catalogue_name); - } - } else { - name_to_catalogue_map_.emplace( - std::piecewise_construct, std::forward_as_tuple(catalogue_name), - std::forward_as_tuple(std::move(entry), std::type_index(typeid(P)), - typeid(P).name())); - } - return crypto::tink::util::OkStatus(); -} - -template <class P> -crypto::tink::util::StatusOr<const Catalogue<P>*> RegistryImpl::get_catalogue( - absl::string_view catalogue_name) const { - absl::MutexLock lock(&maps_mutex_); - auto catalogue_entry = name_to_catalogue_map_.find(catalogue_name); - if (catalogue_entry == name_to_catalogue_map_.end()) { - return ToStatusF(absl::StatusCode::kNotFound, - "No catalogue named '%s' has been added.", catalogue_name); - } - if (catalogue_entry->second.type_id_name != typeid(P).name()) { - return ToStatusF(absl::StatusCode::kInvalidArgument, - "Wrong Primitive type for catalogue named '%s': " - "got '%s', expected '%s'", - catalogue_name, typeid(P).name(), - catalogue_entry->second.type_id_name); - } - return static_cast<Catalogue<P>*>(catalogue_entry->second.catalogue.get()); -} - -template <class P> crypto::tink::util::Status RegistryImpl::RegisterKeyManager( KeyManager<P>* manager, bool new_key_allowed) { auto owned_manager = absl::WrapUnique(manager); - if (owned_manager == nullptr) { + if (manager == nullptr) { return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, "Parameter 'manager' must be non-null."); } - std::string type_url = owned_manager->get_key_type(); - if (!manager->DoesSupport(type_url)) { - return ToStatusF(absl::StatusCode::kInvalidArgument, - "The manager does not support type '%s'.", type_url); - } absl::MutexLock lock(&maps_mutex_); - crypto::tink::util::Status status = CheckInsertable( - type_url, std::type_index(typeid(*owned_manager)), new_key_allowed); - if (!status.ok()) return status; - - auto it = type_url_to_info_.find(type_url); - if (it != type_url_to_info_.end()) { - it->second.set_new_key_allowed(new_key_allowed); - } else { - type_url_to_info_.emplace( - std::piecewise_construct, std::forward_as_tuple(type_url), - std::forward_as_tuple(owned_manager.release(), new_key_allowed)); - } - return crypto::tink::util::OkStatus(); + return key_type_info_store_.AddKeyManager(std::move(owned_manager), + new_key_allowed); } template <class KeyProto, class KeyFormatProto, class PrimitiveList> crypto::tink::util::Status RegistryImpl::RegisterKeyTypeManager( std::unique_ptr<KeyTypeManager<KeyProto, KeyFormatProto, PrimitiveList>> - owned_manager, + manager, bool new_key_allowed) { - if (owned_manager == nullptr) { + if (manager == nullptr) { return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, "Parameter 'manager' must be non-null."); } - std::string type_url = owned_manager->get_key_type(); absl::MutexLock lock(&maps_mutex_); - - // Check FIPS status - internal::FipsCompatibility fips_compatible = owned_manager->FipsStatus(); - auto fips_status = internal::ChecksFipsCompatibility(fips_compatible); - if (!fips_status.ok()) { - return crypto::tink::util::Status( - absl::StatusCode::kInternal, - absl::StrCat("Failed registering the key manager for ", - typeid(*owned_manager).name(), - " as it is not FIPS compatible.")); - } - - crypto::tink::util::Status status = CheckInsertable( - type_url, std::type_index(typeid(*owned_manager)), new_key_allowed); - if (!status.ok()) return status; - - auto it = type_url_to_info_.find(type_url); - if (it != type_url_to_info_.end()) { - it->second.set_new_key_allowed(new_key_allowed); - } else { - type_url_to_info_.emplace( - std::piecewise_construct, std::forward_as_tuple(type_url), - std::forward_as_tuple(owned_manager.release(), new_key_allowed)); - } - return crypto::tink::util::OkStatus(); + return key_type_info_store_.AddKeyTypeManager(std::move(manager), + new_key_allowed); } template <class PrivateKeyProto, class KeyFormatProto, class PublicKeyProto, class PrivatePrimitivesList, class PublicPrimitivesList> crypto::tink::util::Status RegistryImpl::RegisterAsymmetricKeyManagers( PrivateKeyTypeManager<PrivateKeyProto, KeyFormatProto, PublicKeyProto, - PrivatePrimitivesList>* private_key_manager, - KeyTypeManager<PublicKeyProto, void, PublicPrimitivesList>* - public_key_manager, + PrivatePrimitivesList>* private_manager, + KeyTypeManager<PublicKeyProto, void, PublicPrimitivesList>* public_manager, bool new_key_allowed) ABSL_LOCKS_EXCLUDED(maps_mutex_) { - auto owned_private_key_manager = absl::WrapUnique(private_key_manager); - auto owned_public_key_manager = absl::WrapUnique(public_key_manager); - if (private_key_manager == nullptr) { + auto owned_private_manager = absl::WrapUnique(private_manager); + auto owned_public_manager = absl::WrapUnique(public_manager); + + if (private_manager == nullptr) { return crypto::tink::util::Status( absl::StatusCode::kInvalidArgument, - "Parameter 'private_key_manager' must be non-null."); + "Parameter 'private_manager' must be non-null."); } - if (owned_public_key_manager == nullptr) { + if (public_manager == nullptr) { return crypto::tink::util::Status( absl::StatusCode::kInvalidArgument, - "Parameter 'public_key_manager' must be non-null."); + "Parameter 'public_manager' must be non-null."); } - std::string private_type_url = private_key_manager->get_key_type(); - std::string public_type_url = public_key_manager->get_key_type(); absl::MutexLock lock(&maps_mutex_); - - // Check FIPS status - auto private_fips_status = - internal::ChecksFipsCompatibility(private_key_manager->FipsStatus()); - - if (!private_fips_status.ok()) { - return crypto::tink::util::Status( - absl::StatusCode::kInternal, - absl::StrCat("Failed registering the key manager for ", - typeid(*private_key_manager).name(), - " as it is not FIPS compatible.")); - } - - auto public_fips_status = - internal::ChecksFipsCompatibility(public_key_manager->FipsStatus()); - - if (!public_fips_status.ok()) { - return crypto::tink::util::Status( - absl::StatusCode::kInternal, - absl::StrCat("Failed registering the key manager for ", - typeid(*public_key_manager).name(), - " as it is not FIPS compatible.")); - } - - crypto::tink::util::Status status = CheckInsertable( - private_type_url, std::type_index(typeid(*private_key_manager)), + return key_type_info_store_.AddAsymmetricKeyTypeManagers( + std::move(owned_private_manager), std::move(owned_public_manager), new_key_allowed); - if (!status.ok()) return status; - status = CheckInsertable(public_type_url, - std::type_index(typeid(*public_key_manager)), - new_key_allowed); - if (!status.ok()) return status; - - if (private_type_url == public_type_url) { - return crypto::tink::util::Status( - absl::StatusCode::kInvalidArgument, - "Passed in key managers must have different get_key_type() results."); - } - - auto private_it = type_url_to_info_.find(private_type_url); - auto public_it = type_url_to_info_.find(public_type_url); - bool private_found = private_it != type_url_to_info_.end(); - bool public_found = public_it != type_url_to_info_.end(); - - if (private_found && !public_found) { - return crypto::tink::util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat( - "Private key manager corresponding to ", - typeid(*private_key_manager).name(), - " was previously registered, but key manager corresponding to ", - typeid(*public_key_manager).name(), - " was not, so it's impossible to register them jointly")); - } - if (!private_found && public_found) { - return crypto::tink::util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("Key manager corresponding to ", - typeid(*public_key_manager).name(), - " was previously registered, but private key manager " - "corresponding to ", - typeid(*private_key_manager).name(), - " was not, so it's impossible to register them jointly")); - } - - if (private_found) { - // implies public_found. - if (!private_it->second.public_key_manager_type_index().has_value()) { - return crypto::tink::util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("private key manager corresponding to ", - typeid(*private_key_manager).name(), - " is already registered without public key manager, " - "cannot be re-registered with public key manager. ")); - } - if (*private_it->second.public_key_manager_type_index() != - std::type_index(typeid(*public_key_manager))) { - return crypto::tink::util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat( - "private key manager corresponding to ", - typeid(*private_key_manager).name(), - " is already registered with ", - private_it->second.public_key_manager_type_index()->name(), - ", cannot be re-registered with ", - typeid(*public_key_manager).name())); - } - } - - if (!private_found) { - // !public_found must hold. - type_url_to_info_.emplace( - std::piecewise_construct, std::forward_as_tuple(private_type_url), - std::forward_as_tuple(owned_private_key_manager.release(), - owned_public_key_manager.get(), new_key_allowed)); - type_url_to_info_.emplace( - std::piecewise_construct, std::forward_as_tuple(public_type_url), - std::forward_as_tuple(owned_public_key_manager.release(), - new_key_allowed)); - } else { - private_it->second.set_new_key_allowed(new_key_allowed); - } - - return util::OkStatus(); } template <class P, class Q> @@ -697,35 +227,27 @@ return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, "Parameter 'wrapper' must be non-null."); } - std::unique_ptr<PrimitiveWrapper<P, Q>> entry(wrapper); + std::unique_ptr<PrimitiveWrapper<P, Q>> owned_wrapper(wrapper); absl::MutexLock lock(&maps_mutex_); - auto it = primitive_to_wrapper_.find(std::type_index(typeid(Q))); - if (it != primitive_to_wrapper_.end()) { - if (!it->second.HasSameType(*wrapper)) { - return util::Status( - absl::StatusCode::kAlreadyExists, - "A wrapper named for this primitive has already been added."); - } - return crypto::tink::util::OkStatus(); - } - primitive_to_wrapper_.emplace( - std::piecewise_construct, - std::forward_as_tuple(std::type_index(typeid(Q))), - std::forward_as_tuple(std::move(entry))); - return crypto::tink::util::OkStatus(); + std::function<crypto::tink::util::StatusOr<std::unique_ptr<P>>( + const google::crypto::tink::KeyData& key_data)> + primitive_getter = [this](const google::crypto::tink::KeyData& key_data) { + return this->GetPrimitive<P>(key_data); + }; + return keyset_wrapper_store_.Add(std::move(owned_wrapper), primitive_getter); } template <class P> crypto::tink::util::StatusOr<const KeyManager<P>*> RegistryImpl::get_key_manager(absl::string_view type_url) const { - absl::MutexLock lock(&maps_mutex_); - auto it = type_url_to_info_.find(type_url); - if (it == type_url_to_info_.end()) { - return ToStatusF(absl::StatusCode::kNotFound, - "No manager for type '%s' has been registered.", type_url); + crypto::tink::util::StatusOr< + const crypto::tink::internal::KeyTypeInfoStore::Info*> + info = get_key_type_info(type_url); + if (!info.ok()) { + return info.status(); } - return it->second.get_key_manager<P>(type_url); + return (*info)->get_key_manager<P>(type_url); } template <class P> @@ -739,42 +261,6 @@ } template <class P> -crypto::tink::util::StatusOr<std::unique_ptr<P>> RegistryImpl::GetPrimitive( - absl::string_view type_url, const portable_proto::MessageLite& key) const { - auto key_manager_result = get_key_manager<P>(type_url); - if (key_manager_result.ok()) { - return key_manager_result.value()->GetPrimitive(key); - } - return key_manager_result.status(); -} - -template <class P> -crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*> -RegistryImpl::GetLegacyWrapper() const { - absl::MutexLock lock(&maps_mutex_); - auto it = primitive_to_wrapper_.find(std::type_index(typeid(P))); - if (it == primitive_to_wrapper_.end()) { - return util::Status( - absl::StatusCode::kNotFound, - absl::StrCat("No wrapper registered for type ", typeid(P).name())); - } - return it->second.GetLegacyWrapper<P>(); -} - -template <class P> -crypto::tink::util::StatusOr<const KeysetWrapper<P>*> -RegistryImpl::GetKeysetWrapper() const { - absl::MutexLock lock(&maps_mutex_); - auto it = primitive_to_wrapper_.find(std::type_index(typeid(P))); - if (it == primitive_to_wrapper_.end()) { - return util::Status( - absl::StatusCode::kNotFound, - absl::StrCat("No wrapper registered for type ", typeid(P).name())); - } - return it->second.GetKeysetWrapper<P>(); -} - -template <class P> crypto::tink::util::StatusOr<std::unique_ptr<P>> RegistryImpl::Wrap( std::unique_ptr<PrimitiveSet<P>> primitive_set) const { if (primitive_set == nullptr) { @@ -782,29 +268,45 @@ absl::StatusCode::kInvalidArgument, "Parameter 'primitive_set' must be non-null."); } - util::StatusOr<const PrimitiveWrapper<P, P>*> wrapper_result = - GetLegacyWrapper<P>(); - if (!wrapper_result.ok()) { - return wrapper_result.status(); + const PrimitiveWrapper<P, P>* wrapper = nullptr; + { + absl::MutexLock lock(&maps_mutex_); + crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*> wrapper_status = + keyset_wrapper_store_.GetPrimitiveWrapper<P>(); + if (!wrapper_status.ok()) { + return wrapper_status.status(); + } + wrapper = *wrapper_status; } - return wrapper_result.value()->Wrap(std::move(primitive_set)); + return wrapper->Wrap(std::move(primitive_set)); } template <class P> crypto::tink::util::StatusOr<std::unique_ptr<P>> RegistryImpl::WrapKeyset( const google::crypto::tink::Keyset& keyset, const absl::flat_hash_map<std::string, std::string>& annotations) const { - crypto::tink::util::StatusOr<const KeysetWrapper<P>*> keyset_wrapper = - GetKeysetWrapper<P>(); - if (!keyset_wrapper.ok()) { - return keyset_wrapper.status(); + const KeysetWrapper<P>* keyset_wrapper = nullptr; + { + absl::MutexLock lock(&maps_mutex_); + crypto::tink::util::StatusOr<const KeysetWrapper<P>*> + keyset_wrapper_status = keyset_wrapper_store_.Get<P>(); + if (!keyset_wrapper_status.ok()) { + return keyset_wrapper_status.status(); + } + keyset_wrapper = *keyset_wrapper_status; } - return (*keyset_wrapper)->Wrap(keyset, annotations); + // `maps_mutex_` must be released before calling Wrap or this will deadlock, + // as Wrap calls get_key_manager. + return keyset_wrapper->Wrap(keyset, annotations); } inline crypto::tink::util::Status RegistryImpl::RestrictToFipsIfEmpty() const { absl::MutexLock lock(&maps_mutex_); - if (type_url_to_info_.empty()) { + // If we are already in FIPS mode, then do nothing.. + if (IsFipsModeEnabled()) { + return util::OkStatus(); + } + if (key_type_info_store_.IsEmpty()) { SetFipsRestricted(); return util::OkStatus(); }
diff --git a/cc/internal/registry_impl_test.cc b/cc/internal/registry_impl_test.cc index d6aaf21..7b3ec51 100644 --- a/cc/internal/registry_impl_test.cc +++ b/cc/internal/registry_impl_test.cc
@@ -14,46 +14,58 @@ // //////////////////////////////////////////////////////////////////////////////// +#include "tink/internal/registry_impl.h" + +#include <stdint.h> + #include <memory> +#include <sstream> #include <string> #include <thread> // NOLINT(build/c++11) +#include <typeinfo> #include <utility> -#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/memory/memory.h" #include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "openssl/crypto.h" #include "tink/aead.h" #include "tink/aead/aead_wrapper.h" #include "tink/aead/aes_gcm_key_manager.h" -#include "tink/catalogue.h" -#include "tink/config/tink_fips.h" #include "tink/core/key_manager_impl.h" #include "tink/core/key_type_manager.h" -#include "tink/crypto_format.h" +#include "tink/core/private_key_manager_impl.h" +#include "tink/core/private_key_type_manager.h" +#include "tink/core/template_util.h" #include "tink/hybrid/ecies_aead_hkdf_private_key_manager.h" #include "tink/hybrid/ecies_aead_hkdf_public_key_manager.h" -#include "tink/keyset_manager.h" -#include "tink/monitoring/monitoring.h" +#include "tink/hybrid_decrypt.h" +#include "tink/input_stream.h" +#include "tink/internal/fips_utils.h" +#include "tink/key_manager.h" +#include "tink/mac.h" #include "tink/monitoring/monitoring_client_mocks.h" +#include "tink/primitive_set.h" +#include "tink/primitive_wrapper.h" #include "tink/registry.h" #include "tink/subtle/aes_gcm_boringssl.h" #include "tink/subtle/random.h" +#include "tink/util/input_stream_util.h" #include "tink/util/istream_input_stream.h" #include "tink/util/protobuf_helper.h" #include "tink/util/secret_data.h" #include "tink/util/status.h" #include "tink/util/statusor.h" -#include "tink/util/test_keyset_handle.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" #include "proto/aes_ctr_hmac_aead.pb.h" #include "proto/aes_gcm.pb.h" #include "proto/common.pb.h" #include "proto/ecdsa.pb.h" +#include "proto/ecies_aead_hkdf.pb.h" #include "proto/tink.pb.h" namespace crypto { @@ -94,9 +106,7 @@ class RegistryTest : public ::testing::Test { protected: - void SetUp() override { - Registry::Reset(); - } + void SetUp() override { Registry::Reset(); } void TearDown() override { // Reset is needed here to ensure Mock objects get deleted and do not leak. @@ -137,28 +147,23 @@ explicit TestAeadKeyManager(const std::string& key_type) : key_type_(key_type), key_factory_(key_type) {} - util::StatusOr<std::unique_ptr<Aead>> - GetPrimitive(const KeyData& key) const override { + util::StatusOr<std::unique_ptr<Aead>> GetPrimitive( + const KeyData& key) const override { std::unique_ptr<Aead> aead(new DummyAead(key_type_)); return std::move(aead); } - util::StatusOr<std::unique_ptr<Aead>> - GetPrimitive(const MessageLite& key) const override { + util::StatusOr<std::unique_ptr<Aead>> GetPrimitive( + const MessageLite& key) const override { return util::Status(absl::StatusCode::kUnknown, "TestKeyFactory cannot construct an aead"); } - - uint32_t get_version() const override { - return 0; - } + uint32_t get_version() const override { return 0; } const std::string& get_key_type() const override { return key_type_; } - const KeyFactory& get_key_factory() const override { - return key_factory_; - } + const KeyFactory& get_key_factory() const override { return key_factory_; } private: std::string key_type_; @@ -254,7 +259,7 @@ template <typename P, typename Q = P> class TestWrapper : public PrimitiveWrapper<P, Q> { public: - TestWrapper() {} + TestWrapper() = default; crypto::tink::util::StatusOr<std::unique_ptr<Q>> Wrap( std::unique_ptr<PrimitiveSet<P>> primitive_set) const override { return util::Status(absl::StatusCode::kUnimplemented, @@ -286,7 +291,8 @@ for (int i = 0; i < manager_count; i++) { std::string key_type = key_type_prefix + std::to_string(i); util::Status status = Registry::RegisterKeyManager( - new TestAeadKeyManager(key_type)); + absl::make_unique<TestAeadKeyManager>(key_type), + /* new_key_allowed= */ true); EXPECT_TRUE(status.ok()) << status; } } @@ -375,10 +381,8 @@ int count_b = 72; // Register some managers. - std::thread register_a(register_test_managers, - key_type_prefix_a, count_a); - std::thread register_b(register_test_managers, - key_type_prefix_b, count_b); + std::thread register_a(register_test_managers, key_type_prefix_a, count_a); + std::thread register_b(register_test_managers, key_type_prefix_b, count_b); register_a.join(); register_b.join(); @@ -417,7 +421,6 @@ auto status = Registry::RegisterKeyManager( absl::make_unique<TestAeadKeyManager>(key_type_1), true); - EXPECT_TRUE(status.ok()) << status; status = Registry::RegisterKeyManager( @@ -475,7 +478,8 @@ TEST_F(RegistryTest, GetKeyManagerRemainsValid) { std::string key_type = AesGcmKeyManager().get_key_type(); EXPECT_THAT(Registry::RegisterKeyManager( - absl::make_unique<TestAeadKeyManager>(key_type), true), IsOk()); + absl::make_unique<TestAeadKeyManager>(key_type), true), + IsOk()); crypto::tink::util::StatusOr<const KeyManager<Aead>*> key_manager = Registry::get_key_manager<Aead>(key_type); @@ -486,49 +490,6 @@ EXPECT_THAT(key_manager.value()->get_key_type(), Eq(key_type)); } -class TestAeadCatalogue : public Catalogue<Aead> { - public: - TestAeadCatalogue() {} - - util::StatusOr<std::unique_ptr<KeyManager<Aead>>> GetKeyManager( - const std::string& type_url, const std::string& primitive_name, - uint32_t min_version) const override { - return util::Status(absl::StatusCode::kUnimplemented, - "This is a test catalogue."); - } -}; - -class TestAeadCatalogue2 : public TestAeadCatalogue {}; - -TEST_F(RegistryTest, testAddCatalogue) { - std::string catalogue_name = "SomeCatalogue"; - - std::unique_ptr<TestAeadCatalogue> null_catalogue = nullptr; - auto status = - Registry::AddCatalogue(catalogue_name, std::move(null_catalogue)); - EXPECT_FALSE(status.ok()); - EXPECT_EQ(absl::StatusCode::kInvalidArgument, status.code()) << status; - - // Add a catalogue. - status = Registry::AddCatalogue(catalogue_name, - absl::make_unique<TestAeadCatalogue>()); - EXPECT_TRUE(status.ok()) << status; - - // Add the same catalogue again, it should work (idempotence). - status = Registry::AddCatalogue(catalogue_name, - absl::make_unique<TestAeadCatalogue>()); - EXPECT_TRUE(status.ok()) << status; - - // Try overriding a catalogue. - status = Registry::AddCatalogue(catalogue_name, - absl::make_unique<TestAeadCatalogue2>()); - EXPECT_FALSE(status.ok()); - EXPECT_EQ(absl::StatusCode::kAlreadyExists, status.code()) << status; - - // Check the catalogue is still present. - EXPECT_THAT(Registry::get_catalogue<Aead>(catalogue_name), IsOk()); -} - TEST_F(RegistryTest, testGettingPrimitives) { std::string key_type_1 = "google.crypto.tink.AesCtrHmacAeadKey"; std::string key_type_2 = "google.crypto.tink.AesGcmKey"; @@ -653,8 +614,7 @@ key_template.set_value("some totally other value 42"); auto new_key_data_result = Registry::NewKeyData(key_template); EXPECT_FALSE(new_key_data_result.ok()); - EXPECT_EQ(absl::StatusCode::kNotFound, - new_key_data_result.status().code()); + EXPECT_EQ(absl::StatusCode::kNotFound, new_key_data_result.status().code()); EXPECT_PRED_FORMAT2(testing::IsSubstring, bad_type_url, std::string(new_key_data_result.status().message())); } @@ -892,7 +852,7 @@ // Tests that wrapping of a keyset works in the usual case. TEST_F(RegistryTest, KeysetWrappingTest) { - if (!FIPS_mode()) { + if (!IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported when BoringSSL is not built in FIPS-mode."; } @@ -906,9 +866,9 @@ ON_CALL(*fips_key_manager, FipsStatus()) .WillByDefault(testing::Return(FipsCompatibility::kRequiresBoringCrypto)); - ASSERT_THAT(Registry::RegisterKeyTypeManager( - std::move(fips_key_manager), true), - IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(std::move(fips_key_manager), true), + IsOk()); ASSERT_THAT(Registry::RegisterPrimitiveWrapper( absl::make_unique<AeadVariantWrapper>()), IsOk()); @@ -1010,7 +970,7 @@ } TEST_F(RegistryTest, RegisterFipsKeyTypeManager) { - if (!kUseOnlyFips || !FIPS_mode()) { + if (!kUseOnlyFips || !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-mode with BoringCrypto available."; } @@ -1025,7 +985,7 @@ } TEST_F(RegistryTest, RegisterFipsKeyTypeManagerNoBoringCrypto) { - if (!kUseOnlyFips || FIPS_mode()) { + if (!kUseOnlyFips || IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-mode with BoringCrypto not available."; } @@ -1228,8 +1188,9 @@ TEST_F(RegistryTest, KeyManagerDeriveKeyFail) { std::string key_type = "type.googleapis.com/google.crypto.tink.AesGcmKey"; ASSERT_THAT(Registry::RegisterKeyManager( - absl::make_unique<TestAeadKeyManager>(key_type), - /* new_key_allowed= */ true), IsOk()); + absl::make_unique<TestAeadKeyManager>(key_type), + /* new_key_allowed= */ true), + IsOk()); KeyTemplate key_template; key_template.set_type_url("type.googleapis.com/google.crypto.tink.AesGcmKey"); @@ -1323,9 +1284,15 @@ StatusIs(absl::StatusCode::kAlreadyExists)); } +} // namespace + +// NOTE: These are outside of the anonymous namespace to allow compiling with +// MSVC. class PrivatePrimitiveA {}; class PrivatePrimitiveB {}; +namespace { + class TestPrivateKeyTypeManager : public PrivateKeyTypeManager<EcdsaPrivateKey, EcdsaKeyFormat, EcdsaPublicKey, @@ -1388,9 +1355,15 @@ "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey"; }; +} // namespace + +// NOTE: These are outside of the anonymous namespace to allow compiling with +// MSVC. class PublicPrimitiveA {}; class PublicPrimitiveB {}; +namespace { + class TestPublicKeyTypeManager : public KeyTypeManager<EcdsaPublicKey, void, List<PublicPrimitiveA, PublicPrimitiveB>> { @@ -1451,7 +1424,7 @@ } TEST_F(RegistryTest, RegisterAsymmetricKeyManagers) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1463,7 +1436,7 @@ } TEST_F(RegistryTest, AsymmetricMoreRestrictiveNewKey) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1480,7 +1453,7 @@ } TEST_F(RegistryTest, AsymmetricSameNewKey) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1505,7 +1478,7 @@ } TEST_F(RegistryTest, AsymmetricLessRestrictiveGivesError) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1526,7 +1499,7 @@ // remains valid. TEST_F(RegistryTest, RegisterAsymmetricKeyManagersGetKeyManagerStaysValid) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1554,9 +1527,8 @@ Eq(TestPublicKeyTypeManager().get_key_type())); } - TEST_F(RegistryTest, AsymmetricPrivateRegisterAlone) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1583,7 +1555,7 @@ } TEST_F(RegistryTest, AsymmetricGetPrimitiveA) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1601,7 +1573,7 @@ } TEST_F(RegistryTest, AsymmetricGetPrimitiveB) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1619,7 +1591,7 @@ } TEST_F(RegistryTest, AsymmetricGetPublicPrimitiveA) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1637,7 +1609,7 @@ } TEST_F(RegistryTest, AsymmetricGetPublicPrimitiveB) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1655,7 +1627,7 @@ } TEST_F(RegistryTest, AsymmetricGetWrongPrimitiveError) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1673,9 +1645,7 @@ } class PrivateKeyManagerImplTest : public testing::Test { - void SetUp() override { - Registry::Reset(); - } + void SetUp() override { Registry::Reset(); } void TearDown() override { // Reset is needed here to ensure Mock objects get deleted and do not leak. @@ -1684,7 +1654,7 @@ }; TEST_F(PrivateKeyManagerImplTest, AsymmetricFactoryNewKeyFromMessage) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1711,7 +1681,7 @@ } TEST_F(PrivateKeyManagerImplTest, AsymmetricNewKeyDisallowed) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1733,7 +1703,7 @@ } TEST_F(RegistryTest, AsymmetricGetPublicKeyData) { - if (kUseOnlyFips && !FIPS_mode()) { + if (kUseOnlyFips && !IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -1923,8 +1893,8 @@ std::move(delegating_key_manager), true); EXPECT_THAT(status, IsOk()); status = registry_impl.RegisterKeyTypeManager<AesGcmKey, AesGcmKeyFormat, - List<Aead, AeadVariant>>( - absl::make_unique<ExampleKeyTypeManager>(), true); + List<Aead, AeadVariant>>( + absl::make_unique<ExampleKeyTypeManager>(), true); EXPECT_THAT(status, IsOk()); EcdsaKeyFormat format; @@ -1952,8 +1922,8 @@ std::move(delegating_key_manager), true); EXPECT_THAT(status, IsOk()); status = registry_impl.RegisterKeyTypeManager<AesGcmKey, AesGcmKeyFormat, - List<Aead, AeadVariant>>( - absl::make_unique<ExampleKeyTypeManager>(), true); + List<Aead, AeadVariant>>( + absl::make_unique<ExampleKeyTypeManager>(), true); EXPECT_THAT(status, IsOk()); EcdsaKeyFormat format; @@ -1979,8 +1949,8 @@ absl::make_unique<TestPublicKeyTypeManager>().release(), true); EXPECT_THAT(status, IsOk()); status = registry_impl.RegisterKeyTypeManager<AesGcmKey, AesGcmKeyFormat, - List<Aead, AeadVariant>>( - absl::make_unique<ExampleKeyTypeManager>(), true); + List<Aead, AeadVariant>>( + absl::make_unique<ExampleKeyTypeManager>(), true); EXPECT_THAT(status, IsOk()); EcdsaPrivateKey private_key; @@ -1995,14 +1965,29 @@ HasSubstr("GetPublicKey worked"))); } -TEST_F(RegistryImplTest, FipsSucceedsOnEmptyRegistry) { +TEST_F(RegistryImplTest, FipsRestrictionSucceedsOnEmptyRegistry) { + RegistryImpl registry_impl; + EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk()); +} + +TEST_F(RegistryImplTest, FipsRestrictionSucceedsWhenSettingMultipleTimes) { + RegistryImpl registry_impl; + EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk()); + EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk()); + EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk()); +} + +TEST_F(RegistryImplTest, FipsRestrictionSucceedsIfBuildInFipsMode) { + if (!kUseOnlyFips) { + GTEST_SKIP() << "Not supported when Tink is not built in FIPS mode."; + } RegistryImpl registry_impl; EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), IsOk()); } TEST_F(RegistryImplTest, FipsFailsIfNotEmpty) { - if (!FIPS_mode()) { - GTEST_SKIP() << "Not supported when BoringSSL is not built in FIPS-mode."; + if (kUseOnlyFips) { + GTEST_SKIP() << "Not supported in FIPS-only mode"; } auto fips_key_manager = absl::make_unique<ExampleKeyTypeManager>(); @@ -2011,8 +1996,8 @@ RegistryImpl registry_impl; auto status = registry_impl.RegisterKeyTypeManager<AesGcmKey, AesGcmKeyFormat, - List<Aead, AeadVariant>>( - std::move(fips_key_manager), true); + List<Aead, AeadVariant>>( + std::move(fips_key_manager), true); EXPECT_THAT(status, IsOk()); EXPECT_THAT(registry_impl.RestrictToFipsIfEmpty(), StatusIs(absl::StatusCode::kInternal));
diff --git a/cc/internal/rsa_util.cc b/cc/internal/rsa_util.cc index 62398e0..45c8c17 100644 --- a/cc/internal/rsa_util.cc +++ b/cc/internal/rsa_util.cc
@@ -15,18 +15,26 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/rsa_util.h" +#include <stddef.h> + +#include <memory> #include <string> #include <utility> #include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" #include "openssl/bn.h" +#include "openssl/rsa.h" #include "tink/config/tink_fips.h" #include "tink/internal/bn_util.h" #include "tink/internal/err_util.h" +#include "tink/internal/fips_utils.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/internal/ssl_util.h" #include "tink/util/errors.h" +#include "tink/util/secret_data.h" #include "tink/util/status.h" #include "tink/util/statusor.h"
diff --git a/cc/internal/rsa_util.h b/cc/internal/rsa_util.h index fbf9c30..ae0a7ba 100644 --- a/cc/internal/rsa_util.h +++ b/cc/internal/rsa_util.h
@@ -16,8 +16,11 @@ #ifndef TINK_INTERNAL_RSA_UTIL_H_ #define TINK_INTERNAL_RSA_UTIL_H_ +#include <stddef.h> + #include <string> +#include "absl/strings/string_view.h" #include "openssl/bn.h" #include "openssl/rsa.h" #include "tink/internal/ssl_unique_ptr.h"
diff --git a/cc/internal/rsa_util_test.cc b/cc/internal/rsa_util_test.cc index 6b9b5c8..b86cd92 100644 --- a/cc/internal/rsa_util_test.cc +++ b/cc/internal/rsa_util_test.cc
@@ -15,19 +15,27 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/internal/rsa_util.h" +#include <stddef.h> +#include <stdint.h> + #include <algorithm> +#include <iterator> +#include <memory> #include <string> #include <utility> #include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/status/status.h" #include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" #include "openssl/bn.h" #include "openssl/rsa.h" #include "tink/internal/bn_util.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/subtle/random.h" +#include "tink/util/secret_data.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h"
diff --git a/cc/internal/serialization.h b/cc/internal/serialization.h new file mode 100644 index 0000000..b392969 --- /dev/null +++ b/cc/internal/serialization.h
@@ -0,0 +1,53 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_SERIALIZATION_H_ +#define TINK_INTERNAL_SERIALIZATION_H_ + +#include "absl/strings/string_view.h" + +namespace crypto { +namespace tink { + +// Represents either a serialized `Key` or a serialized `Parameters` object. +// +// Serialization objects are used within Tink to serialize keys, keysets, and +// parameters. For each serialization method (e.g., binary protobuf +// serialization), one subclass of this interface must be defined. +// +// This class should eventually be moved to the Tink Public API, but major +// changes still might be made until then (i.e., don't assume that this API +// is completely stable yet). +class Serialization { + public: + // Identifies which parsing method to use in the registry. + // + // When registering a parsing function in the registry, one argument will be + // this object identifier. When the registry is asked to parse a + // `Serialization`, the registry will then dispatch it to the corresponding + // method. + // + // The returned absl::string_view must remain valid for the lifetime of this + // `Serialization` object. + virtual absl::string_view ObjectIdentifier() const = 0; + + virtual ~Serialization() = default; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_SERIALIZATION_H_
diff --git a/cc/internal/serialization_registry.cc b/cc/internal/serialization_registry.cc new file mode 100644 index 0000000..4d34473 --- /dev/null +++ b/cc/internal/serialization_registry.cc
@@ -0,0 +1,140 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/serialization_registry.h" + +#include <memory> +#include <string> +#include <typeinfo> + +#include "absl/container/flat_hash_map.h" +#include "absl/status/status.h" +#include "absl/strings/str_format.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/parser_index.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serializer_index.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +SerializationRegistry::Builder::Builder(const SerializationRegistry& registry) + : Builder(registry.parameters_parsers_, registry.parameters_serializers_, + registry.key_parsers_, registry.key_serializers_) {} + +util::Status SerializationRegistry::Builder::RegisterParametersParser( + ParametersParser* parser) { + ParserIndex index = parser->Index(); + auto it = parameters_parsers_.find(index); + if (it != parameters_parsers_.end()) { + if (parameters_parsers_[index] != parser) { + return util::Status(absl::StatusCode::kAlreadyExists, + "Attempted to update existing parameters parser."); + } + } + parameters_parsers_.insert({parser->Index(), parser}); + return util::OkStatus(); +} + +util::Status SerializationRegistry::Builder::RegisterParametersSerializer( + ParametersSerializer* serializer) { + SerializerIndex index = serializer->Index(); + auto it = parameters_serializers_.find(index); + if (it != parameters_serializers_.end()) { + if (parameters_serializers_[index] != serializer) { + return util::Status( + absl::StatusCode::kAlreadyExists, + "Attempted to update existing parameters serializer."); + } + } + parameters_serializers_.insert({serializer->Index(), serializer}); + return util::OkStatus(); +} + +util::Status SerializationRegistry::Builder::RegisterKeyParser( + KeyParser* parser) { + ParserIndex index = parser->Index(); + auto it = key_parsers_.find(index); + if (it != key_parsers_.end()) { + if (key_parsers_[index] != parser) { + return util::Status(absl::StatusCode::kAlreadyExists, + "Attempted to update existing key parser."); + } + } + key_parsers_.insert({parser->Index(), parser}); + return util::OkStatus(); +} + +util::Status SerializationRegistry::Builder::RegisterKeySerializer( + KeySerializer* serializer) { + SerializerIndex index = serializer->Index(); + auto it = key_serializers_.find(index); + if (it != key_serializers_.end()) { + if (key_serializers_[index] != serializer) { + return util::Status(absl::StatusCode::kAlreadyExists, + "Attempted to update existing key serializer."); + } + } + key_serializers_.insert({serializer->Index(), serializer}); + return util::OkStatus(); +} + +SerializationRegistry SerializationRegistry::Builder::Build() { + return SerializationRegistry(parameters_parsers_, parameters_serializers_, + key_parsers_, key_serializers_); +} + +util::StatusOr<std::unique_ptr<Parameters>> +SerializationRegistry::ParseParameters( + const Serialization& serialization) const { + ParserIndex index = ParserIndex::Create(serialization); + auto it = parameters_parsers_.find(index); + if (it == parameters_parsers_.end()) { + return util::Status( + absl::StatusCode::kNotFound, + absl::StrFormat("No parameters parser found for parameters type %s", + typeid(serialization).name())); + } + + return parameters_parsers_.at(index)->ParseParameters(serialization); +} + +util::StatusOr<std::unique_ptr<Key>> SerializationRegistry::ParseKey( + const Serialization& serialization, + absl::optional<SecretKeyAccessToken> token) const { + ParserIndex index = ParserIndex::Create(serialization); + auto it = key_parsers_.find(index); + if (it == key_parsers_.end()) { + return util::Status( + absl::StatusCode::kNotFound, + absl::StrFormat("No key parser found for serialization type %s", + typeid(serialization).name())); + } + + return key_parsers_.at(index)->ParseKey(serialization, token); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/serialization_registry.h b/cc/internal/serialization_registry.h new file mode 100644 index 0000000..ac90e75 --- /dev/null +++ b/cc/internal/serialization_registry.h
@@ -0,0 +1,176 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_SERIALIZATION_REGISTRY_H_ +#define TINK_INTERNAL_SERIALIZATION_REGISTRY_H_ + +#include <map> +#include <memory> +#include <string> +#include <typeindex> +#include <typeinfo> + +#include "absl/container/flat_hash_map.h" +#include "absl/status/status.h" +#include "absl/strings/str_format.h" +#include "absl/types/optional.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/parser_index.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serializer_index.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +class SerializationRegistry { + public: + class Builder { + public: + // Neither movable nor copyable. + Builder(const Builder& other) = delete; + Builder& operator=(const Builder& other) = delete; + + // Creates initially empty serialization registry builder. + Builder() = default; + // Creates serialization registry builder by initially copying entries from + // `registry`. + explicit Builder(const SerializationRegistry& registry); + + // Registers parameters `parser`. Returns an error if a different parameters + // parser has already been registered. + util::Status RegisterParametersParser(ParametersParser* parser); + + // Registers parameters `serializer`. Returns an error if a different + // parameters serializer has already been registered. + util::Status RegisterParametersSerializer(ParametersSerializer* serializer); + + // Registers key `parser`. Returns an error if a different key parser has + // already been registered. + util::Status RegisterKeyParser(KeyParser* parser); + + // Registers key `serializer`. Returns an error if a different key + // serializer has already been registered. + util::Status RegisterKeySerializer(KeySerializer* serializer); + + // Creates serialization registry from this builder. + SerializationRegistry Build(); + + private: + Builder(const absl::flat_hash_map<ParserIndex, ParametersParser*>& + parameters_parsers, + const absl::flat_hash_map<SerializerIndex, ParametersSerializer*>& + parameters_serializers, + const absl::flat_hash_map<ParserIndex, KeyParser*> key_parsers, + const absl::flat_hash_map<SerializerIndex, KeySerializer*> + key_serializers) + : parameters_parsers_(parameters_parsers), + parameters_serializers_(parameters_serializers), + key_parsers_(key_parsers), + key_serializers_(key_serializers) {} + + absl::flat_hash_map<ParserIndex, ParametersParser*> parameters_parsers_; + absl::flat_hash_map<SerializerIndex, ParametersSerializer*> + parameters_serializers_; + absl::flat_hash_map<ParserIndex, KeyParser*> key_parsers_; + absl::flat_hash_map<SerializerIndex, KeySerializer*> key_serializers_; + }; + + // Movable and copyable. + SerializationRegistry(SerializationRegistry&& other) = default; + SerializationRegistry& operator=(SerializationRegistry&& other) = default; + SerializationRegistry(const SerializationRegistry& other) = default; + SerializationRegistry& operator=(const SerializationRegistry& other) = + default; + + // Creates empty serialization registry. + SerializationRegistry() = default; + + // Parses `serialization` into a `Parameters` instance. + util::StatusOr<std::unique_ptr<Parameters>> ParseParameters( + const Serialization& serialization) const; + + // Serializes `parameters` into a `Serialization` instance. + template <typename SerializationT> + util::StatusOr<std::unique_ptr<Serialization>> SerializeParameters( + const Parameters& parameters) const { + SerializerIndex index = SerializerIndex::Create<SerializationT>(parameters); + auto it = parameters_serializers_.find(index); + if (it == parameters_serializers_.end()) { + return util::Status( + absl::StatusCode::kNotFound, + absl::StrFormat( + "No parameters serializer found for parameters type %s", + typeid(parameters).name())); + } + + return parameters_serializers_.at(index)->SerializeParameters(parameters); + } + + // Parses `serialization` into a `Key` instance. + util::StatusOr<std::unique_ptr<Key>> ParseKey( + const Serialization& serialization, + absl::optional<SecretKeyAccessToken> token) const; + + // Serializes `parameters` into a `Serialization` instance. + template <typename SerializationT> + util::StatusOr<std::unique_ptr<Serialization>> SerializeKey( + const Key& key, absl::optional<SecretKeyAccessToken> token) const { + SerializerIndex index = SerializerIndex::Create<SerializationT>(key); + auto it = key_serializers_.find(index); + if (it == key_serializers_.end()) { + return util::Status( + absl::StatusCode::kNotFound, + absl::StrFormat("No key serializer found for key type %s", + typeid(key).name())); + } + + return key_serializers_.at(index)->SerializeKey(key, token); + } + + private: + SerializationRegistry( + const absl::flat_hash_map<ParserIndex, ParametersParser*>& + parameters_parsers, + const absl::flat_hash_map<SerializerIndex, ParametersSerializer*>& + parameters_serializers, + const absl::flat_hash_map<ParserIndex, KeyParser*> key_parsers, + const absl::flat_hash_map<SerializerIndex, KeySerializer*> + key_serializers) + : parameters_parsers_(parameters_parsers), + parameters_serializers_(parameters_serializers), + key_parsers_(key_parsers), + key_serializers_(key_serializers) {} + + absl::flat_hash_map<ParserIndex, ParametersParser*> parameters_parsers_; + absl::flat_hash_map<SerializerIndex, ParametersSerializer*> + parameters_serializers_; + absl::flat_hash_map<ParserIndex, KeyParser*> key_parsers_; + absl::flat_hash_map<SerializerIndex, KeySerializer*> key_serializers_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_SERIALIZATION_REGISTRY_H_
diff --git a/cc/internal/serialization_registry_test.cc b/cc/internal/serialization_registry_test.cc new file mode 100644 index 0000000..0e0872b --- /dev/null +++ b/cc/internal/serialization_registry_test.cc
@@ -0,0 +1,415 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/serialization_registry.h" + +#include <memory> +#include <string_view> +#include <typeindex> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/serialization.h" +#include "tink/internal/serialization_test_util.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::testing::Eq; +using ::testing::IsFalse; +using ::testing::IsTrue; + +TEST(SerializationRegistryTest, ParseParameters) { + SerializationRegistry::Builder builder; + ParametersParserImpl<NoIdSerialization, NoIdParams> parser1(kNoIdTypeUrl, + ParseNoIdParams); + ParametersParserImpl<IdParamsSerialization, IdParams> parser2(kIdTypeUrl, + ParseIdParams); + ASSERT_THAT(builder.RegisterParametersParser(&parser1), IsOk()); + ASSERT_THAT(builder.RegisterParametersParser(&parser2), IsOk()); + + SerializationRegistry registry = builder.Build(); + + util::StatusOr<std::unique_ptr<Parameters>> no_id_params = + registry.ParseParameters(NoIdSerialization()); + ASSERT_THAT(no_id_params, IsOk()); + EXPECT_THAT((*no_id_params)->HasIdRequirement(), IsFalse()); + EXPECT_THAT(std::type_index(typeid(**no_id_params)), + std::type_index(typeid(NoIdParams))); + + util::StatusOr<std::unique_ptr<Parameters>> id_params = + registry.ParseParameters(IdParamsSerialization()); + ASSERT_THAT(id_params, IsOk()); + EXPECT_THAT((*id_params)->HasIdRequirement(), IsTrue()); + EXPECT_THAT(std::type_index(typeid(**id_params)), + std::type_index(typeid(IdParams))); +} + +TEST(SerializationRegistryTest, ParseParametersWithoutRegistration) { + SerializationRegistry::Builder builder; + SerializationRegistry registry = builder.Build(); + + ASSERT_THAT(registry.ParseParameters(NoIdSerialization()).status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(SerializationRegistryTest, RegisterSameParametersParser) { + SerializationRegistry::Builder builder; + ParametersParserImpl<NoIdSerialization, NoIdParams> parser(kNoIdTypeUrl, + ParseNoIdParams); + + EXPECT_THAT(builder.RegisterParametersParser(&parser), IsOk()); + EXPECT_THAT(builder.RegisterParametersParser(&parser), IsOk()); +} + +TEST(SerializationRegistryTest, + RegisterDifferentParametersParserWithSameIndex) { + SerializationRegistry::Builder builder; + ParametersParserImpl<NoIdSerialization, NoIdParams> parser1(kNoIdTypeUrl, + ParseNoIdParams); + ParametersParserImpl<NoIdSerialization, NoIdParams> parser2(kNoIdTypeUrl, + ParseNoIdParams); + + EXPECT_THAT(builder.RegisterParametersParser(&parser1), IsOk()); + EXPECT_THAT(builder.RegisterParametersParser(&parser2), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(SerializationRegistryTest, SerializeParameters) { + SerializationRegistry::Builder builder; + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer1( + kNoIdTypeUrl, SerializeNoIdParams); + ParametersSerializerImpl<IdParams, IdParamsSerialization> serializer2( + kIdTypeUrl, SerializeIdParams); + ASSERT_THAT(builder.RegisterParametersSerializer(&serializer1), IsOk()); + ASSERT_THAT(builder.RegisterParametersSerializer(&serializer2), IsOk()); + + SerializationRegistry registry = builder.Build(); + + util::StatusOr<std::unique_ptr<Serialization>> serialization1 = + registry.SerializeParameters<NoIdSerialization>(NoIdParams()); + ASSERT_THAT(serialization1, IsOk()); + EXPECT_THAT((*serialization1)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + + util::StatusOr<std::unique_ptr<Serialization>> serialization2 = + registry.SerializeParameters<IdParamsSerialization>(IdParams()); + ASSERT_THAT(serialization2, IsOk()); + EXPECT_THAT((*serialization2)->ObjectIdentifier(), Eq(kIdTypeUrl)); +} + +TEST(SerializationRegistryTest, SerializeParametersWithoutRegistration) { + SerializationRegistry::Builder builder; + SerializationRegistry registry = builder.Build(); + + ASSERT_THAT( + registry.SerializeParameters<NoIdSerialization>(NoIdParams()).status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(SerializationRegistryTest, RegisterSameParametersSerializer) { + SerializationRegistry::Builder builder; + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer( + kNoIdTypeUrl, SerializeNoIdParams); + + EXPECT_THAT(builder.RegisterParametersSerializer(&serializer), IsOk()); + EXPECT_THAT(builder.RegisterParametersSerializer(&serializer), IsOk()); +} + +TEST(SerializationRegistryTest, + RegisterDifferentParametersSerializerWithSameIndex) { + SerializationRegistry::Builder builder; + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer1( + kNoIdTypeUrl, SerializeNoIdParams); + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer2( + kNoIdTypeUrl, SerializeNoIdParams); + + EXPECT_THAT(builder.RegisterParametersSerializer(&serializer1), IsOk()); + EXPECT_THAT(builder.RegisterParametersSerializer(&serializer2), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(SerializationRegistryTest, ParseKey) { + SerializationRegistry::Builder builder; + KeyParserImpl<NoIdSerialization, NoIdKey> parser1(kNoIdTypeUrl, ParseNoIdKey); + KeyParserImpl<IdKeySerialization, IdKey> parser2(kIdTypeUrl, ParseIdKey); + ASSERT_THAT(builder.RegisterKeyParser(&parser1), IsOk()); + ASSERT_THAT(builder.RegisterKeyParser(&parser2), IsOk()); + + SerializationRegistry registry = builder.Build(); + + util::StatusOr<std::unique_ptr<Key>> no_id_key = + registry.ParseKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(no_id_key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**no_id_key)), + std::type_index(typeid(NoIdKey))); + + util::StatusOr<std::unique_ptr<Key>> id_key = registry.ParseKey( + IdKeySerialization(/*id=*/123), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(id_key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**id_key)), + std::type_index(typeid(IdKey))); + EXPECT_THAT((*id_key)->GetIdRequirement(), Eq(123)); +} + +TEST(SerializationRegistryTest, ParseKeyNoSecretAccess) { + SerializationRegistry::Builder builder; + KeyParserImpl<NoIdSerialization, NoIdKey> parser(kNoIdTypeUrl, ParseNoIdKey); + ASSERT_THAT(builder.RegisterKeyParser(&parser), IsOk()); + + SerializationRegistry registry = builder.Build(); + + util::StatusOr<std::unique_ptr<Key>> no_id_public_key = + registry.ParseKey(NoIdSerialization(), absl::nullopt); + ASSERT_THAT(no_id_public_key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**no_id_public_key)), + std::type_index(typeid(NoIdKey))); +} + +TEST(SerializationRegistryTest, ParseKeyWithoutRegistration) { + SerializationRegistry::Builder builder; + SerializationRegistry registry = builder.Build(); + + ASSERT_THAT( + registry.ParseKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()) + .status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(SerializationRegistryTest, RegisterSameKeyParser) { + SerializationRegistry::Builder builder; + KeyParserImpl<NoIdSerialization, NoIdKey> parser(kNoIdTypeUrl, ParseNoIdKey); + + EXPECT_THAT(builder.RegisterKeyParser(&parser), IsOk()); + EXPECT_THAT(builder.RegisterKeyParser(&parser), IsOk()); +} + +TEST(SerializationRegistryTest, RegisterDifferentKeyParserWithSameIndex) { + SerializationRegistry::Builder builder; + KeyParserImpl<NoIdSerialization, NoIdKey> parser1(kNoIdTypeUrl, ParseNoIdKey); + KeyParserImpl<NoIdSerialization, NoIdKey> parser2(kNoIdTypeUrl, ParseNoIdKey); + + EXPECT_THAT(builder.RegisterKeyParser(&parser1), IsOk()); + EXPECT_THAT(builder.RegisterKeyParser(&parser2), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(SerializationRegistryTest, SerializeKey) { + SerializationRegistry::Builder builder; + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer1(SerializeNoIdKey); + KeySerializerImpl<IdKey, IdKeySerialization> serializer2(SerializeIdKey); + ASSERT_THAT(builder.RegisterKeySerializer(&serializer1), IsOk()); + ASSERT_THAT(builder.RegisterKeySerializer(&serializer2), IsOk()); + + SerializationRegistry registry = builder.Build(); + + util::StatusOr<std::unique_ptr<Serialization>> serialization1 = + registry.SerializeKey<NoIdSerialization>(NoIdKey(), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization1, IsOk()); + EXPECT_THAT((*serialization1)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + + util::StatusOr<std::unique_ptr<Serialization>> serialization2 = + registry.SerializeKey<IdKeySerialization>(IdKey(123), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization2, IsOk()); + EXPECT_THAT((*serialization2)->ObjectIdentifier(), Eq(kIdTypeUrl)); +} + +TEST(SerializationRegistryTest, SerializeKeyNoSecretAccess) { + SerializationRegistry::Builder builder; + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer(SerializeNoIdKey); + ASSERT_THAT(builder.RegisterKeySerializer(&serializer), IsOk()); + + SerializationRegistry registry = builder.Build(); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + registry.SerializeKey<NoIdSerialization>(NoIdKey(), + absl::nullopt); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); +} + +TEST(SerializationRegistryTest, SerializeKeyWithoutRegistration) { + SerializationRegistry::Builder builder; + SerializationRegistry registry = builder.Build(); + + ASSERT_THAT(registry + .SerializeKey<NoIdSerialization>( + NoIdKey(), InsecureSecretKeyAccess::Get()) + .status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +TEST(SerializationRegistryTest, RegisterSameKeySerializer) { + SerializationRegistry::Builder builder; + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer(SerializeNoIdKey); + + EXPECT_THAT(builder.RegisterKeySerializer(&serializer), IsOk()); + EXPECT_THAT(builder.RegisterKeySerializer(&serializer), IsOk()); +} + +TEST(SerializationRegistryTest, RegisterDifferentKeySerializerWithSameIndex) { + SerializationRegistry::Builder builder; + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer1(SerializeNoIdKey); + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer2(SerializeNoIdKey); + + EXPECT_THAT(builder.RegisterKeySerializer(&serializer1), IsOk()); + EXPECT_THAT(builder.RegisterKeySerializer(&serializer2), + StatusIs(absl::StatusCode::kAlreadyExists)); +} + +TEST(SerializationRegistryTest, BuiltFromAnotherRegistry) { + SerializationRegistry::Builder builder1; + ParametersParserImpl<NoIdSerialization, NoIdParams> parser1(kNoIdTypeUrl, + ParseNoIdParams); + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer1( + kNoIdTypeUrl, SerializeNoIdParams); + ASSERT_THAT(builder1.RegisterParametersParser(&parser1), IsOk()); + ASSERT_THAT(builder1.RegisterParametersSerializer(&serializer1), IsOk()); + + SerializationRegistry registry1 = builder1.Build(); + SerializationRegistry::Builder builder2(registry1); + + KeyParserImpl<NoIdSerialization, NoIdKey> parser2(kNoIdTypeUrl, ParseNoIdKey); + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer2(SerializeNoIdKey); + ASSERT_THAT(builder2.RegisterKeyParser(&parser2), IsOk()); + ASSERT_THAT(builder2.RegisterKeySerializer(&serializer2), IsOk()); + + SerializationRegistry registry2 = builder2.Build(); + + util::StatusOr<std::unique_ptr<Parameters>> params = + registry2.ParseParameters(NoIdSerialization()); + ASSERT_THAT(params, IsOk()); + EXPECT_THAT((*params)->HasIdRequirement(), IsFalse()); + EXPECT_THAT(std::type_index(typeid(**params)), + std::type_index(typeid(NoIdParams))); + + util::StatusOr<std::unique_ptr<Serialization>> params_serialization = + registry2.SerializeParameters<NoIdSerialization>(NoIdParams()); + ASSERT_THAT(params_serialization, IsOk()); + EXPECT_THAT((*params_serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + + util::StatusOr<std::unique_ptr<Key>> key = + registry2.ParseKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**key)), std::type_index(typeid(NoIdKey))); + + util::StatusOr<std::unique_ptr<Serialization>> key_serialization = + registry2.SerializeKey<NoIdSerialization>(NoIdKey(), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key_serialization, IsOk()); + EXPECT_THAT((*key_serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); +} + +TEST(SerializationRegistryTest, RegistryCopy) { + SerializationRegistry::Builder builder; + ParametersParserImpl<NoIdSerialization, NoIdParams> parser1(kNoIdTypeUrl, + ParseNoIdParams); + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer1( + kNoIdTypeUrl, SerializeNoIdParams); + KeyParserImpl<NoIdSerialization, NoIdKey> parser2(kNoIdTypeUrl, ParseNoIdKey); + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer2(SerializeNoIdKey); + ASSERT_THAT(builder.RegisterParametersParser(&parser1), IsOk()); + ASSERT_THAT(builder.RegisterParametersSerializer(&serializer1), IsOk()); + ASSERT_THAT(builder.RegisterKeyParser(&parser2), IsOk()); + ASSERT_THAT(builder.RegisterKeySerializer(&serializer2), IsOk()); + + SerializationRegistry registry1 = builder.Build(); + SerializationRegistry registry2 = registry1; + + util::StatusOr<std::unique_ptr<Parameters>> params = + registry2.ParseParameters(NoIdSerialization()); + ASSERT_THAT(params, IsOk()); + EXPECT_THAT((*params)->HasIdRequirement(), IsFalse()); + EXPECT_THAT(std::type_index(typeid(**params)), + std::type_index(typeid(NoIdParams))); + + util::StatusOr<std::unique_ptr<Serialization>> params_serialization = + registry2.SerializeParameters<NoIdSerialization>(NoIdParams()); + ASSERT_THAT(params_serialization, IsOk()); + EXPECT_THAT((*params_serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + + util::StatusOr<std::unique_ptr<Key>> key = + registry2.ParseKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**key)), std::type_index(typeid(NoIdKey))); + + util::StatusOr<std::unique_ptr<Serialization>> key_serialization = + registry2.SerializeKey<NoIdSerialization>(NoIdKey(), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key_serialization, IsOk()); + EXPECT_THAT((*key_serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); +} + +TEST(SerializationRegistryTest, RegistryMove) { + SerializationRegistry::Builder builder; + ParametersParserImpl<NoIdSerialization, NoIdParams> parser1(kNoIdTypeUrl, + ParseNoIdParams); + ParametersSerializerImpl<NoIdParams, NoIdSerialization> serializer1( + kNoIdTypeUrl, SerializeNoIdParams); + KeyParserImpl<NoIdSerialization, NoIdKey> parser2(kNoIdTypeUrl, ParseNoIdKey); + KeySerializerImpl<NoIdKey, NoIdSerialization> serializer2(SerializeNoIdKey); + ASSERT_THAT(builder.RegisterParametersParser(&parser1), IsOk()); + ASSERT_THAT(builder.RegisterParametersSerializer(&serializer1), IsOk()); + ASSERT_THAT(builder.RegisterKeyParser(&parser2), IsOk()); + ASSERT_THAT(builder.RegisterKeySerializer(&serializer2), IsOk()); + + SerializationRegistry registry1 = builder.Build(); + SerializationRegistry registry2 = std::move(registry1); + + util::StatusOr<std::unique_ptr<Parameters>> params = + registry2.ParseParameters(NoIdSerialization()); + ASSERT_THAT(params, IsOk()); + EXPECT_THAT((*params)->HasIdRequirement(), IsFalse()); + EXPECT_THAT(std::type_index(typeid(**params)), + std::type_index(typeid(NoIdParams))); + + util::StatusOr<std::unique_ptr<Serialization>> params_serialization = + registry2.SerializeParameters<NoIdSerialization>(NoIdParams()); + ASSERT_THAT(params_serialization, IsOk()); + EXPECT_THAT((*params_serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); + + util::StatusOr<std::unique_ptr<Key>> key = + registry2.ParseKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT(std::type_index(typeid(**key)), std::type_index(typeid(NoIdKey))); + + util::StatusOr<std::unique_ptr<Serialization>> key_serialization = + registry2.SerializeKey<NoIdSerialization>(NoIdKey(), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key_serialization, IsOk()); + EXPECT_THAT((*key_serialization)->ObjectIdentifier(), Eq(kNoIdTypeUrl)); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/serialization_test_util.h b/cc/internal/serialization_test_util.h new file mode 100644 index 0000000..b772a0a --- /dev/null +++ b/cc/internal/serialization_test_util.h
@@ -0,0 +1,189 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_SERIALIZATION_TEST_UTIL_H_ +#define TINK_INTERNAL_SERIALIZATION_TEST_UTIL_H_ + +#include <string> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/internal/serialization.h" +#include "tink/key.h" +#include "tink/parameters.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +constexpr absl::string_view kNoIdTypeUrl = "NoIdTypeUrl"; +constexpr absl::string_view kIdTypeUrl = "IdTypeUrl"; + +// Generic serialization for keys or parameters. +class BaseSerialization : public Serialization { + public: + explicit BaseSerialization(absl::string_view object_identifier) + : object_identifier_(object_identifier) {} + + absl::string_view ObjectIdentifier() const override { + return object_identifier_; + } + + bool operator==(const BaseSerialization& other) const { + return object_identifier_ == other.object_identifier_; + } + + private: + std::string object_identifier_; +}; + +// Serialization for keys or parameters without an ID requirement. +class NoIdSerialization : public BaseSerialization { + public: + NoIdSerialization() : BaseSerialization(kNoIdTypeUrl) {} +}; + +// Serialization for parameters with an ID requirement. +class IdParamsSerialization : public BaseSerialization { + public: + IdParamsSerialization() : BaseSerialization(kIdTypeUrl) {} +}; + +// Serialization for keys with an ID requirement. +class IdKeySerialization : public BaseSerialization { + public: + explicit IdKeySerialization(int id) + : BaseSerialization(kIdTypeUrl), id_(id) {} + + int GetKeyId() const { return id_; } + + private: + int id_; +}; + +// Parameters without an ID requirement. +class NoIdParams : public Parameters { + public: + bool HasIdRequirement() const override { return false; } + + bool operator==(const Parameters& other) const override { + return !other.HasIdRequirement(); + } +}; + +// Key without an ID requirement. +class NoIdKey : public Key { + public: + const Parameters& GetParameters() const override { return params_; } + + absl::optional<int> GetIdRequirement() const override { + return absl::nullopt; + } + + bool operator==(const Key& other) const override { + return params_ == other.GetParameters() && + absl::nullopt == other.GetIdRequirement(); + } + + private: + NoIdParams params_; +}; + +// Parameters with an ID requirement. +class IdParams : public Parameters { + public: + bool HasIdRequirement() const override { return true; } + + bool operator==(const Parameters& other) const override { + return other.HasIdRequirement(); + } +}; + +// Key with an ID requirement. +class IdKey : public Key { + public: + explicit IdKey(int id) : id_(id) {} + + const Parameters& GetParameters() const override { return params_; } + + absl::optional<int> GetIdRequirement() const override { return id_; } + + bool operator==(const Key& other) const override { + return params_ == other.GetParameters() && id_ == other.GetIdRequirement(); + } + + private: + IdParams params_; + int id_; +}; + +// Parse `serialization` into parameters without an ID requirement. +inline util::StatusOr<NoIdParams> ParseNoIdParams( + NoIdSerialization serialization) { + return NoIdParams(); +} + +// Parse `serialization` into parameters with an ID requirement. +inline util::StatusOr<IdParams> ParseIdParams( + IdParamsSerialization serialization) { + return IdParams(); +} + +// Serialize `parameters` without an ID requirement. +inline util::StatusOr<NoIdSerialization> SerializeNoIdParams( + NoIdParams parameters) { + return NoIdSerialization(); +} + +// Serialize `parameters` with an ID requirement. +inline util::StatusOr<IdParamsSerialization> SerializeIdParams( + IdParams parameters) { + return IdParamsSerialization(); +} + +// Parse `serialization` into a key without an ID requirement. +inline util::StatusOr<NoIdKey> ParseNoIdKey( + NoIdSerialization serialization, + absl::optional<SecretKeyAccessToken> token) { + return NoIdKey(); +} + +// Parse `serialization` into a key with an ID requirement. +inline util::StatusOr<IdKey> ParseIdKey( + IdKeySerialization serialization, + absl::optional<SecretKeyAccessToken> token) { + return IdKey(serialization.GetKeyId()); +} + +// Serialize `key` without an ID requirement. +inline util::StatusOr<NoIdSerialization> SerializeNoIdKey( + NoIdKey key, absl::optional<SecretKeyAccessToken> token) { + return NoIdSerialization(); +} + +// Serialize `key` with an ID requirement. +inline util::StatusOr<IdKeySerialization> SerializeIdKey( + IdKey key, absl::optional<SecretKeyAccessToken> token) { + return IdKeySerialization(key.GetIdRequirement().value()); +} + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_SERIALIZATION_TEST_UTIL_H_
diff --git a/cc/internal/serialization_test_util_test.cc b/cc/internal/serialization_test_util_test.cc new file mode 100644 index 0000000..1309af8 --- /dev/null +++ b/cc/internal/serialization_test_util_test.cc
@@ -0,0 +1,116 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/serialization_test_util.h" + +#include <string_view> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/types/optional.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/parameters.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOkAndHolds; +using ::testing::Eq; +using ::testing::IsFalse; +using ::testing::IsTrue; +using ::testing::Not; + +TEST(SerializationTest, Create) { + EXPECT_THAT(BaseSerialization("base_type_url").ObjectIdentifier(), + Eq("base_type_url")); + EXPECT_THAT(NoIdSerialization().ObjectIdentifier(), Eq(kNoIdTypeUrl)); + EXPECT_THAT(IdParamsSerialization().ObjectIdentifier(), Eq(kIdTypeUrl)); + + IdKeySerialization id_key(123); + EXPECT_THAT(id_key.ObjectIdentifier(), Eq(kIdTypeUrl)); + EXPECT_THAT(id_key.GetKeyId(), Eq(123)); +} + +TEST(NoIdParamsTest, Create) { + NoIdParams params; + + EXPECT_THAT(params.HasIdRequirement(), IsFalse()); + EXPECT_THAT(params, Eq(NoIdParams())); + EXPECT_THAT(params, Not(Eq(IdParams()))); +} + +TEST(NoIdParamsTest, ParseAndSerialize) { + EXPECT_THAT(ParseNoIdParams(NoIdSerialization()), IsOkAndHolds(NoIdParams())); + EXPECT_THAT(SerializeNoIdParams(NoIdParams()), + IsOkAndHolds(NoIdSerialization())); +} + +TEST(IdParamsTest, Create) { + IdParams params; + + EXPECT_THAT(params.HasIdRequirement(), IsTrue()); + EXPECT_THAT(params, Eq(IdParams())); + EXPECT_THAT(params, Not(Eq(NoIdParams()))); +} + +TEST(IdParamsTest, ParseAndSerialize) { + EXPECT_THAT(ParseIdParams(IdParamsSerialization()), IsOkAndHolds(IdParams())); + EXPECT_THAT(SerializeIdParams(IdParams()), + IsOkAndHolds(IdParamsSerialization())); +} + +TEST(NoIdKeyTest, Create) { + NoIdKey key; + + EXPECT_THAT(key.GetIdRequirement(), Eq(absl::nullopt)); + EXPECT_THAT(key.GetParameters(), Eq(NoIdParams())); + EXPECT_THAT(key, Eq(NoIdKey())); + EXPECT_THAT(key, Not(Eq(IdKey(123)))); +} + +TEST(NoIdKeyTest, ParseAndSerialize) { + EXPECT_THAT(ParseNoIdKey(NoIdSerialization(), InsecureSecretKeyAccess::Get()), + IsOkAndHolds(NoIdKey())); + EXPECT_THAT(SerializeNoIdKey(NoIdKey(), InsecureSecretKeyAccess::Get()), + IsOkAndHolds(NoIdSerialization())); +} + +TEST(IdKeyTest, Create) { + IdKey key(123); + + EXPECT_THAT(key.GetIdRequirement(), Eq(123)); + EXPECT_THAT(key.GetParameters(), Eq(IdParams())); + EXPECT_THAT(key, Eq(IdKey(123))); + EXPECT_THAT(key, Not(Eq(IdKey(456)))); + EXPECT_THAT(key, Not(Eq(NoIdKey()))); +} + +TEST(IdKeyTest, ParseAndSerialize) { + EXPECT_THAT(ParseIdKey(IdKeySerialization(123), + InsecureSecretKeyAccess::Get()), + IsOkAndHolds(IdKey(123))); + EXPECT_THAT(SerializeIdKey(IdKey(123), InsecureSecretKeyAccess::Get()), + IsOkAndHolds(IdKeySerialization(123))); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/serializer_index.h b/cc/internal/serializer_index.h new file mode 100644 index 0000000..e0c4e66 --- /dev/null +++ b/cc/internal/serializer_index.h
@@ -0,0 +1,82 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_INTERNAL_SERIALIZER_INDEX_H_ +#define TINK_INTERNAL_SERIALIZER_INDEX_H_ + +#include <string> +#include <typeindex> + +#include "tink/internal/serialization.h" +#include "tink/key.h" +#include "tink/parameters.h" + +namespace crypto { +namespace tink { +namespace internal { + +class SerializerIndex { + public: + // Create registry lookup key for the combination of the `KeyOrParameterT` and + // `SerializationT` types. Useful for key and parameters serializers. + template <typename KeyOrParameterT, typename SerializationT> + static SerializerIndex Create() { + return SerializerIndex(std::type_index(typeid(KeyOrParameterT)), + std::type_index(typeid(SerializationT))); + } + + // Create registry lookup key for `SerializationT` type and `parameters`. + // Useful for the serialization registry. + template <typename SerializationT> + static SerializerIndex Create(const Parameters& parameters) { + return SerializerIndex(std::type_index(typeid(parameters)), + std::type_index(typeid(SerializationT))); + } + + // Create registry lookup key for `SerializationT` type and `key`. Useful for + // the serialization registry. + template <typename SerializationT> + static SerializerIndex Create(const Key& key) { + return SerializerIndex(std::type_index(typeid(key)), + std::type_index(typeid(SerializationT))); + } + + // Returns true if key/parameters index and serialization type index match. + bool operator==(const SerializerIndex& other) const { + return kp_index_ == other.kp_index_ && + serialization_index_ == other.serialization_index_; + } + + // Required function to make `SerializerIndex` hashable for Abseil hash maps. + template <typename H> + friend H AbslHashValue(H h, const SerializerIndex& index) { + return H::combine(std::move(h), index.kp_index_, + index.serialization_index_); + } + + private: + SerializerIndex(std::type_index kp_index, std::type_index serialization_index) + : kp_index_(kp_index), serialization_index_(serialization_index) {} + + std::type_index kp_index_; + std::type_index serialization_index_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_SERIALIZER_INDEX_H_
diff --git a/cc/internal/serializer_index_test.cc b/cc/internal/serializer_index_test.cc new file mode 100644 index 0000000..9653ee6 --- /dev/null +++ b/cc/internal/serializer_index_test.cc
@@ -0,0 +1,91 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/serializer_index.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/internal/serialization_test_util.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::testing::Eq; +using ::testing::Not; + +TEST(SerializerIndex, CreateEquivalentFromParameters) { + // Multi-parameter templates require extra surrounding parentheses. + ASSERT_THAT((SerializerIndex::Create<NoIdParams, NoIdSerialization>()), + Eq((SerializerIndex::Create<NoIdParams, NoIdSerialization>()))); + ASSERT_THAT((SerializerIndex::Create<NoIdParams, NoIdSerialization>()), + Eq((SerializerIndex::Create<NoIdSerialization>(NoIdParams())))); + ASSERT_THAT((SerializerIndex::Create<NoIdSerialization>(NoIdParams())), + Eq((SerializerIndex::Create<NoIdSerialization>(NoIdParams())))); +} + +TEST(SerializerIndex, CreateFromDifferentParametersType) { + // Multi-parameter templates require extra surrounding parentheses. + ASSERT_THAT( + (SerializerIndex::Create<NoIdParams, NoIdSerialization>()), + Not(Eq((SerializerIndex::Create<IdParams, NoIdSerialization>())))); + ASSERT_THAT( + (SerializerIndex::Create<NoIdSerialization>(NoIdParams())), + Not(Eq((SerializerIndex::Create<NoIdSerialization>(IdParams()))))); +} + +TEST(SerializerIndex, CreateFromSameParametersTypeWithDifferentSerialization) { + // Multi-parameter templates require extra surrounding parentheses. + ASSERT_THAT( + (SerializerIndex::Create<NoIdParams, NoIdSerialization>()), + Not(Eq((SerializerIndex::Create<NoIdParams, IdParamsSerialization>())))); + ASSERT_THAT( + (SerializerIndex::Create<NoIdSerialization>(NoIdParams())), + Not(Eq((SerializerIndex::Create<IdParamsSerialization>(NoIdParams()))))); +} + +TEST(SerializerIndex, CreateEquivalentFromKey) { + // Multi-parameter templates require extra surrounding parentheses. + ASSERT_THAT((SerializerIndex::Create<NoIdKey, NoIdSerialization>()), + Eq((SerializerIndex::Create<NoIdKey, NoIdSerialization>()))); + ASSERT_THAT((SerializerIndex::Create<NoIdKey, NoIdSerialization>()), + Eq((SerializerIndex::Create<NoIdSerialization>(NoIdKey())))); + ASSERT_THAT((SerializerIndex::Create<NoIdSerialization>(NoIdKey())), + Eq((SerializerIndex::Create<NoIdSerialization>(NoIdKey())))); +} + +TEST(SerializerIndex, CreateFromDifferentKeyType) { + // Multi-parameter templates require extra surrounding parentheses. + ASSERT_THAT((SerializerIndex::Create<NoIdKey, NoIdSerialization>()), + Not(Eq((SerializerIndex::Create<IdKey, NoIdSerialization>())))); + ASSERT_THAT( + (SerializerIndex::Create<NoIdSerialization>(NoIdKey())), + Not(Eq((SerializerIndex::Create<NoIdSerialization>(IdKey(/*id=*/1)))))); +} + +TEST(SerializerIndex, CreateFromSameKeyTypeWithDifferentSerialization) { + // Multi-parameter templates require extra surrounding parentheses. + ASSERT_THAT( + (SerializerIndex::Create<NoIdKey, NoIdSerialization>()), + Not(Eq((SerializerIndex::Create<NoIdKey, IdKeySerialization>())))); + ASSERT_THAT( + (SerializerIndex::Create<NoIdSerialization>(NoIdKey())), + Not(Eq((SerializerIndex::Create<IdKeySerialization>(NoIdKey()))))); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/ssl_unique_ptr.h b/cc/internal/ssl_unique_ptr.h index d7c0586..7bc6314 100644 --- a/cc/internal/ssl_unique_ptr.h +++ b/cc/internal/ssl_unique_ptr.h
@@ -17,6 +17,7 @@ #define TINK_INTERNAL_SSL_UNIQUE_PTR_H_ #include <memory> + // Every header in BoringSSL includes base.h, which in turn defines // OPENSSL_IS_BORINGSSL. So we include this common header here to "force" the // definition of OPENSSL_IS_BORINGSSL in case BoringSSL is used.
diff --git a/cc/internal/test_file_util.cc b/cc/internal/test_file_util.cc new file mode 100644 index 0000000..bdb9374 --- /dev/null +++ b/cc/internal/test_file_util.cc
@@ -0,0 +1,77 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/internal/test_file_util.h" + +#include <fstream> +#include <ios> +#include <string> +#include <vector> + +#include "gtest/gtest.h" +#include "absl/log/check.h" +#include "absl/status/status.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "tink/subtle/random.h" +#include "tink/util/status.h" +#include "tink/util/test_util.h" + +namespace crypto { +namespace tink { +namespace internal { + +util::Status CreateTestFile(absl::string_view filename, + absl::string_view file_content) { + std::string full_filename = absl::StrCat(test::TmpDir(), "/", filename); + std::ofstream output_stream(full_filename, std::ios::binary); + if (!output_stream) { + return util::Status(absl::StatusCode::kInternal, "Cannot open file"); + } + output_stream.write(file_content.data(), file_content.size()); + return util::OkStatus(); +} + +std::string GetTestFileNamePrefix() { + const testing::TestInfo* const test_info = + testing::UnitTest::GetInstance()->current_test_info(); + CHECK(test_info != nullptr); + std::string random_string = subtle::Random::GetRandomBytes(/*length=*/16); + std::string test_suite_name = test_info->test_suite_name(); + std::string test_name = test_info->name(); + // Parametrized tests return test_suite_name of the form <Prefix>/<Test Suite> + // and name of the form <Test Name>/<Suffix>. + // In this case, get only the prefix and test name. Keeping all of these may + // result in a file name that is too long. + if (test_info->value_param() != nullptr) { + std::vector<std::string> test_suite_parts = + absl::StrSplit(test_info->test_suite_name(), '/'); + CHECK_GE(test_suite_parts.size(), 1); + test_suite_name = test_suite_parts[0]; + std::vector<std::string> test_name_parts = + absl::StrSplit(test_info->name(), '/'); + CHECK_GE(test_name_parts.size(), 1); + test_name = test_name_parts[0]; + } + return absl::StrCat(test_suite_name, "_", test_name, "_", + absl::BytesToHexString(random_string)); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/test_file_util.h b/cc/internal/test_file_util.h index 9f37a56..f3e37aa 100644 --- a/cc/internal/test_file_util.h +++ b/cc/internal/test_file_util.h
@@ -20,6 +20,7 @@ #include <string> #include "absl/strings/string_view.h" +#include "tink/util/status.h" namespace crypto { namespace tink { @@ -33,6 +34,13 @@ // Returns the path of the specified file in the runfiles directory. std::string RunfilesPath(absl::string_view path); +crypto::tink::util::Status CreateTestFile(absl::string_view filename, + absl::string_view file_content); + +// Returns the prefix to use for files to use in tests. The result will be of +// the form: <test name>_<testcase name>_<hex encoded random 32 bytes string>. +std::string GetTestFileNamePrefix(); + } // namespace internal } // namespace tink } // namespace crypto
diff --git a/cc/internal/test_random_access_stream.cc b/cc/internal/test_random_access_stream.cc new file mode 100644 index 0000000..53918d8 --- /dev/null +++ b/cc/internal/test_random_access_stream.cc
@@ -0,0 +1,97 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// +#include "tink/internal/test_random_access_stream.h" + +#include <stdint.h> + +#include <algorithm> +#include <memory> +#include <string> +#include <utility> + +#include "absl/status/status.h" +#include "tink/random_access_stream.h" +#include "tink/util/buffer.h" +#include "tink/util/status.h" + +namespace crypto { +namespace tink { +namespace internal { + +util::Status TestRandomAccessStream::PRead(int64_t position, int count, + util::Buffer* dest_buffer) { + if (dest_buffer == nullptr) { + return util::Status(absl::StatusCode::kInvalidArgument, + "dest_buffer must be non-null"); + } + if (count <= 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "count must be positive"); + } + if (count > dest_buffer->allocated_size()) { + return util::Status(absl::StatusCode::kInvalidArgument, "buffer too small"); + } + if (position < 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "position cannot be negative"); + } + if (position >= content_.size()) { + dest_buffer->set_size(0).IgnoreError(); + return util::Status(absl::StatusCode::kOutOfRange, "EOF"); + } + util::Status status = dest_buffer->set_size(count); + if (!status.ok()) { + return status; + } + int read_count = + std::min(count, static_cast<int>(content_.size() - position)); + std::copy(content_.begin() + position, + content_.begin() + position + read_count, + dest_buffer->get_mem_block()); + status = dest_buffer->set_size(read_count); + if (!status.ok()) { + return status; + } + if (position + read_count == content_.size()) { + // We reached EOF. + return util::Status(absl::StatusCode::kOutOfRange, "EOF"); + } + return util::OkStatus(); +} + +util::Status ReadAllFromRandomAccessStream( + RandomAccessStream* random_access_stream, std::string& contents, + int chunk_size) { + if (chunk_size < 1) { + return util::Status(absl::StatusCode::kInvalidArgument, + "chunk_size must be greater than zero"); + } + contents.clear(); + std::unique_ptr<util::Buffer> buffer = + *std::move(util::Buffer::New(chunk_size)); + int64_t position = 0; + auto status = util::OkStatus(); + while (status.ok()) { + status = random_access_stream->PRead(position, chunk_size, buffer.get()); + contents.append(buffer->get_mem_block(), buffer->size()); + position = contents.size(); + } + return status; +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/test_random_access_stream.h b/cc/internal/test_random_access_stream.h new file mode 100644 index 0000000..5e2e8fa --- /dev/null +++ b/cc/internal/test_random_access_stream.h
@@ -0,0 +1,67 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_INTERNAL_TEST_RANDOM_ACCESS_STREAM_H_ +#define TINK_INTERNAL_TEST_RANDOM_ACCESS_STREAM_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <utility> + +#include "absl/strings/string_view.h" +#include "tink/random_access_stream.h" +#include "tink/util/buffer.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +// A simple test-only RandomAccessStream implementation that reads from a +// std::string. +class TestRandomAccessStream : public RandomAccessStream { + public: + explicit TestRandomAccessStream(std::string content) + : content_(std::move(content)) {} + // Move only. + TestRandomAccessStream(TestRandomAccessStream&& other) = default; + TestRandomAccessStream& operator=(TestRandomAccessStream&& other) = default; + TestRandomAccessStream(const TestRandomAccessStream&) = delete; + TestRandomAccessStream& operator=(const TestRandomAccessStream&) = delete; + + util::Status PRead(int64_t position, int count, + util::Buffer* dest_buffer) override; + + util::StatusOr<int64_t> size() override { return content_.size(); } + + private: + std::string content_; +}; + +// Reads the entire `random_access_stream` using a buffer of size `chunk_size` +// until no more bytes can be read, and puts the read bytes into `contents`. +// Returns the status of the last call to random_access_stream->PRead(). +util::Status ReadAllFromRandomAccessStream( + RandomAccessStream* random_access_stream, std::string& contents, + int chunk_size = 42); + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_INTERNAL_TEST_RANDOM_ACCESS_STREAM_H_
diff --git a/cc/internal/test_random_access_stream_test.cc b/cc/internal/test_random_access_stream_test.cc new file mode 100644 index 0000000..28ddcaa --- /dev/null +++ b/cc/internal/test_random_access_stream_test.cc
@@ -0,0 +1,161 @@ +// Copyright 2023 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. +// +/////////////////////////////////////////////////////////////////////////////// +#include "tink/internal/test_random_access_stream.h" + +#include <memory> +#include <string> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/subtle/random.h" +#include "tink/util/buffer.h" +#include "tink/util/status.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::StatusIs; + +TEST(TestRandomAccessStreamTest, ReadAllSucceeds) { + const int buffer_size = 4 * 1024; + const int stream_size = 100 * 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + auto rand_access_stream = + std::make_unique<TestRandomAccessStream>(stream_content); + auto buffer = *std::move(util::Buffer::New(buffer_size)); + util::Status pread_status = util::OkStatus(); + std::string result; + do { + pread_status = + rand_access_stream->PRead(result.size(), buffer_size, buffer.get()); + result.append(buffer->get_mem_block(), buffer->size()); + } while (pread_status.ok()); + EXPECT_THAT(pread_status, StatusIs(absl::StatusCode::kOutOfRange)); + EXPECT_EQ(result, stream_content); +} + +TEST(TestRandomAccessStreamTest, PreadAllInOnePread) { + const int stream_size = 8 * 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + auto rand_access_stream = + std::make_unique<TestRandomAccessStream>(stream_content); + auto buffer = *std::move(util::Buffer::New(stream_size)); + ASSERT_THAT( + rand_access_stream->PRead(/*position=*/0, stream_size, buffer.get()), + StatusIs(absl::StatusCode::kOutOfRange)); + EXPECT_EQ(std::string(buffer->get_mem_block(), buffer->size()), + stream_content); +} + +TEST(TestRandomAccessStreamTest, PreadCountLargerThanBufferFails) { + const int buffer_size = 4 * 1024; + const int stream_size = 100 * 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + auto rand_access_stream = + std::make_unique<TestRandomAccessStream>(stream_content); + auto buffer = *std::move(util::Buffer::New(buffer_size)); + EXPECT_THAT( + rand_access_stream->PRead(/*position=*/0, buffer_size + 1, buffer.get()), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(TestRandomAccessStreamTest, InvalidPosition) { + const int buffer_size = 4 * 1024; + const int stream_size = 100 * 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + auto rand_access_stream = + std::make_unique<TestRandomAccessStream>(stream_content); + auto buffer = *std::move(util::Buffer::New(buffer_size)); + EXPECT_THAT(rand_access_stream->PRead(-1, buffer_size, buffer.get()), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(TestRandomAccessStreamTest, PreadWithNullBufferFails) { + const int stream_size = 100 * 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + auto rand_access_stream = + std::make_unique<TestRandomAccessStream>(stream_content); + EXPECT_THAT(rand_access_stream->PRead(/*position=*/0, stream_size, + /*dest_buffer=*/nullptr), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(TestRandomAccessStreamTest, PreadWithEmptyStreamEof) { + const int buffer_size = 4 * 1024; + std::string stream_content; // Empty string. + auto rand_access_stream = + std::make_unique<TestRandomAccessStream>(stream_content); + auto buffer = *std::move(util::Buffer::New(buffer_size)); + EXPECT_THAT( + rand_access_stream->PRead(/*position=*/0, buffer_size, buffer.get()), + StatusIs(absl::StatusCode::kOutOfRange)); +} + +// Pread of the last partial block populates the buffer with the remaining +// bytes and returns an EOF status. +TEST(TestRandomAccessStreamTest, PreadTheLastPartialBlockReturnsEof) { + const int buffer_size = 4 * 1024; + const int stream_size = 100 * 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + auto rand_access_stream = + std::make_unique<TestRandomAccessStream>(stream_content); + auto buffer = *std::move(util::Buffer::New(buffer_size)); + // Read at a postion so that only buffer_size - 1 bytes are left. + EXPECT_THAT(rand_access_stream->PRead(stream_size - buffer_size + 1, + buffer_size, buffer.get()), + StatusIs(absl::StatusCode::kOutOfRange)); + EXPECT_EQ(buffer->size(), buffer_size - 1); + EXPECT_EQ(std::string(buffer->get_mem_block(), buffer->size()), + stream_content.substr(stream_size - buffer_size + 1)); +} + +TEST(TestRandomAccessStreamTest, ReadAllFromRandomAccessStreamSucceeds) { + std::string content_to_read = subtle::Random::GetRandomBytes(4 * 1024); + auto test_random_access_stream = + std::make_unique<TestRandomAccessStream>(content_to_read); + std::string read_content; + EXPECT_THAT(ReadAllFromRandomAccessStream(test_random_access_stream.get(), + read_content, + /*chunk_size=*/128), + StatusIs(absl::StatusCode::kOutOfRange)); + EXPECT_EQ(content_to_read, read_content); +} + +TEST(TestRandomAccessStreamTest, + ReadAllFromRandomAccessStreamFailsWhenChunkIsLessThanOne) { + std::string content_to_read = subtle::Random::GetRandomBytes(4 * 1024); + auto test_random_access_stream = + std::make_unique<TestRandomAccessStream>(content_to_read); + std::string read_content; + EXPECT_THAT(ReadAllFromRandomAccessStream(test_random_access_stream.get(), + read_content, + /*chunk_size=*/0), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(ReadAllFromRandomAccessStream(test_random_access_stream.get(), + read_content, + /*chunk_size=*/-10), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/internal/util.cc b/cc/internal/util.cc index 25c0aa0..c569d80 100644 --- a/cc/internal/util.cc +++ b/cc/internal/util.cc
@@ -18,6 +18,8 @@ #include <iterator> #include <functional> +#include "absl/strings/ascii.h" +#include "absl/log/log.h" #include "absl/strings/string_view.h" namespace crypto { @@ -57,6 +59,19 @@ std::prev(first.end()), std::prev(second.end())); } +bool IsPrintableAscii(absl::string_view input) { + for (char c : input) { + if (!absl::ascii_isprint(c) || absl::ascii_isspace(c)) { + return false; + } + } + return true; +} + +void LogFatal(absl::string_view msg) { + LOG(FATAL) << msg; +} + } // namespace internal } // namespace tink } // namespace crypto
diff --git a/cc/internal/util.h b/cc/internal/util.h index 247e499..6973189 100644 --- a/cc/internal/util.h +++ b/cc/internal/util.h
@@ -16,6 +16,7 @@ #ifndef TINK_INTERNAL_UTIL_H_ #define TINK_INTERNAL_UTIL_H_ +#include "absl/base/attributes.h" #include "absl/strings/string_view.h" namespace crypto { @@ -31,6 +32,23 @@ // Returns true if `first` fully overlaps with `second`. bool BuffersAreIdentical(absl::string_view first, absl::string_view second); +// Returns true if `input` only contains printable ASCII characters (whitespace +// is not allowed). +bool IsPrintableAscii(absl::string_view input); + +// Returns true if built on Windows; false otherwise. +inline bool IsWindows() { +#if defined(_WIN32) + return true; +#else + return false; +#endif +} + +// Wraps Abseil's LOG(FATAL) macro and sets the [noreturn] attribute, which is +// useful for avoiding false positive [-Werror=return-type] compiler errors. +ABSL_ATTRIBUTE_NORETURN void LogFatal(absl::string_view msg); + } // namespace internal } // namespace tink } // namespace crypto
diff --git a/cc/internal/util_test.cc b/cc/internal/util_test.cc index 1746993..fe470a8 100644 --- a/cc/internal/util_test.cc +++ b/cc/internal/util_test.cc
@@ -26,6 +26,9 @@ namespace internal { namespace { +using ::testing::IsFalse; +using ::testing::IsTrue; + constexpr absl::string_view kLongString = "a long buffer with \n several \n newlines"; @@ -97,6 +100,22 @@ EXPECT_FALSE(BuffersAreIdentical(buffer.substr(10, 5), buffer.substr(0, 10))); } +TEST(UtilTest, IsPrintableAscii) { + const std::string input = + "!\"#$%&'()*+,-./" + "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" + "abcdefghijklmnopqrstuvwxyz{|}~"; + EXPECT_THAT(IsPrintableAscii(input), IsTrue()); +} + +TEST(UtilTest, IsNotPrintableAscii) { + EXPECT_THAT(IsPrintableAscii("\n"), IsFalse()); + EXPECT_THAT(IsPrintableAscii("\t"), IsFalse()); + EXPECT_THAT(IsPrintableAscii(" "), IsFalse()); + EXPECT_THAT(IsPrintableAscii(std::string("\x7f", 1)), IsFalse()); + EXPECT_THAT(IsPrintableAscii("ö"), IsFalse()); +} + } // namespace } // namespace internal } // namespace tink
diff --git a/cc/json_keyset_reader.h b/cc/json_keyset_reader.h index 350d7a8..8bcb0cc 100644 --- a/cc/json_keyset_reader.h +++ b/cc/json_keyset_reader.h
@@ -18,6 +18,7 @@ #define TINK_JSON_KEYSET_READER_H_ #include <istream> +#include <memory> #include <string> #include <utility>
diff --git a/cc/json_keyset_writer.h b/cc/json_keyset_writer.h index 81dd488..d7fbb65 100644 --- a/cc/json_keyset_writer.h +++ b/cc/json_keyset_writer.h
@@ -17,6 +17,7 @@ #ifndef TINK_JSON_KEYSET_WRITER_H_ #define TINK_JSON_KEYSET_WRITER_H_ +#include <memory> #include <ostream> #include <utility>
diff --git a/cc/jwt/internal/jwt_ecdsa_sign_key_manager.cc b/cc/jwt/internal/jwt_ecdsa_sign_key_manager.cc index 009057e..b264e40 100644 --- a/cc/jwt/internal/jwt_ecdsa_sign_key_manager.cc +++ b/cc/jwt/internal/jwt_ecdsa_sign_key_manager.cc
@@ -15,6 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/jwt/internal/jwt_ecdsa_sign_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/jwt_ecdsa_sign_key_manager.h b/cc/jwt/internal/jwt_ecdsa_sign_key_manager.h index 0259a26..4c7e3e2 100644 --- a/cc/jwt/internal/jwt_ecdsa_sign_key_manager.h +++ b/cc/jwt/internal/jwt_ecdsa_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_ECDSA_SIGN_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_JWT_ECDSA_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/jwt_ecdsa_sign_verify_key_manager_test.cc b/cc/jwt/internal/jwt_ecdsa_sign_verify_key_manager_test.cc index 8172973..2bd3dca 100644 --- a/cc/jwt/internal/jwt_ecdsa_sign_verify_key_manager_test.cc +++ b/cc/jwt/internal/jwt_ecdsa_sign_verify_key_manager_test.cc
@@ -14,8 +14,10 @@ // //////////////////////////////////////////////////////////////////////////////// +#include <memory> #include <string> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/jwt/internal/jwt_ecdsa_verify_key_manager.cc b/cc/jwt/internal/jwt_ecdsa_verify_key_manager.cc index e6a51d5..68d8505 100644 --- a/cc/jwt/internal/jwt_ecdsa_verify_key_manager.cc +++ b/cc/jwt/internal/jwt_ecdsa_verify_key_manager.cc
@@ -15,6 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/jwt/internal/jwt_ecdsa_verify_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/jwt_ecdsa_verify_key_manager.h b/cc/jwt/internal/jwt_ecdsa_verify_key_manager.h index 5e23d58..530d2ba 100644 --- a/cc/jwt/internal/jwt_ecdsa_verify_key_manager.h +++ b/cc/jwt/internal/jwt_ecdsa_verify_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_ECDSA_VERIFY_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_JWT_ECDSA_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/jwt_format_test.cc b/cc/jwt/internal/jwt_format_test.cc index f25652e..4d963c6 100644 --- a/cc/jwt/internal/jwt_format_test.cc +++ b/cc/jwt/internal/jwt_format_test.cc
@@ -17,6 +17,7 @@ #include "tink/jwt/internal/jwt_format.h" #include <string> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/jwt/internal/jwt_hmac_key_manager.h b/cc/jwt/internal/jwt_hmac_key_manager.h index 03228dd..02f544d 100644 --- a/cc/jwt/internal/jwt_hmac_key_manager.h +++ b/cc/jwt/internal/jwt_hmac_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_HMAC_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_JWT_HMAC_KEY_MANAGER_H_ +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/jwt_hmac_key_manager_test.cc b/cc/jwt/internal/jwt_hmac_key_manager_test.cc index 00be35c..d4285bf 100644 --- a/cc/jwt/internal/jwt_hmac_key_manager_test.cc +++ b/cc/jwt/internal/jwt_hmac_key_manager_test.cc
@@ -16,7 +16,10 @@ #include "tink/jwt/internal/jwt_hmac_key_manager.h" +#include <memory> +#include <sstream> #include <string> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/jwt/internal/jwt_mac_impl.cc b/cc/jwt/internal/jwt_mac_impl.cc index 5b24c1b..aa2123d 100644 --- a/cc/jwt/internal/jwt_mac_impl.cc +++ b/cc/jwt/internal/jwt_mac_impl.cc
@@ -18,6 +18,7 @@ #include <string> #include <utility> +#include <vector> #include "absl/status/status.h" #include "absl/strings/escaping.h"
diff --git a/cc/jwt/internal/jwt_mac_impl.h b/cc/jwt/internal/jwt_mac_impl.h index a5cc64b..f557cd3 100644 --- a/cc/jwt/internal/jwt_mac_impl.h +++ b/cc/jwt/internal/jwt_mac_impl.h
@@ -17,6 +17,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_MAC_IMPL_H_ #define TINK_JWT_INTERNAL_JWT_MAC_IMPL_H_ +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/jwt_mac_impl_test.cc b/cc/jwt/internal/jwt_mac_impl_test.cc index fb9a7df..077e3ed 100644 --- a/cc/jwt/internal/jwt_mac_impl_test.cc +++ b/cc/jwt/internal/jwt_mac_impl_test.cc
@@ -16,8 +16,10 @@ #include "tink/jwt/internal/jwt_mac_impl.h" +#include <memory> #include <string> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/jwt/internal/jwt_mac_internal.h b/cc/jwt/internal/jwt_mac_internal.h index 0b83f02..d7f3ec7 100644 --- a/cc/jwt/internal/jwt_mac_internal.h +++ b/cc/jwt/internal/jwt_mac_internal.h
@@ -65,7 +65,7 @@ absl::string_view compact, const JwtValidator& validator, absl::optional<absl::string_view> kid) const = 0; - virtual ~JwtMacInternal() {} + virtual ~JwtMacInternal() = default; }; } // namespace jwt_internal
diff --git a/cc/jwt/internal/jwt_mac_wrapper.cc b/cc/jwt/internal/jwt_mac_wrapper.cc index d53fb2d..73ee43f 100644 --- a/cc/jwt/internal/jwt_mac_wrapper.cc +++ b/cc/jwt/internal/jwt_mac_wrapper.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/jwt_mac_wrapper.h" +#include <memory> #include <string> #include <utility> @@ -48,7 +49,7 @@ absl::string_view compact, const crypto::tink::JwtValidator& validator) const override; - ~JwtMacSetWrapper() override {} + ~JwtMacSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<JwtMacInternal>> jwt_mac_set_;
diff --git a/cc/jwt/internal/jwt_mac_wrapper.h b/cc/jwt/internal/jwt_mac_wrapper.h index e764c6c..3a93664 100644 --- a/cc/jwt/internal/jwt_mac_wrapper.h +++ b/cc/jwt/internal/jwt_mac_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_JWT_INTERNAL_JWT_MAC_WRAPPER_H_ #define TINK_JWT_INTERNAL_JWT_MAC_WRAPPER_H_ +#include <memory> + #include "tink/jwt/internal/jwt_mac_internal.h" #include "tink/jwt/jwt_mac.h" #include "tink/primitive_set.h"
diff --git a/cc/jwt/internal/jwt_mac_wrapper_test.cc b/cc/jwt/internal/jwt_mac_wrapper_test.cc index f7e66ed..0d6062b 100644 --- a/cc/jwt/internal/jwt_mac_wrapper_test.cc +++ b/cc/jwt/internal/jwt_mac_wrapper_test.cc
@@ -16,8 +16,10 @@ #include "tink/jwt/internal/jwt_mac_wrapper.h" +#include <memory> #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/strings/str_split.h"
diff --git a/cc/jwt/internal/jwt_public_key_sign_impl.h b/cc/jwt/internal/jwt_public_key_sign_impl.h index 7f83103..7f665bd 100644 --- a/cc/jwt/internal/jwt_public_key_sign_impl.h +++ b/cc/jwt/internal/jwt_public_key_sign_impl.h
@@ -17,6 +17,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_PUBLIC_KEY_SIGN_IMPL_H_ #define TINK_JWT_INTERNAL_JWT_PUBLIC_KEY_SIGN_IMPL_H_ +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/jwt_public_key_sign_internal.h b/cc/jwt/internal/jwt_public_key_sign_internal.h index 28a095b..f7522e6 100644 --- a/cc/jwt/internal/jwt_public_key_sign_internal.h +++ b/cc/jwt/internal/jwt_public_key_sign_internal.h
@@ -41,7 +41,7 @@ virtual crypto::tink::util::StatusOr<std::string> SignAndEncodeWithKid( const RawJwt& token, absl::optional<absl::string_view> kid) const = 0; - virtual ~JwtPublicKeySignInternal() {} + virtual ~JwtPublicKeySignInternal() = default; }; } // namespace tink
diff --git a/cc/jwt/internal/jwt_public_key_sign_verify_impl_test.cc b/cc/jwt/internal/jwt_public_key_sign_verify_impl_test.cc index c69907a..14cb895 100644 --- a/cc/jwt/internal/jwt_public_key_sign_verify_impl_test.cc +++ b/cc/jwt/internal/jwt_public_key_sign_verify_impl_test.cc
@@ -14,8 +14,10 @@ // /////////////////////////////////////////////////////////////////////////////// +#include <memory> #include <string> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/jwt/internal/jwt_public_key_sign_wrapper.cc b/cc/jwt/internal/jwt_public_key_sign_wrapper.cc index 316e3ec..5ad41da 100644 --- a/cc/jwt/internal/jwt_public_key_sign_wrapper.cc +++ b/cc/jwt/internal/jwt_public_key_sign_wrapper.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/jwt_public_key_sign_wrapper.h" +#include <memory> #include <string> #include <utility> @@ -44,7 +45,7 @@ crypto::tink::util::StatusOr<std::string> SignAndEncode( const crypto::tink::RawJwt& token) const override; - ~JwtPublicKeySignSetWrapper() override {} + ~JwtPublicKeySignSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<JwtPublicKeySignInternal>> jwt_sign_set_;
diff --git a/cc/jwt/internal/jwt_public_key_sign_wrapper.h b/cc/jwt/internal/jwt_public_key_sign_wrapper.h index 477ccd9..d1e951a 100644 --- a/cc/jwt/internal/jwt_public_key_sign_wrapper.h +++ b/cc/jwt/internal/jwt_public_key_sign_wrapper.h
@@ -16,6 +16,8 @@ #ifndef TINK_JWT_INTERNAL_JWT_PUBLIC_KEY_SIGN_WRAPPER_H_ #define TINK_JWT_INTERNAL_JWT_PUBLIC_KEY_SIGN_WRAPPER_H_ +#include <memory> + #include "tink/jwt/internal/jwt_public_key_sign_internal.h" #include "tink/jwt/jwt_public_key_sign.h" #include "tink/primitive_set.h"
diff --git a/cc/jwt/internal/jwt_public_key_verify_impl.cc b/cc/jwt/internal/jwt_public_key_verify_impl.cc index 37e02b7..8ea3a6a 100644 --- a/cc/jwt/internal/jwt_public_key_verify_impl.cc +++ b/cc/jwt/internal/jwt_public_key_verify_impl.cc
@@ -18,6 +18,7 @@ #include <string> #include <utility> +#include <vector> #include "absl/status/status.h" #include "absl/strings/escaping.h"
diff --git a/cc/jwt/internal/jwt_public_key_verify_impl.h b/cc/jwt/internal/jwt_public_key_verify_impl.h index b6adba1..99c0af3 100644 --- a/cc/jwt/internal/jwt_public_key_verify_impl.h +++ b/cc/jwt/internal/jwt_public_key_verify_impl.h
@@ -17,6 +17,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_PUBLIC_KEY_VERIFY_IMPL_H_ #define TINK_JWT_INTERNAL_JWT_PUBLIC_KEY_VERIFY_IMPL_H_ +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/jwt_public_key_verify_internal.h b/cc/jwt/internal/jwt_public_key_verify_internal.h index 9ec5aa2..392dc3c 100644 --- a/cc/jwt/internal/jwt_public_key_verify_internal.h +++ b/cc/jwt/internal/jwt_public_key_verify_internal.h
@@ -54,7 +54,7 @@ absl::string_view compact, const JwtValidator& validator, absl::optional<absl::string_view> kid) const = 0; - virtual ~JwtPublicKeyVerifyInternal() {} + virtual ~JwtPublicKeyVerifyInternal() = default; }; } // namespace tink
diff --git a/cc/jwt/internal/jwt_public_key_verify_wrapper.cc b/cc/jwt/internal/jwt_public_key_verify_wrapper.cc index 179de6f..6d4159f 100644 --- a/cc/jwt/internal/jwt_public_key_verify_wrapper.cc +++ b/cc/jwt/internal/jwt_public_key_verify_wrapper.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/jwt_public_key_verify_wrapper.h" +#include <memory> #include <string> #include <utility> @@ -45,7 +46,7 @@ absl::string_view compact, const crypto::tink::JwtValidator& validator) const override; - ~JwtPublicKeyVerifySetWrapper() override {} + ~JwtPublicKeyVerifySetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<JwtPublicKeyVerifyInternal>> jwt_verify_set_;
diff --git a/cc/jwt/internal/jwt_public_key_verify_wrapper.h b/cc/jwt/internal/jwt_public_key_verify_wrapper.h index ac0b807..cc59461 100644 --- a/cc/jwt/internal/jwt_public_key_verify_wrapper.h +++ b/cc/jwt/internal/jwt_public_key_verify_wrapper.h
@@ -16,6 +16,8 @@ #ifndef TINK_JWT_INTERNAL_JWT_PUBLIC_KEY_VERIFY_WRAPPER_H_ #define TINK_JWT_INTERNAL_JWT_PUBLIC_KEY_VERIFY_WRAPPER_H_ +#include <memory> + #include "tink/jwt/internal/jwt_public_key_verify_internal.h" #include "tink/jwt/jwt_public_key_verify.h" #include "tink/primitive_set.h"
diff --git a/cc/jwt/internal/jwt_public_key_wrappers_test.cc b/cc/jwt/internal/jwt_public_key_wrappers_test.cc index 285aa6e..ce5c8ca 100644 --- a/cc/jwt/internal/jwt_public_key_wrappers_test.cc +++ b/cc/jwt/internal/jwt_public_key_wrappers_test.cc
@@ -14,8 +14,10 @@ // //////////////////////////////////////////////////////////////////////////////// +#include <memory> #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/strings/str_split.h"
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.cc b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.cc index 8cd220f..31bd8ad 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.cc +++ b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.cc
@@ -15,8 +15,9 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.h" -#include <utility> +#include <memory> #include <string> +#include <utility> #include "tink/jwt/internal/jwt_public_key_sign_impl.h" #include "tink/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.h"
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.h b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.h index 5e799e5..0cd598a 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.h +++ b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_RSA_SSA_PKCS1_SIGN_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_JWT_RSA_SSA_PKCS1_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_verify_key_manager_test.cc b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_verify_key_manager_test.cc index 14f00dc..6741929 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_verify_key_manager_test.cc +++ b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_sign_verify_key_manager_test.cc
@@ -14,8 +14,10 @@ // //////////////////////////////////////////////////////////////////////////////// +#include <memory> #include <string> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.cc b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.cc index 2d75ae4..3c4432c 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.cc +++ b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.cc
@@ -15,6 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.h b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.h index be85648..f977489 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.h +++ b/cc/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_RSA_SSA_PKCS1_VERIFY_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_JWT_RSA_SSA_PKCS1_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pss_sign_key_manager.cc b/cc/jwt/internal/jwt_rsa_ssa_pss_sign_key_manager.cc index 470be79..183ebb6 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pss_sign_key_manager.cc +++ b/cc/jwt/internal/jwt_rsa_ssa_pss_sign_key_manager.cc
@@ -15,6 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/jwt/internal/jwt_rsa_ssa_pss_sign_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pss_sign_key_manager.h b/cc/jwt/internal/jwt_rsa_ssa_pss_sign_key_manager.h index 1ac226b..fd8891f 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pss_sign_key_manager.h +++ b/cc/jwt/internal/jwt_rsa_ssa_pss_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_RSA_SSA_PSS_SIGN_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_JWT_RSA_SSA_PSS_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pss_sign_verify_key_manager_test.cc b/cc/jwt/internal/jwt_rsa_ssa_pss_sign_verify_key_manager_test.cc index 5fe31e6..ddc7c0b 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pss_sign_verify_key_manager_test.cc +++ b/cc/jwt/internal/jwt_rsa_ssa_pss_sign_verify_key_manager_test.cc
@@ -14,8 +14,10 @@ // //////////////////////////////////////////////////////////////////////////////// +#include <memory> #include <string> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pss_verify_key_manager.cc b/cc/jwt/internal/jwt_rsa_ssa_pss_verify_key_manager.cc index c6813b0..4826e99 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pss_verify_key_manager.cc +++ b/cc/jwt/internal/jwt_rsa_ssa_pss_verify_key_manager.cc
@@ -15,6 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/jwt/internal/jwt_rsa_ssa_pss_verify_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/jwt_rsa_ssa_pss_verify_key_manager.h b/cc/jwt/internal/jwt_rsa_ssa_pss_verify_key_manager.h index dd6d5f8..c8ba629 100644 --- a/cc/jwt/internal/jwt_rsa_ssa_pss_verify_key_manager.h +++ b/cc/jwt/internal/jwt_rsa_ssa_pss_verify_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_JWT_RSA_SSA_PSS_VERIFY_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_JWT_RSA_SSA_PSS_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager.cc b/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager.cc index 2d917a0..144bf8b 100644 --- a/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager.cc +++ b/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_ecdsa_sign_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager.h b/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager.h index d90660e..99e8911 100644 --- a/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager.h +++ b/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_RAW_JWT_ECDSA_SIGN_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_RAW_JWT_ECDSA_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager_test.cc b/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager_test.cc index 6147e88..552b6aa 100644 --- a/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager_test.cc +++ b/cc/jwt/internal/raw_jwt_ecdsa_sign_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_ecdsa_sign_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h"
diff --git a/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager.cc b/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager.cc index b9e9675..956790a 100644 --- a/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager.cc +++ b/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_ecdsa_verify_key_manager.h" +#include <memory> #include <utility> #include "absl/status/status.h"
diff --git a/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager.h b/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager.h index ecfafe1..8179d48 100644 --- a/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager.h +++ b/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_RAW_JWT_ECDSA_VERIFY_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_RAW_JWT_ECDSA_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager_test.cc b/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager_test.cc index 8a4bc8a..84b7dec 100644 --- a/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager_test.cc +++ b/cc/jwt/internal/raw_jwt_ecdsa_verify_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_ecdsa_verify_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h"
diff --git a/cc/jwt/internal/raw_jwt_hmac_key_manager.cc b/cc/jwt/internal/raw_jwt_hmac_key_manager.cc index 0169931..72b4335 100644 --- a/cc/jwt/internal/raw_jwt_hmac_key_manager.cc +++ b/cc/jwt/internal/raw_jwt_hmac_key_manager.cc
@@ -46,8 +46,6 @@ namespace { -constexpr int kMinKeySizeInBytes = 32; - StatusOr<int> MinimumKeySize(const JwtHmacAlgorithm& algorithm) { switch (algorithm) { case JwtHmacAlgorithm::HS256:
diff --git a/cc/jwt/internal/raw_jwt_hmac_key_manager.h b/cc/jwt/internal/raw_jwt_hmac_key_manager.h index 62d88a9..7e6b1e3 100644 --- a/cc/jwt/internal/raw_jwt_hmac_key_manager.h +++ b/cc/jwt/internal/raw_jwt_hmac_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_RAW_JWT_HMAC_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_RAW_JWT_HMAC_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/raw_jwt_hmac_key_manager_test.cc b/cc/jwt/internal/raw_jwt_hmac_key_manager_test.cc index 4235952..88428ee 100644 --- a/cc/jwt/internal/raw_jwt_hmac_key_manager_test.cc +++ b/cc/jwt/internal/raw_jwt_hmac_key_manager_test.cc
@@ -16,6 +16,8 @@ #include "tink/jwt/internal/raw_jwt_hmac_key_manager.h" +#include <memory> +#include <sstream> #include <utility> #include "gmock/gmock.h"
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.cc b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.cc index d0839d7..a4450f3 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.cc +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.h" +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.h b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.h index 22b3f83..efe5f32 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.h +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_RAW_JWT_RSA_SSA_PKCS1_SIGN_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_RAW_JWT_RSA_SSA_PKCS1_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager_test.cc b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager_test.cc index e1eaf2d..32395ae 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager_test.cc +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_rsa_ssa_pkcs1_sign_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.cc b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.cc index 10dae49..9e8a415 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.cc +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.h" +#include <memory> #include <utility> #include "absl/status/status.h"
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.h b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.h index 7cbb449..aca8285 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.h +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.h
@@ -18,6 +18,7 @@ #define TINK_JWT_INTERNAL_RAW_JWT_RSA_SSA_PKCS1_VERIFY_KEY_MANAGER_H_ #include <algorithm> +#include <memory> #include <string> #include <vector>
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager_test.cc b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager_test.cc index be19b50..3bd1629 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager_test.cc +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_rsa_ssa_pkcs1_verify_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h"
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.cc b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.cc index 9fd230c..9b4509b 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.cc +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.h b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.h index 12f55e2..bf06792 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.h +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_RAW_JWT_RSA_SSA_PSS_SIGN_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_RAW_JWT_RSA_SSA_PSS_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager_test.cc b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager_test.cc index f6adfcf..56e627b 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager_test.cc +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_rsa_ssa_pss_sign_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.cc b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.cc index baabdf9..d5e83b1 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.cc +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.h" +#include <memory> #include <utility> #include "absl/status/status.h"
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.h b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.h index 5750194..0e9cc8f 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.h +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_JWT_INTERNAL_RAW_JWT_RSA_SSA_PSS_VERIFY_KEY_MANAGER_H_ #define TINK_JWT_INTERNAL_RAW_JWT_RSA_SSA_PSS_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager_test.cc b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager_test.cc index 39ec7ec..6cc9688 100644 --- a/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager_test.cc +++ b/cc/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/internal/raw_jwt_rsa_ssa_pss_verify_key_manager.h" +#include <memory> #include <string> #include "gmock/gmock.h"
diff --git a/cc/jwt/jwk_set_converter.cc b/cc/jwt/jwk_set_converter.cc index 4d8a7af..cf26d38 100644 --- a/cc/jwt/jwk_set_converter.cc +++ b/cc/jwt/jwk_set_converter.cc
@@ -16,6 +16,9 @@ #include "tink/jwt/jwk_set_converter.h" +#include <memory> +#include <ostream> +#include <sstream> #include <string> #include "absl/strings/escaping.h"
diff --git a/cc/jwt/jwk_set_converter.h b/cc/jwt/jwk_set_converter.h index 6595a23..1d3914d 100644 --- a/cc/jwt/jwk_set_converter.h +++ b/cc/jwt/jwk_set_converter.h
@@ -17,6 +17,7 @@ #ifndef TINK_JWT_JWK_SET_CONVERTER_H_ #define TINK_JWT_JWK_SET_CONVERTER_H_ +#include <memory> #include <string> #include "absl/strings/string_view.h"
diff --git a/cc/jwt/jwk_set_converter_test.cc b/cc/jwt/jwk_set_converter_test.cc index 743518c..494cdf5 100644 --- a/cc/jwt/jwk_set_converter_test.cc +++ b/cc/jwt/jwk_set_converter_test.cc
@@ -16,7 +16,9 @@ #include "tink/jwt/jwk_set_converter.h" +#include <memory> #include <string> +#include <tuple> #include <utility> #include "google/protobuf/util/message_differencer.h"
diff --git a/cc/jwt/jwt_key_templates_test.cc b/cc/jwt/jwt_key_templates_test.cc index f52d2ff..d85c450 100644 --- a/cc/jwt/jwt_key_templates_test.cc +++ b/cc/jwt/jwt_key_templates_test.cc
@@ -16,6 +16,7 @@ #include "tink/jwt/jwt_key_templates.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/jwt/jwt_mac.h b/cc/jwt/jwt_mac.h index 3589e00..7b4374b 100644 --- a/cc/jwt/jwt_mac.h +++ b/cc/jwt/jwt_mac.h
@@ -56,7 +56,7 @@ virtual crypto::tink::util::StatusOr<VerifiedJwt> VerifyMacAndDecode( absl::string_view compact, const JwtValidator& validator) const = 0; - virtual ~JwtMac() {} + virtual ~JwtMac() = default; }; } // namespace tink
diff --git a/cc/jwt/jwt_public_key_sign.h b/cc/jwt/jwt_public_key_sign.h index 840182a..a20f1d1 100644 --- a/cc/jwt/jwt_public_key_sign.h +++ b/cc/jwt/jwt_public_key_sign.h
@@ -38,7 +38,7 @@ virtual crypto::tink::util::StatusOr<std::string> SignAndEncode( const RawJwt& token) const = 0; - virtual ~JwtPublicKeySign() {} + virtual ~JwtPublicKeySign() = default; }; } // namespace tink
diff --git a/cc/jwt/jwt_public_key_verify.h b/cc/jwt/jwt_public_key_verify.h index 1e6eb3a..decdc2f 100644 --- a/cc/jwt/jwt_public_key_verify.h +++ b/cc/jwt/jwt_public_key_verify.h
@@ -48,7 +48,7 @@ virtual crypto::tink::util::StatusOr<VerifiedJwt> VerifyAndDecode( absl::string_view compact, const JwtValidator& validator) const = 0; - virtual ~JwtPublicKeyVerify() {} + virtual ~JwtPublicKeyVerify() = default; }; } // namespace tink
diff --git a/cc/jwt/jwt_validator.cc b/cc/jwt/jwt_validator.cc index c56a399..881d9e1 100644 --- a/cc/jwt/jwt_validator.cc +++ b/cc/jwt/jwt_validator.cc
@@ -18,6 +18,7 @@ #include <algorithm> #include <string> +#include <vector> #include "absl/status/status.h"
diff --git a/cc/jwt/raw_jwt.cc b/cc/jwt/raw_jwt.cc index e1fae19..23ba60d 100644 --- a/cc/jwt/raw_jwt.cc +++ b/cc/jwt/raw_jwt.cc
@@ -18,6 +18,7 @@ #include <string> #include <utility> +#include <vector> #include "absl/status/status.h" #include "absl/strings/numbers.h" @@ -185,7 +186,7 @@ return jwt_internal::ProtoStructToJsonString(json_proto_); } -RawJwt::RawJwt() {} +RawJwt::RawJwt() = default; RawJwt::RawJwt(absl::optional<std::string> type_header, google::protobuf::Struct json_proto) {
diff --git a/cc/jwt/raw_jwt.h b/cc/jwt/raw_jwt.h index 36f40de..2a83d23 100644 --- a/cc/jwt/raw_jwt.h +++ b/cc/jwt/raw_jwt.h
@@ -18,6 +18,7 @@ #define TINK_JWT_RAW_JWT_H_ #include <string> +#include <vector> #include "google/protobuf/struct.pb.h" #include "absl/strings/string_view.h"
diff --git a/cc/jwt/raw_jwt_test.cc b/cc/jwt/raw_jwt_test.cc index e7dc361..a191d56 100644 --- a/cc/jwt/raw_jwt_test.cc +++ b/cc/jwt/raw_jwt_test.cc
@@ -17,6 +17,7 @@ #include "tink/jwt/raw_jwt.h" #include <string> +#include <vector> #include "gtest/gtest.h" #include "absl/strings/escaping.h" @@ -473,7 +474,15 @@ RawJwtBuilder().SetIssuer("issuer").WithoutExpiration().Build(); ASSERT_THAT(jwt, IsOk()); - ASSERT_THAT(jwt->GetJsonPayload(), IsOkAndHolds(R"({"iss":"issuer"})")); + EXPECT_THAT(jwt->GetJsonPayload(), IsOkAndHolds(R"({"iss":"issuer"})")); +} + +TEST(RawJwt, IntegerIsEncodedAsInteger) { + util::StatusOr<RawJwt> jwt = + RawJwtBuilder().AddNumberClaim("num", 1).WithoutExpiration().Build(); + ASSERT_THAT(jwt, IsOk()); + + EXPECT_THAT(jwt->GetJsonPayload(), IsOkAndHolds(R"({"num":1})")); } TEST(RawJwt, GetExpirationJsonPayload) {
diff --git a/cc/jwt/verified_jwt.cc b/cc/jwt/verified_jwt.cc index 4374044..c1359f5 100644 --- a/cc/jwt/verified_jwt.cc +++ b/cc/jwt/verified_jwt.cc
@@ -17,6 +17,7 @@ #include "tink/jwt/verified_jwt.h" #include <string> +#include <vector> #include "absl/strings/numbers.h" #include "absl/strings/str_format.h" @@ -26,7 +27,7 @@ namespace crypto { namespace tink { -VerifiedJwt::VerifiedJwt() {} +VerifiedJwt::VerifiedJwt() = default; VerifiedJwt::VerifiedJwt(const RawJwt& raw_jwt) { raw_jwt_ = raw_jwt;
diff --git a/cc/jwt/verified_jwt.h b/cc/jwt/verified_jwt.h index faa5ba3..e4d4c85 100644 --- a/cc/jwt/verified_jwt.h +++ b/cc/jwt/verified_jwt.h
@@ -18,6 +18,7 @@ #define TINK_JWT_VERIFIED_JWT_H_ #include <string> +#include <vector> #include "google/protobuf/struct.pb.h" #include "absl/strings/string_view.h"
diff --git a/cc/jwt/verified_jwt_test.cc b/cc/jwt/verified_jwt_test.cc index ff1ba54..ed0c7e3 100644 --- a/cc/jwt/verified_jwt_test.cc +++ b/cc/jwt/verified_jwt_test.cc
@@ -16,8 +16,10 @@ #include "tink/jwt/verified_jwt.h" +#include <memory> #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/status/status.h" @@ -326,7 +328,7 @@ VerifiedJwt jwt2 = std::move(jwt1); // We want that a VerifiedJwt object remains a valid object, even after // std::moved has been called. - EXPECT_TRUE(jwt1.HasIssuer()); + EXPECT_TRUE(jwt1.HasIssuer()); // NOLINT(bugprone-use-after-move) EXPECT_THAT(jwt1.GetIssuer(), IsOkAndHolds("issuer")); EXPECT_TRUE(jwt2.HasIssuer()); EXPECT_THAT(jwt2.GetIssuer(), IsOkAndHolds("issuer"));
diff --git a/cc/key_access.h b/cc/key_access.h index 51b7e32..77465a6 100644 --- a/cc/key_access.h +++ b/cc/key_access.h
@@ -14,8 +14,8 @@ // //////////////////////////////////////////////////////////////////////////////// -#ifndef THIRD_PARTY_TINK_KEY_ACCESS_H_ -#define THIRD_PARTY_TINK_KEY_ACCESS_H_ +#ifndef TINK_KEY_ACCESS_H_ +#define TINK_KEY_ACCESS_H_ namespace crypto { namespace tink { @@ -27,7 +27,7 @@ return token; } - const bool CanAccessSecret() { return can_access_secret_; } + bool CanAccessSecret() { return can_access_secret_; } // KeyAccess objects are copiable and movable. KeyAccess(const KeyAccess&) = default; @@ -46,4 +46,4 @@ } // namespace tink } // namespace crypto -#endif // THIRD_PARTY_TINK_KEY_ACCESS_H_ +#endif // TINK_KEY_ACCESS_H_
diff --git a/cc/key_gen_configuration.h b/cc/key_gen_configuration.h new file mode 100644 index 0000000..f8e3e09 --- /dev/null +++ b/cc/key_gen_configuration.h
@@ -0,0 +1,52 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_KEY_GEN_CONFIGURATION_H_ +#define TINK_KEY_GEN_CONFIGURATION_H_ + +#include "tink/internal/key_type_info_store.h" + +namespace crypto { +namespace tink { + +namespace internal { +class KeyGenConfigurationImpl; +} + +// KeyGenConfiguration used to generate keys using stored key type managers. +class KeyGenConfiguration { + public: + KeyGenConfiguration() = default; + + // Not copyable or movable. + KeyGenConfiguration(const KeyGenConfiguration&) = delete; + KeyGenConfiguration& operator=(const KeyGenConfiguration&) = delete; + + private: + friend class internal::KeyGenConfigurationImpl; + + // When true, KeyGenConfiguration is in global registry mode. For + // `some_fn(config)` with a `config` parameter, this indicates to `some_fn` to + // use crypto::tink::Registry directly. + bool global_registry_mode_ = false; + + crypto::tink::internal::KeyTypeInfoStore key_type_info_store_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_KEY_GEN_CONFIGURATION_H_
diff --git a/cc/key_manager.h b/cc/key_manager.h index 871acee..58d2744 100644 --- a/cc/key_manager.h +++ b/cc/key_manager.h
@@ -17,6 +17,7 @@ #ifndef TINK_KEY_MANAGER_H_ #define TINK_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h" @@ -56,7 +57,7 @@ std::unique_ptr<google::crypto::tink::KeyData>> NewKeyData(absl::string_view serialized_key_format) const = 0; - virtual ~KeyFactory() {} + virtual ~KeyFactory() = default; }; class PrivateKeyFactory : public virtual KeyFactory { @@ -66,7 +67,7 @@ std::unique_ptr<google::crypto::tink::KeyData>> GetPublicKeyData(absl::string_view serialized_private_key) const = 0; - virtual ~PrivateKeyFactory() {} + ~PrivateKeyFactory() override = default; }; /** @@ -94,7 +95,7 @@ return (key_type == get_key_type()); } - virtual ~KeyManagerBase() {} + virtual ~KeyManagerBase() = default; }; template <class P>
diff --git a/cc/key_status.h b/cc/key_status.h new file mode 100644 index 0000000..8d37cb5 --- /dev/null +++ b/cc/key_status.h
@@ -0,0 +1,36 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_KEY_STATUS_H_ +#define TINK_KEY_STATUS_H_ + +namespace crypto { +namespace tink { + +// Enum representation of KeyStatusType in tink/proto/tink.proto. Using an +// enum class prevents unintentional implicit conversions. +enum class KeyStatus : int { + kEnabled = 1, // Can be used for cryptographic operations. + kDisabled = 2, // Cannot be used (but can become kEnabled again). + kDestroyed = 3, // Key data does not exist in this Keyset any more. + // Added to guard from failures that may be caused by future expansions. + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements = 20, +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_KEY_STATUS_H_
diff --git a/cc/keyderivation/BUILD.bazel b/cc/keyderivation/BUILD.bazel new file mode 100644 index 0000000..449698a --- /dev/null +++ b/cc/keyderivation/BUILD.bazel
@@ -0,0 +1,115 @@ +package( + default_visibility = ["//:__subpackages__"], +) + +licenses(["notice"]) + +cc_library( + name = "key_derivation_config", + srcs = ["key_derivation_config.cc"], + hdrs = ["key_derivation_config.h"], + include_prefix = "tink/keyderivation", + visibility = ["//visibility:public"], + deps = [ + ":keyset_deriver_wrapper", + "//config:tink_fips", + "//keyderivation/internal:prf_based_deriver_key_manager", + "//prf:hkdf_prf_key_manager", + "//util:status", + ], +) + +cc_test( + name = "key_derivation_config_test", + srcs = ["key_derivation_config_test.cc"], + deps = [ + ":key_derivation_config", + ":key_derivation_key_templates", + ":keyset_deriver", + "//:registry", + "//aead:aead_config", + "//aead:aead_key_templates", + "//aead:aes_gcm_key_manager", + "//prf:prf_key_templates", + "//util:statusor", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "key_derivation_key_templates", + srcs = ["key_derivation_key_templates.cc"], + hdrs = ["key_derivation_key_templates.h"], + include_prefix = "tink/keyderivation", + visibility = ["//visibility:public"], + deps = [ + "//keyderivation/internal:prf_based_deriver_key_manager", + "//proto:tink_cc_proto", + "//subtle:random", + "//util:statusor", + ], +) + +cc_test( + name = "key_derivation_key_templates_test", + srcs = ["key_derivation_key_templates_test.cc"], + deps = [ + ":key_derivation_key_templates", + ":keyset_deriver_wrapper", + "//:registry", + "//aead:aead_key_templates", + "//aead:aes_gcm_key_manager", + "//keyderivation/internal:prf_based_deriver_key_manager", + "//prf:hkdf_prf_key_manager", + "//prf:prf_key_templates", + "//proto:prf_based_deriver_cc_proto", + "//proto:tink_cc_proto", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "keyset_deriver", + hdrs = ["keyset_deriver.h"], + include_prefix = "tink/keyderivation", + visibility = ["//visibility:public"], + deps = [ + "//:keyset_handle", + "//util:statusor", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "keyset_deriver_wrapper", + srcs = ["keyset_deriver_wrapper.cc"], + hdrs = ["keyset_deriver_wrapper.h"], + include_prefix = "tink/keyderivation", + deps = [ + ":keyset_deriver", + "//:cleartext_keyset_handle", + "//:primitive_set", + "//:primitive_wrapper", + "//proto:tink_cc_proto", + "@com_google_absl//absl/status", + ], +) + +cc_test( + name = "keyset_deriver_wrapper_test", + srcs = ["keyset_deriver_wrapper_test.cc"], + deps = [ + ":keyset_deriver", + ":keyset_deriver_wrapper", + "//:cleartext_keyset_handle", + "//:primitive_set", + "//proto:tink_cc_proto", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/cc/keyderivation/CMakeLists.txt b/cc/keyderivation/CMakeLists.txt new file mode 100644 index 0000000..fa3d7e8 --- /dev/null +++ b/cc/keyderivation/CMakeLists.txt
@@ -0,0 +1,109 @@ +tink_module(keyderivation) + +add_subdirectory(internal) + +tink_cc_library( + NAME key_derivation_config + SRCS + key_derivation_config.cc + key_derivation_config.h + DEPS + tink::keyderivation::keyset_deriver_wrapper + tink::config::tink_fips + tink::keyderivation::internal::prf_based_deriver_key_manager + tink::prf::hkdf_prf_key_manager + tink::util::status + PUBLIC +) + +tink_cc_test( + NAME key_derivation_config_test + SRCS + key_derivation_config_test.cc + DEPS + tink::keyderivation::key_derivation_config + tink::keyderivation::key_derivation_key_templates + tink::keyderivation::keyset_deriver + gmock + tink::core::registry + tink::aead::aead_config + tink::aead::aead_key_templates + tink::aead::aes_gcm_key_manager + tink::prf::prf_key_templates + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_library( + NAME key_derivation_key_templates + SRCS + key_derivation_key_templates.cc + key_derivation_key_templates.h + DEPS + tink::keyderivation::internal::prf_based_deriver_key_manager + tink::subtle::random + tink::util::statusor + tink::proto::tink_cc_proto + PUBLIC +) + +tink_cc_test( + NAME key_derivation_key_templates_test + SRCS + key_derivation_key_templates_test.cc + DEPS + tink::keyderivation::key_derivation_key_templates + tink::keyderivation::keyset_deriver_wrapper + gmock + absl::status + tink::core::registry + tink::aead::aead_key_templates + tink::aead::aes_gcm_key_manager + tink::keyderivation::internal::prf_based_deriver_key_manager + tink::prf::hkdf_prf_key_manager + tink::prf::prf_key_templates + tink::util::statusor + tink::util::test_matchers + tink::proto::prf_based_deriver_cc_proto + tink::proto::tink_cc_proto +) + +tink_cc_library( + NAME keyset_deriver + SRCS + keyset_deriver.h + DEPS + absl::strings + tink::core::keyset_handle + tink::util::statusor + PUBLIC +) + +tink_cc_library( + NAME keyset_deriver_wrapper + SRCS + keyset_deriver_wrapper.cc + keyset_deriver_wrapper.h + DEPS + tink::keyderivation::keyset_deriver + absl::status + tink::core::cleartext_keyset_handle + tink::core::primitive_set + tink::core::primitive_wrapper + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME keyset_deriver_wrapper_test + SRCS + keyset_deriver_wrapper_test.cc + DEPS + tink::keyderivation::keyset_deriver + tink::keyderivation::keyset_deriver_wrapper + gmock + absl::status + tink::core::cleartext_keyset_handle + tink::core::primitive_set + tink::util::test_matchers + tink::proto::tink_cc_proto +)
diff --git a/cc/keyderivation/internal/BUILD.bazel b/cc/keyderivation/internal/BUILD.bazel new file mode 100644 index 0000000..09aa90b --- /dev/null +++ b/cc/keyderivation/internal/BUILD.bazel
@@ -0,0 +1,73 @@ +package(default_visibility = ["//:__subpackages__"]) + +licenses(["notice"]) + +cc_library( + name = "prf_based_deriver", + srcs = ["prf_based_deriver.cc"], + hdrs = ["prf_based_deriver.h"], + include_prefix = "tink/keyderivation/internal", + deps = [ + "//:cleartext_keyset_handle", + "//:keyset_handle", + "//:registry", + "//keyderivation:keyset_deriver", + "//proto:tink_cc_proto", + "//subtle/prf:streaming_prf", + ], +) + +cc_test( + name = "prf_based_deriver_test", + srcs = ["prf_based_deriver_test.cc"], + deps = [ + ":prf_based_deriver", + "//:cleartext_keyset_handle", + "//aead:aead_key_templates", + "//aead:aes_gcm_key_manager", + "//prf:hkdf_prf_key_manager", + "//proto:aes_gcm_cc_proto", + "//util:test_matchers", + "//util:test_util", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "prf_based_deriver_key_manager", + hdrs = ["prf_based_deriver_key_manager.h"], + include_prefix = "tink/keyderivation/internal", + deps = [ + ":prf_based_deriver", + "//keyderivation:keyset_deriver", + "//proto:prf_based_deriver_cc_proto", + "//proto:tink_cc_proto", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "prf_based_deriver_key_manager_test", + srcs = ["prf_based_deriver_key_manager_test.cc"], + deps = [ + ":prf_based_deriver_key_manager", + "//:cleartext_keyset_handle", + "//aead:aead_key_templates", + "//aead:aes_gcm_key_manager", + "//keyderivation:keyset_deriver", + "//prf:hkdf_prf_key_manager", + "//proto:aes_gcm_cc_proto", + "//proto:hkdf_prf_cc_proto", + "//proto:prf_based_deriver_cc_proto", + "//proto:tink_cc_proto", + "//subtle", + "//util:statusor", + "//util:test_matchers", + "//util:test_util", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/cc/keyderivation/internal/CMakeLists.txt b/cc/keyderivation/internal/CMakeLists.txt new file mode 100644 index 0000000..2fd4c7e --- /dev/null +++ b/cc/keyderivation/internal/CMakeLists.txt
@@ -0,0 +1,69 @@ +tink_module(keyderivation::internal) + +tink_cc_library( + NAME prf_based_deriver + SRCS + prf_based_deriver.cc + prf_based_deriver.h + DEPS + tink::core::cleartext_keyset_handle + tink::core::keyset_handle + tink::core::registry + tink::keyderivation::keyset_deriver + tink::subtle::prf::streaming_prf + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME prf_based_deriver_test + SRCS + prf_based_deriver_test.cc + DEPS + tink::keyderivation::internal::prf_based_deriver + gmock + absl::status + tink::core::cleartext_keyset_handle + tink::aead::aead_key_templates + tink::aead::aes_gcm_key_manager + tink::prf::hkdf_prf_key_manager + tink::util::test_matchers + tink::util::test_util + tink::proto::aes_gcm_cc_proto +) + +tink_cc_library( + NAME prf_based_deriver_key_manager + SRCS + prf_based_deriver_key_manager.h + DEPS + tink::keyderivation::internal::prf_based_deriver + absl::memory + absl::status + absl::strings + tink::keyderivation::keyset_deriver + tink::proto::prf_based_deriver_cc_proto + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME prf_based_deriver_key_manager_test + SRCS + prf_based_deriver_key_manager_test.cc + DEPS + tink::keyderivation::internal::prf_based_deriver_key_manager + gmock + absl::status + tink::core::cleartext_keyset_handle + tink::aead::aead_key_templates + tink::aead::aes_gcm_key_manager + tink::keyderivation::keyset_deriver + tink::prf::hkdf_prf_key_manager + tink::subtle::subtle + tink::util::statusor + tink::util::test_matchers + tink::util::test_util + tink::proto::aes_gcm_cc_proto + tink::proto::hkdf_prf_cc_proto + tink::proto::prf_based_deriver_cc_proto + tink::proto::tink_cc_proto +)
diff --git a/cc/keyderivation/internal/prf_based_deriver.cc b/cc/keyderivation/internal/prf_based_deriver.cc new file mode 100644 index 0000000..3aa7c72 --- /dev/null +++ b/cc/keyderivation/internal/prf_based_deriver.cc
@@ -0,0 +1,90 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyderivation/internal/prf_based_deriver.h" + +#include <memory> +#include <utility> + +#include "tink/cleartext_keyset_handle.h" +#include "tink/keyset_handle.h" +#include "tink/registry.h" +#include "tink/subtle/prf/streaming_prf.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; + +util::StatusOr<std::unique_ptr<KeysetDeriver>> PrfBasedDeriver::New( + const KeyData& prf_key, const KeyTemplate& key_template) { + // Validate `prf_key`. + util::StatusOr<std::unique_ptr<StreamingPrf>> streaming_prf = + Registry::GetPrimitive<StreamingPrf>(prf_key); + if (!streaming_prf.ok()) { + return streaming_prf.status(); + } + + // Validate `key_template`. + std::unique_ptr<InputStream> randomness = (*streaming_prf)->ComputePrf("s"); + util::StatusOr<KeyData> key_data = + internal::RegistryImpl::GlobalInstance().DeriveKey(key_template, + randomness.get()); + if (!key_data.ok()) { + return key_data.status(); + } + + return {absl::WrapUnique<PrfBasedDeriver>( + new PrfBasedDeriver(*std::move(streaming_prf), key_template))}; +} + +util::StatusOr<std::unique_ptr<KeysetHandle>> PrfBasedDeriver::DeriveKeyset( + absl::string_view salt) const { + std::unique_ptr<InputStream> randomness = streaming_prf_->ComputePrf(salt); + + util::StatusOr<KeyData> key_data = + crypto::tink::internal::RegistryImpl::GlobalInstance().DeriveKey( + key_template_, randomness.get()); + if (!key_data.ok()) { + return key_data.status(); + } + + // Fill in placeholder values for key ID, status, and output prefix type. + // These will be populated with the correct values in the keyset deriver + // factory. This is acceptable because the keyset as-is will never leave Tink, + // and the user only interacts via the keyset deriver factory. + Keyset::Key key; + *key.mutable_key_data() = *key_data; + key.set_status(KeyStatusType::UNKNOWN_STATUS); + key.set_key_id(0); + key.set_output_prefix_type(OutputPrefixType::UNKNOWN_PREFIX); + + Keyset keyset; + *keyset.add_key() = key; + keyset.set_primary_key_id(0); + + return CleartextKeysetHandle::GetKeysetHandle(keyset); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/keyderivation/internal/prf_based_deriver.h b/cc/keyderivation/internal/prf_based_deriver.h new file mode 100644 index 0000000..0b07d3f --- /dev/null +++ b/cc/keyderivation/internal/prf_based_deriver.h
@@ -0,0 +1,55 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_KEYDERIVATION_INTERNAL_PRF_BASED_DERIVER_H_ +#define TINK_KEYDERIVATION_INTERNAL_PRF_BASED_DERIVER_H_ + +#include <memory> +#include <utility> + +#include "tink/keyderivation/keyset_deriver.h" +#include "tink/keyset_handle.h" +#include "tink/subtle/prf/streaming_prf.h" + +namespace crypto { +namespace tink { +namespace internal { + +// The PrfBasedDeriver first uses a PRF to get some randomness, then gives this +// to the Tink registry to derive a key. +class PrfBasedDeriver : public KeysetDeriver { + public: + static crypto::tink::util::StatusOr<std::unique_ptr<KeysetDeriver>> New( + const ::google::crypto::tink::KeyData& prf_key, + const ::google::crypto::tink::KeyTemplate& key_template); + + crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> DeriveKeyset( + absl::string_view salt) const override; + + private: + PrfBasedDeriver(std::unique_ptr<StreamingPrf> streaming_prf, + const ::google::crypto::tink::KeyTemplate& key_template) + : streaming_prf_(std::move(streaming_prf)), key_template_(key_template) {} + + const ::std::unique_ptr<StreamingPrf> streaming_prf_; + const ::google::crypto::tink::KeyTemplate key_template_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_KEYDERIVATION_INTERNAL_PRF_BASED_DERIVER_H_
diff --git a/cc/keyderivation/internal/prf_based_deriver_key_manager.h b/cc/keyderivation/internal/prf_based_deriver_key_manager.h new file mode 100644 index 0000000..f5725d3 --- /dev/null +++ b/cc/keyderivation/internal/prf_based_deriver_key_manager.h
@@ -0,0 +1,132 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_KEYDERIVATION_INTERNAL_PRF_BASED_DERIVER_KEY_MANAGER_H_ +#define TINK_KEYDERIVATION_INTERNAL_PRF_BASED_DERIVER_KEY_MANAGER_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "tink/keyderivation/internal/prf_based_deriver.h" +#include "tink/keyderivation/keyset_deriver.h" +#include "proto/prf_based_deriver.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { + +class PrfBasedDeriverKeyManager + : public KeyTypeManager<google::crypto::tink::PrfBasedDeriverKey, + google::crypto::tink::PrfBasedDeriverKeyFormat, + List<KeysetDeriver>> { + public: + class KeysetDeriverFactory : public PrimitiveFactory<KeysetDeriver> { + crypto::tink::util::StatusOr<std::unique_ptr<KeysetDeriver>> Create( + const google::crypto::tink::PrfBasedDeriverKey& key) const override { + return internal::PrfBasedDeriver::New( + key.prf_key(), key.params().derived_key_template()); + } + }; + + PrfBasedDeriverKeyManager() + : KeyTypeManager(absl::make_unique< + PrfBasedDeriverKeyManager::KeysetDeriverFactory>()) {} + + // Returns the version of this key manager. + uint32_t get_version() const override { return 0; } + + google::crypto::tink::KeyData::KeyMaterialType key_material_type() + const override { + return google::crypto::tink::KeyData::SYMMETRIC; + } + + const std::string& get_key_type() const override { return key_type_; } + + crypto::tink::util::Status ValidateKey( + const google::crypto::tink::PrfBasedDeriverKey& key) const override { + crypto::tink::util::Status status = + ValidateVersion(key.version(), get_version()); + if (!status.ok()) return status; + if (!key.has_prf_key()) { + return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, + "key.prf_key() must be set"); + } + if (!key.params().has_derived_key_template()) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + "key.params().derived_key_template() must be set"); + } + return util::OkStatus(); + } + + crypto::tink::util::Status ValidateKeyFormat( + const google::crypto::tink::PrfBasedDeriverKeyFormat& key_format) + const override { + if (!key_format.has_prf_key_template()) { + return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, + "key.prf_key_template() must be set"); + } + if (!key_format.params().has_derived_key_template()) { + return crypto::tink::util::Status( + absl::StatusCode::kInvalidArgument, + "key_format.params().derived_key_template() must be set"); + } + return util::OkStatus(); + } + + crypto::tink::util::StatusOr<google::crypto::tink::PrfBasedDeriverKey> + CreateKey(const google::crypto::tink::PrfBasedDeriverKeyFormat& key_format) + const override { + crypto::tink::util::StatusOr<std::unique_ptr<google::crypto::tink::KeyData>> + prf_key = CreateKeyData(key_format.prf_key_template()); + if (!prf_key.ok()) return prf_key.status(); + + // Java and Go implementations perform additional verification by getting a + // StreamingPrf primitive from the registry and trying to derive + // `key_format.params().derived_key_template()` with a fake salt. This is + // currently not possible in C++. + + google::crypto::tink::PrfBasedDeriverKey key; + key.set_version(get_version()); + *key.mutable_params()->mutable_derived_key_template() = + key_format.params().derived_key_template(); + *key.mutable_prf_key() = **std::move(prf_key); + return key; + } + + protected: + virtual crypto::tink::util::StatusOr< + std::unique_ptr<google::crypto::tink::KeyData>> + CreateKeyData(const google::crypto::tink::KeyTemplate& key_template) const { + return Registry::NewKeyData(key_template); + } + + private: + const std::string key_type_ = + absl::StrCat(kTypeGoogleapisCom, + google::crypto::tink::PrfBasedDeriverKey().GetTypeName()); +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_KEYDERIVATION_INTERNAL_PRF_BASED_DERIVER_KEY_MANAGER_H_
diff --git a/cc/keyderivation/internal/prf_based_deriver_key_manager_test.cc b/cc/keyderivation/internal/prf_based_deriver_key_manager_test.cc new file mode 100644 index 0000000..62bb1f1 --- /dev/null +++ b/cc/keyderivation/internal/prf_based_deriver_key_manager_test.cc
@@ -0,0 +1,287 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyderivation/internal/prf_based_deriver_key_manager.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/aead/aead_key_templates.h" +#include "tink/aead/aes_gcm_key_manager.h" +#include "tink/cleartext_keyset_handle.h" +#include "tink/keyderivation/keyset_deriver.h" +#include "tink/prf/hkdf_prf_key_manager.h" +#include "tink/subtle/random.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "tink/util/test_util.h" +#include "proto/aes_gcm.pb.h" +#include "proto/hkdf_prf.pb.h" +#include "proto/prf_based_deriver.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::crypto::tink::util::StatusOr; +using ::google::crypto::tink::AesGcmKey; +using ::google::crypto::tink::HashType; +using ::google::crypto::tink::HkdfPrfKey; +using ::google::crypto::tink::HkdfPrfKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::PrfBasedDeriverKey; +using ::google::crypto::tink::PrfBasedDeriverKeyFormat; +using ::testing::Eq; +using ::testing::SizeIs; + +TEST(PrfBasedDeriverKeyManagerTest, Basics) { + EXPECT_THAT(PrfBasedDeriverKeyManager().get_version(), Eq(0)); + EXPECT_THAT(PrfBasedDeriverKeyManager().get_key_type(), + Eq("type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey")); + EXPECT_THAT(PrfBasedDeriverKeyManager().key_material_type(), + Eq(KeyData::SYMMETRIC)); +} + +TEST(PrfBasedDeriverKeyManagerTest, ValidateKeyEmpty) { + EXPECT_THAT(PrfBasedDeriverKeyManager().ValidateKey(PrfBasedDeriverKey()), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(PrfBasedDeriverKeyManagerTest, ValidateKey) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.set_key_value("0123456789abcdef"); + prf_key.mutable_params()->set_hash(HashType::SHA256); + + PrfBasedDeriverKey key; + key.set_version(0); + *key.mutable_prf_key() = test::AsKeyData(prf_key, KeyData::SYMMETRIC); + *key.mutable_params()->mutable_derived_key_template() = + AeadKeyTemplates::Aes256Gcm(); + + EXPECT_THAT(PrfBasedDeriverKeyManager().ValidateKey(key), IsOk()); +} + +TEST(PrfBasedDeriverKeyManagerTest, ValidateKeyWithWrongVersion) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.set_key_value("0123456789abcdef"); + prf_key.mutable_params()->set_hash(HashType::SHA256); + + PrfBasedDeriverKey key; + key.set_version(1); + *key.mutable_prf_key() = test::AsKeyData(prf_key, KeyData::SYMMETRIC); + *key.mutable_params()->mutable_derived_key_template() = + AeadKeyTemplates::Aes256Gcm(); + + EXPECT_THAT(PrfBasedDeriverKeyManager().ValidateKey(key), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(PrfBasedDeriverKeyManagerTest, ValidateKeyFormat) { + HkdfPrfKeyFormat prf_key_format; + prf_key_format.set_key_size(16); + prf_key_format.mutable_params()->set_hash(HashType::SHA256); + + PrfBasedDeriverKeyFormat key_format; + key_format.mutable_prf_key_template()->set_type_url( + HkdfPrfKeyManager().get_key_type()); + key_format.mutable_prf_key_template()->set_value( + prf_key_format.SerializeAsString()); + *key_format.mutable_params()->mutable_derived_key_template() = + AeadKeyTemplates::Aes256Gcm(); + + EXPECT_THAT(PrfBasedDeriverKeyManager().ValidateKeyFormat(key_format), + IsOk()); +} + +TEST(PrfBasedDeriverKeyManagerTest, ValidateKeyFormatEmpty) { + EXPECT_THAT( + PrfBasedDeriverKeyManager().ValidateKeyFormat(PrfBasedDeriverKeyFormat()), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(PrfBasedDeriverKeyManagerTest, CreateKey) { + Registry::Reset(); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<HkdfPrfKeyManager>(), true), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<HkdfPrfKeyManager>(), true), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), true), + IsOk()); + + HkdfPrfKeyFormat prf_key_format; + prf_key_format.set_key_size(32); + prf_key_format.mutable_params()->set_hash(HashType::SHA256); + + PrfBasedDeriverKeyFormat key_format; + key_format.mutable_prf_key_template()->set_type_url( + HkdfPrfKeyManager().get_key_type()); + key_format.mutable_prf_key_template()->set_value( + prf_key_format.SerializeAsString()); + *key_format.mutable_params()->mutable_derived_key_template() = + AeadKeyTemplates::Aes256Gcm(); + + util::StatusOr<PrfBasedDeriverKey> key = + PrfBasedDeriverKeyManager().CreateKey(key_format); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key).version(), Eq(0)); + EXPECT_THAT((*key).prf_key().type_url(), + Eq(HkdfPrfKeyManager().get_key_type())); + EXPECT_THAT((*key).prf_key().key_material_type(), Eq(KeyData::SYMMETRIC)); + + HkdfPrfKey prf_key; + ASSERT_TRUE(prf_key.ParseFromString((*key).prf_key().value())); + EXPECT_THAT(prf_key.key_value().size(), Eq(32)); + + EXPECT_THAT((*key).params().derived_key_template().type_url(), + Eq(key_format.params().derived_key_template().type_url())); + EXPECT_THAT((*key).params().derived_key_template().value(), + Eq(key_format.params().derived_key_template().value())); +} + +TEST(PrfBasedDeriverKeyManagerTest, CreateKeyWithInvalidPrfKey) { + Registry::Reset(); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<HkdfPrfKeyManager>(), true), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<HkdfPrfKeyManager>(), true), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), true), + IsOk()); + + HkdfPrfKeyFormat prf_key_format; + prf_key_format.set_key_size(32); + prf_key_format.mutable_params()->set_hash(HashType::UNKNOWN_HASH); + + PrfBasedDeriverKeyFormat key_format; + key_format.mutable_prf_key_template()->set_type_url( + HkdfPrfKeyManager().get_key_type()); + key_format.mutable_prf_key_template()->set_value( + prf_key_format.SerializeAsString()); + *key_format.mutable_params()->mutable_derived_key_template() = + AeadKeyTemplates::Aes256Gcm(); + + EXPECT_THAT(PrfBasedDeriverKeyManager().CreateKey(key_format).status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(PrfBasedDeriverKeyManagerTest, CreateKeyWithInvalidDerivedKeyTemplate) { + Registry::Reset(); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<HkdfPrfKeyManager>(), true), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<HkdfPrfKeyManager>(), true), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), true), + IsOk()); + + HkdfPrfKeyFormat prf_key_format; + prf_key_format.set_key_size(32); + prf_key_format.mutable_params()->set_hash(HashType::SHA256); + KeyTemplate derived_template; + derived_template.set_type_url("nonexistent.type.url"); + + PrfBasedDeriverKeyFormat key_format; + key_format.mutable_prf_key_template()->set_type_url( + HkdfPrfKeyManager().get_key_type()); + key_format.mutable_prf_key_template()->set_value( + prf_key_format.SerializeAsString()); + *key_format.mutable_params()->mutable_derived_key_template() = + derived_template; + + // See comment in PrfBasedDeriverKeyManager::CreateKey(). + EXPECT_THAT(PrfBasedDeriverKeyManager().CreateKey(key_format).status(), + IsOk()); +} + +TEST(PrfBasedDeriverKeyManagerTest, GetPrimitive) { + Registry::Reset(); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<PrfBasedDeriverKeyManager>(), true), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<HkdfPrfKeyManager>(), true), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), true), + IsOk()); + + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA256); + prf_key.mutable_params()->set_salt(subtle::Random::GetRandomBytes(15)); + prf_key.set_key_value(subtle::Random::GetRandomBytes(33)); + prf_key.mutable_params()->set_hash(HashType::SHA256); + PrfBasedDeriverKey key; + key.set_version(0); + *key.mutable_prf_key() = test::AsKeyData(prf_key, KeyData::SYMMETRIC); + *key.mutable_params()->mutable_derived_key_template() = + AeadKeyTemplates::Aes256Gcm(); + + StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriverKeyManager().GetPrimitive<KeysetDeriver>(key); + ASSERT_THAT(deriver, IsOk()); + + std::string salt = subtle::Random::GetRandomBytes(23); + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset(salt); + ASSERT_THAT(handle, IsOk()); + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + + StatusOr<std::unique_ptr<KeysetDeriver>> direct_deriver = + internal::PrfBasedDeriver::New(key.prf_key(), + key.params().derived_key_template()); + ASSERT_THAT(direct_deriver, IsOk()); + util::StatusOr<std::unique_ptr<KeysetHandle>> direct_handle = + (*direct_deriver)->DeriveKeyset(salt); + ASSERT_THAT(direct_handle, IsOk()); + Keyset direct_keyset = CleartextKeysetHandle::GetKeyset(**direct_handle); + + ASSERT_THAT(keyset.key(), SizeIs(1)); + ASSERT_THAT(direct_keyset.key(), SizeIs(1)); + + ASSERT_THAT(keyset.key(0).key_data().type_url(), + Eq(keyset.key(0).key_data().type_url())); + + AesGcmKey derived_key; + ASSERT_TRUE(derived_key.ParseFromString(keyset.key(0).key_data().value())); + AesGcmKey direct_derived_key; + ASSERT_TRUE(direct_derived_key.ParseFromString( + direct_keyset.key(0).key_data().value())); + EXPECT_THAT(derived_key.key_value(), Eq(direct_derived_key.key_value())); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/keyderivation/internal/prf_based_deriver_test.cc b/cc/keyderivation/internal/prf_based_deriver_test.cc new file mode 100644 index 0000000..c46e572 --- /dev/null +++ b/cc/keyderivation/internal/prf_based_deriver_test.cc
@@ -0,0 +1,342 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyderivation/internal/prf_based_deriver.h" + +#include <memory> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/aead/aead_key_templates.h" +#include "tink/aead/aes_gcm_key_manager.h" +#include "tink/cleartext_keyset_handle.h" +#include "tink/prf/hkdf_prf_key_manager.h" +#include "tink/util/test_matchers.h" +#include "tink/util/test_util.h" +#include "proto/aes_gcm.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesGcmKey; +using ::google::crypto::tink::HashType; +using ::google::crypto::tink::HkdfPrfKey; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::Ne; +using ::testing::SizeIs; + +class PrfBasedDeriverTest : public ::testing::Test { + public: + static void SetUpTestSuite() { + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), true), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<HkdfPrfKeyManager>(), true), + IsOk()); + } +}; + +TEST_F(PrfBasedDeriverTest, New) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA256); + prf_key.mutable_params()->set_salt(""); + prf_key.set_key_value("0123456789abcdef0123456789abcdef"); + + EXPECT_THAT(PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()), + IsOk()); +} + +TEST_F(PrfBasedDeriverTest, NewWithInvalidPrfKey) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::UNKNOWN_HASH); + prf_key.mutable_params()->set_salt(""); + prf_key.set_key_value("0123456789abcdef0123456789abcdef"); + + EXPECT_THAT(PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(PrfBasedDeriverTest, NewWithInvalidDerivedKeyTemplate) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA256); + prf_key.mutable_params()->set_salt(""); + prf_key.set_key_value("0123456789abcdef0123456789abcdef"); + + KeyTemplate derived_template; + derived_template.set_type_url("some non-existent type url"); + EXPECT_THAT(PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + derived_template) + .status(), + StatusIs(absl::StatusCode::kNotFound)); +} + +// Test vector from https://tools.ietf.org/html/rfc5869#appendix-A.2. +TEST_F(PrfBasedDeriverTest, DeriveKeyset) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA256); + prf_key.mutable_params()->set_salt( + test::HexDecodeOrDie("606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf")); + prf_key.set_key_value( + test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f")); + + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes256Gcm()); + ASSERT_THAT(deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset( + test::HexDecodeOrDie("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")); + ASSERT_THAT(handle, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + ASSERT_THAT(keyset.key(), SizeIs(1)); + EXPECT_THAT(keyset.key(0).key_data().type_url(), + Eq(AesGcmKeyManager().get_key_type())); + EXPECT_THAT(keyset.key(0).key_data().key_material_type(), + Eq(AesGcmKeyManager().key_material_type())); + + AesGcmKey derived_key; + ASSERT_TRUE(derived_key.ParseFromString(keyset.key(0).key_data().value())); + EXPECT_THAT(derived_key.version(), Eq(AesGcmKeyManager().get_version())); + // The derived key value is the first 32 bytes of the test vector's OKM field. + EXPECT_THAT(test::HexEncode(derived_key.key_value()), + Eq("b11e398dc80327a1c8e7f78c596a4934" + "4f012eda2d4efad8a050cc4c19afa97c")); +} + +TEST_F(PrfBasedDeriverTest, DeriveKeysetHoldsPlaceholderValues) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA256); + prf_key.mutable_params()->set_salt(""); + prf_key.set_key_value("0123456789abcdef0123456789abcdef"); + + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset("salt"); + ASSERT_THAT(handle, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + EXPECT_THAT(keyset.primary_key_id(), Eq(0)); + ASSERT_THAT(keyset.key(), SizeIs(1)); + EXPECT_THAT(keyset.key(0).status(), Eq(KeyStatusType::UNKNOWN_STATUS)); + EXPECT_THAT(keyset.key(0).key_id(), Eq(0)); + EXPECT_THAT(keyset.key(0).output_prefix_type(), + Eq(OutputPrefixType::UNKNOWN_PREFIX)); +} + +TEST_F(PrfBasedDeriverTest, DeriveKeysetWithDifferentPrfKeys) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA512); + prf_key.mutable_params()->set_salt(""); + prf_key.set_key_value(subtle::Random::GetRandomBytes(32)); + + AesGcmKey derived_key_0; + AesGcmKey derived_key_1; + { + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset("salt"); + ASSERT_THAT(handle, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + ASSERT_TRUE( + derived_key_0.ParseFromString(keyset.key(0).key_data().value())); + } + { + prf_key.set_key_value(prf_key.key_value() + '\0'); + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset("salt"); + ASSERT_THAT(handle, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + ASSERT_TRUE( + derived_key_1.ParseFromString(keyset.key(0).key_data().value())); + } + EXPECT_THAT(derived_key_0.key_value(), SizeIs(16)); + EXPECT_THAT(derived_key_1.key_value(), SizeIs(16)); + EXPECT_THAT(derived_key_0.key_value(), Ne(derived_key_1.key_value())); +} + +TEST_F(PrfBasedDeriverTest, DeriveKeysetWithDifferentSalts) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA512); + prf_key.set_key_value(subtle::Random::GetRandomBytes(32)); + + AesGcmKey derived_key_0; + AesGcmKey derived_key_1; + { + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset(std::string(10, '\0')); + ASSERT_THAT(handle, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + ASSERT_TRUE( + derived_key_0.ParseFromString(keyset.key(0).key_data().value())); + } + { + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset(std::string(11, '\0')); + ASSERT_THAT(handle, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + ASSERT_TRUE( + derived_key_1.ParseFromString(keyset.key(0).key_data().value())); + } + EXPECT_THAT(derived_key_0.key_value(), SizeIs(16)); + EXPECT_THAT(derived_key_1.key_value(), SizeIs(16)); + EXPECT_THAT(derived_key_0.key_value(), Ne(derived_key_1.key_value())); +} + +// Test vector generated with Java implementation. +TEST_F(PrfBasedDeriverTest, DeriveKeysetWithTestVector0) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA512); + prf_key.set_key_value(test::HexDecodeOrDie( + "a1a2a3a4a5a6a7a8a9aaabacadaeafb1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c1c2c3c4c5c6c7c8c9cacbcccdcecfc1c2c3c4c5c6c7c8c9cacbcccdcecf" + "00")); + + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset(test::HexDecodeOrDie("1122334455")); + ASSERT_THAT(handle, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + AesGcmKey derived_key; + ASSERT_TRUE(derived_key.ParseFromString(keyset.key(0).key_data().value())); + EXPECT_THAT(derived_key.version(), Eq(AesGcmKeyManager().get_version())); + EXPECT_THAT(test::HexEncode(derived_key.key_value()), + Eq("31c449af66b669b9963ef2df30dfe5f9")); +} + +// Test vector generated with Java implementation. +TEST_F(PrfBasedDeriverTest, DeriveKeysetWithTestVector1) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA512); + prf_key.set_key_value(test::HexDecodeOrDie( + "c1c2c3c4c5c6c7c8c9cacbcccdcecfc1c2c3c4c5c6c7c8c9cacbcccdcecf" + "a1a2a3a4a5a6a7a8a9aaabacadaeafb1b2b3b4b5b6b7b8b9babbbcbdbebf")); + + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset(test::HexDecodeOrDie("00")); + ASSERT_THAT(handle, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + AesGcmKey derived_key; + ASSERT_TRUE(derived_key.ParseFromString(keyset.key(0).key_data().value())); + EXPECT_THAT(derived_key.version(), Eq(AesGcmKeyManager().get_version())); + EXPECT_THAT(test::HexEncode(derived_key.key_value()), + Eq("887af0808c1855eba1594bf540adb957")); +} + +// Test vector generated with Java implementation. +TEST_F(PrfBasedDeriverTest, DeriveKeysetWithEmptySaltTestVector) { + HkdfPrfKey prf_key; + prf_key.set_version(0); + prf_key.mutable_params()->set_hash(HashType::SHA512); + prf_key.set_key_value(test::HexDecodeOrDie( + "c1c2c3c4c5c6c7c8c9cacbcccdcecfc1c2c3c4c5c6c7c8c9cacbcccdcecf" + "a1a2a3a4a5a6a7a8a9aaabacadaeafb1b2b3b4b5b6b7b8b9babbbcbdbebf")); + + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + PrfBasedDeriver::New(test::AsKeyData(prf_key, KeyData::SYMMETRIC), + AeadKeyTemplates::Aes128Gcm()); + ASSERT_THAT(deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + (*deriver)->DeriveKeyset(""); + ASSERT_THAT(handle, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(**handle); + AesGcmKey derived_key; + ASSERT_TRUE(derived_key.ParseFromString(keyset.key(0).key_data().value())); + EXPECT_THAT(derived_key.version(), Eq(AesGcmKeyManager().get_version())); + EXPECT_THAT(test::HexEncode(derived_key.key_value()), + Eq("fb2b448c2595caf75129e282af758bf1")); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/keyderivation/key_derivation_config.cc b/cc/keyderivation/key_derivation_config.cc new file mode 100644 index 0000000..2194e33 --- /dev/null +++ b/cc/keyderivation/key_derivation_config.cc
@@ -0,0 +1,55 @@ +// Copyright 2020 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyderivation/key_derivation_config.h" + +#include "tink/config/tink_fips.h" +#include "tink/keyderivation/internal/prf_based_deriver_key_manager.h" +#include "tink/keyderivation/keyset_deriver_wrapper.h" +#include "tink/prf/hkdf_prf_key_manager.h" + +namespace crypto { +namespace tink { + +// static +util::Status KeyDerivationConfig::Register() { + // Register primitive wrappers. + util::Status status = Registry::RegisterPrimitiveWrapper( + absl::make_unique<KeysetDeriverWrapper>()); + if (!status.ok()) { + return status; + } + + // Currently, no KeysetDeriver key managers only use FIPS-validated + // implementations, so none are registered in FIPS-only mode. + if (IsFipsModeEnabled()) { + return util::OkStatus(); + } + + // Register required key manager for PrfBasedDeriverKeyManager. + status = Registry::RegisterKeyTypeManager( + absl::make_unique<HkdfPrfKeyManager>(), true); + if (!status.ok()) { + return status; + } + + // Register key managers. + return Registry::RegisterKeyTypeManager( + absl::make_unique<internal::PrfBasedDeriverKeyManager>(), true); +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/keyderivation/key_derivation_config.h b/cc/keyderivation/key_derivation_config.h new file mode 100644 index 0000000..5575024 --- /dev/null +++ b/cc/keyderivation/key_derivation_config.h
@@ -0,0 +1,46 @@ +// Copyright 2020 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_KEYDERIVATION_KEY_DERIVATION_CONFIG_H_ +#define TINK_KEYDERIVATION_KEY_DERIVATION_CONFIG_H_ + +#include "tink/util/status.h" + +namespace crypto { +namespace tink { + +/////////////////////////////////////////////////////////////////////////////// +// Static methods and constants for registering to the Registry all +// KeysetDeriver key types supported in a particular release of Tink. +// +// To register all KeysetDeriver key types, one can do: +// +// crypto::tink::util::Status status = KeyDerivationConfig::Register(); +// +class KeyDerivationConfig { + public: + // Registers KeysetDeriver primitive wrapper and key managers for all + // KeyDerivation key types from the current Tink release. + static crypto::tink::util::Status Register(); + + private: + KeyDerivationConfig() {} +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_KEYDERIVATION_KEY_DERIVATION_CONFIG_H_
diff --git a/cc/keyderivation/key_derivation_config_test.cc b/cc/keyderivation/key_derivation_config_test.cc new file mode 100644 index 0000000..5540d26 --- /dev/null +++ b/cc/keyderivation/key_derivation_config_test.cc
@@ -0,0 +1,83 @@ +// Copyright 2020 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyderivation/key_derivation_config.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/aead/aead_config.h" +#include "tink/aead/aead_key_templates.h" +#include "tink/aead/aes_gcm_key_manager.h" +#include "tink/keyderivation/key_derivation_key_templates.h" +#include "tink/keyderivation/keyset_deriver.h" +#include "tink/prf/prf_key_templates.h" +#include "tink/registry.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { + +namespace { + +using ::crypto::tink::test::IsOk; +using ::testing::Not; + +TEST(KeyDerivationConfigTest, Register) { + Registry::Reset(); + + EXPECT_THAT(KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + PrfKeyTemplates::HkdfSha256(), AeadKeyTemplates::Aes256Gcm()), + Not(IsOk())); + + ASSERT_THAT(KeyDerivationConfig::Register(), IsOk()); + ASSERT_THAT(AeadConfig::Register(), IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<AesGcmKeyManager>(), true), + IsOk()); + + util::StatusOr<::google::crypto::tink::KeyTemplate> templ = + KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + PrfKeyTemplates::HkdfSha256(), AeadKeyTemplates::Aes256Gcm()); + ASSERT_THAT(templ, IsOk()); + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + KeysetHandle::GenerateNew(*templ); + ASSERT_THAT(handle, IsOk()); + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + (*handle)->GetPrimitive<KeysetDeriver>(); + ASSERT_THAT(deriver, IsOk()); + util::StatusOr<std::unique_ptr<KeysetHandle>> derived_handle = + (*deriver)->DeriveKeyset("salty"); + ASSERT_THAT(derived_handle, IsOk()); + + util::StatusOr<std::unique_ptr<Aead>> aead = + (*derived_handle)->GetPrimitive<Aead>(); + ASSERT_THAT(aead, IsOk()); + std::string plaintext = "plaintext"; + std::string ad = "ad"; + util::StatusOr<std::string> ciphertext = (*aead)->Encrypt(plaintext, ad); + ASSERT_THAT(ciphertext, IsOk()); + util::StatusOr<std::string> got = (*aead)->Decrypt(*ciphertext, ad); + ASSERT_THAT(got, IsOk()); + EXPECT_EQ(plaintext, *got); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/keyderivation/key_derivation_key_templates.cc b/cc/keyderivation/key_derivation_key_templates.cc new file mode 100644 index 0000000..8b0e66f --- /dev/null +++ b/cc/keyderivation/key_derivation_key_templates.cc
@@ -0,0 +1,62 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyderivation/key_derivation_key_templates.h" + +#include <memory> + +#include "tink/keyderivation/internal/prf_based_deriver_key_manager.h" +#include "tink/subtle/random.h" + +namespace crypto { +namespace tink { + +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::PrfBasedDeriverKeyFormat; + +util::StatusOr<KeyTemplate> +KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + const KeyTemplate& prf_key_template, + const KeyTemplate& derived_key_template) { + KeyTemplate key_template; + key_template.set_type_url( + internal::PrfBasedDeriverKeyManager().get_key_type()); + key_template.set_output_prefix_type( + derived_key_template.output_prefix_type()); + + PrfBasedDeriverKeyFormat format; + *format.mutable_prf_key_template() = prf_key_template; + *format.mutable_params()->mutable_derived_key_template() = + derived_key_template; + format.SerializeToString(key_template.mutable_value()); + + // Verify `key_template` is derivable. + util::StatusOr<std::unique_ptr<KeysetHandle>> handle = + KeysetHandle::GenerateNew(key_template); + if (!handle.ok()) { + return handle.status(); + } + util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver = + (*handle)->GetPrimitive<KeysetDeriver>(); + if (!deriver.ok()) { + return deriver.status(); + } + + return key_template; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/keyderivation/key_derivation_key_templates.h b/cc/keyderivation/key_derivation_key_templates.h new file mode 100644 index 0000000..50af285 --- /dev/null +++ b/cc/keyderivation/key_derivation_key_templates.h
@@ -0,0 +1,52 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_KEYDERIVATION_KEY_DERIVATION_KEY_TEMPLATES_H_ +#define TINK_KEYDERIVATION_KEY_DERIVATION_KEY_TEMPLATES_H_ + +#include "tink/util/statusor.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { + +/////////////////////////////////////////////////////////////////////////////// +// Methods to generate KeyTemplates for key derivation. +class KeyDerivationKeyTemplates { + public: + // Creates a key template for key derivation that uses a PRF to derive a key + // that adheres to `derived_key_template`. The following must be true: + // (1) `prf_key_template` is a PRF key template, i.e. + // `keyset_handle->GetPrimitive<StreamingPrf>()` works. + // (2) `derived_key_template` describes a key type that supports derivation. + // + // The output prefix type of the derived key will match the output prefix type + // of `derived_key_template`. + // + // This function verifies the newly created key template by creating a + // KeysetDeriver primitive from it. This requires both the `prf_key_template` + // and `derived_key_template` key types to be in the registry. It also + // attempts to derive a key, returning an error on failure. + static util::StatusOr<google::crypto::tink::KeyTemplate> + CreatePrfBasedKeyTemplate( + const google::crypto::tink::KeyTemplate& prf_key_template, + const google::crypto::tink::KeyTemplate& derived_key_template); +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_KEYDERIVATION_KEY_DERIVATION_KEY_TEMPLATES_H_
diff --git a/cc/keyderivation/key_derivation_key_templates_test.cc b/cc/keyderivation/key_derivation_key_templates_test.cc new file mode 100644 index 0000000..a6e01b3 --- /dev/null +++ b/cc/keyderivation/key_derivation_key_templates_test.cc
@@ -0,0 +1,206 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyderivation/key_derivation_key_templates.h" + +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/aead/aead_key_templates.h" +#include "tink/aead/aes_gcm_key_manager.h" +#include "tink/keyderivation/internal/prf_based_deriver_key_manager.h" +#include "tink/keyderivation/keyset_deriver_wrapper.h" +#include "tink/prf/hkdf_prf_key_manager.h" +#include "tink/prf/prf_key_templates.h" +#include "tink/registry.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "proto/prf_based_deriver.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; +using ::google::crypto::tink::PrfBasedDeriverKeyFormat; +using ::testing::Eq; +using ::testing::Not; + +class KeyDerivationKeyTemplatesTest : public ::testing::Test { + protected: + void TearDown() override { Registry::Reset(); } +}; + +TEST_F(KeyDerivationKeyTemplatesTest, CreatePrfBasedKeyTemplate) { + ASSERT_THAT(Registry::RegisterPrimitiveWrapper( + absl::make_unique<KeysetDeriverWrapper>()), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<internal::PrfBasedDeriverKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<HkdfPrfKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + std::vector<OutputPrefixType> output_prefix_types = { + OutputPrefixType::RAW, OutputPrefixType::TINK, OutputPrefixType::LEGACY}; + for (OutputPrefixType output_prefix_type : output_prefix_types) { + KeyTemplate derived_key_template = AeadKeyTemplates::Aes256Gcm(); + derived_key_template.set_output_prefix_type(output_prefix_type); + util::StatusOr<KeyTemplate> key_template = + KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + PrfKeyTemplates::HkdfSha256(), derived_key_template); + + ASSERT_THAT(key_template, IsOk()); + EXPECT_THAT( + key_template->type_url(), + Eq("type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey")); + EXPECT_THAT(key_template->type_url(), + Eq(internal::PrfBasedDeriverKeyManager().get_key_type())); + EXPECT_THAT(key_template->output_prefix_type(), Eq(output_prefix_type)); + + PrfBasedDeriverKeyFormat key_format; + EXPECT_TRUE(key_format.ParseFromString(key_template->value())); + EXPECT_THAT( + internal::PrfBasedDeriverKeyManager().ValidateKeyFormat(key_format), + IsOk()); + } +} + +TEST_F(KeyDerivationKeyTemplatesTest, CreatePrfBasedKeyTemplateInvalidPrfKey) { + ASSERT_THAT(Registry::RegisterPrimitiveWrapper( + absl::make_unique<KeysetDeriverWrapper>()), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<internal::PrfBasedDeriverKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<HkdfPrfKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + EXPECT_THAT(KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + AeadKeyTemplates::Aes256Gcm(), AeadKeyTemplates::Aes256Gcm()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(KeyDerivationKeyTemplatesTest, + CreatePrfBasedKeyTemplateInvalidDerivedKeyTemplate) { + ASSERT_THAT(Registry::RegisterPrimitiveWrapper( + absl::make_unique<KeysetDeriverWrapper>()), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<internal::PrfBasedDeriverKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<HkdfPrfKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + util::StatusOr<KeyTemplate> derived_key_template = + KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + PrfKeyTemplates::HkdfSha256(), AeadKeyTemplates::Aes256Gcm()); + EXPECT_THAT(KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + PrfKeyTemplates::HkdfSha256(), *derived_key_template) + .status(), + StatusIs(absl::StatusCode::kUnimplemented)); +} + +TEST_F(KeyDerivationKeyTemplatesTest, + CreatePrfBasedKeyTemplateNoPrfBasedDeriverKeyManager) { + ASSERT_THAT(Registry::RegisterPrimitiveWrapper( + absl::make_unique<KeysetDeriverWrapper>()), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<HkdfPrfKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + EXPECT_THAT(KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + PrfKeyTemplates::HkdfSha256(), AeadKeyTemplates::Aes256Gcm()), + Not(IsOk())); +} + +TEST_F(KeyDerivationKeyTemplatesTest, + CreatePrfBasedKeyTemplateNoHkdfPrfKeyManager) { + ASSERT_THAT(Registry::RegisterPrimitiveWrapper( + absl::make_unique<KeysetDeriverWrapper>()), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<internal::PrfBasedDeriverKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + EXPECT_THAT(KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + PrfKeyTemplates::HkdfSha256(), AeadKeyTemplates::Aes256Gcm()), + Not(IsOk())); +} + +TEST_F(KeyDerivationKeyTemplatesTest, + CreatePrfBasedKeyTemplateNoAesGcmKeyManager) { + ASSERT_THAT(Registry::RegisterPrimitiveWrapper( + absl::make_unique<KeysetDeriverWrapper>()), + IsOk()); + ASSERT_THAT(Registry::RegisterKeyTypeManager( + absl::make_unique<internal::PrfBasedDeriverKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + ASSERT_THAT( + Registry::RegisterKeyTypeManager(absl::make_unique<HkdfPrfKeyManager>(), + /*new_key_allowed=*/true), + IsOk()); + + EXPECT_THAT(KeyDerivationKeyTemplates::CreatePrfBasedKeyTemplate( + PrfKeyTemplates::HkdfSha256(), AeadKeyTemplates::Aes256Gcm()), + Not(IsOk())); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/keyderivation/keyset_deriver.h b/cc/keyderivation/keyset_deriver.h new file mode 100644 index 0000000..f6f11bd --- /dev/null +++ b/cc/keyderivation/keyset_deriver.h
@@ -0,0 +1,46 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_KEYDERIVATION_KEYSET_DERIVER_H_ +#define TINK_KEYDERIVATION_KEYSET_DERIVER_H_ + +#include <memory> + +#include "absl/strings/string_view.h" +#include "tink/keyset_handle.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +// KeysetDeriver is the interface used to derive new keysets based on an +// additional input, the salt. +// +// The salt is used to create the keyset using a pseudorandom function. +// Implementations must be indistinguishable from ideal KeysetDerivers, which, +// for every salt, generates a new random keyset and caches it. +class KeysetDeriver { + public: + virtual crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> + DeriveKeyset(absl::string_view salt) const = 0; + + virtual ~KeysetDeriver() = default; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_KEYDERIVATION_KEYSET_DERIVER_H_
diff --git a/cc/keyderivation/keyset_deriver_wrapper.cc b/cc/keyderivation/keyset_deriver_wrapper.cc new file mode 100644 index 0000000..cf74a11 --- /dev/null +++ b/cc/keyderivation/keyset_deriver_wrapper.cc
@@ -0,0 +1,104 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyderivation/keyset_deriver_wrapper.h" + +#include <memory> +#include <utility> + +#include "absl/status/status.h" +#include "tink/cleartext_keyset_handle.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { + +namespace { + +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::Keyset; + +util::Status Validate(PrimitiveSet<KeysetDeriver>* deriver_set) { + if (deriver_set == nullptr) { + return util::Status(absl::StatusCode::kInternal, + "deriver_set must be non-NULL"); + } + if (deriver_set->get_primary() == nullptr) { + return util::Status(absl::StatusCode::kInvalidArgument, + "deriver_set has no primary"); + } + return util::OkStatus(); +} + +class KeysetDeriverSetWrapper : public KeysetDeriver { + public: + explicit KeysetDeriverSetWrapper( + std::unique_ptr<PrimitiveSet<KeysetDeriver>> deriver_set) + : deriver_set_(std::move(deriver_set)) {} + + crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> DeriveKeyset( + absl::string_view salt) const override; + + ~KeysetDeriverSetWrapper() override = default; + + private: + std::unique_ptr<PrimitiveSet<KeysetDeriver>> deriver_set_; +}; + +crypto::tink::util::StatusOr<KeyData> DeriveAndGetKeyData( + absl::string_view salt, const KeysetDeriver& deriver) { + auto keyset_handle_or = deriver.DeriveKeyset(salt); + if (!keyset_handle_or.ok()) return keyset_handle_or.status(); + const Keyset& keyset = + CleartextKeysetHandle::GetKeyset(*keyset_handle_or.value()); + if (keyset.key_size() != 1) { + return util::Status( + absl::StatusCode::kInternal, + "Wrapper Deriver must create a keyset with exactly one KeyData"); + } + return keyset.key(0).key_data(); +} + +crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> +KeysetDeriverSetWrapper::DeriveKeyset(absl::string_view salt) const { + Keyset keyset; + for (const auto* entry : deriver_set_->get_all_in_keyset_order()) { + Keyset::Key* key = keyset.add_key(); + + crypto::tink::util::StatusOr<KeyData> key_data_or = + DeriveAndGetKeyData(salt, entry->get_primitive()); + if (!key_data_or.ok()) return key_data_or.status(); + *key->mutable_key_data() = key_data_or.value(); + key->set_status(entry->get_status()); + key->set_output_prefix_type(entry->get_output_prefix_type()); + key->set_key_id(entry->get_key_id()); + } + keyset.set_primary_key_id(deriver_set_->get_primary()->get_key_id()); + return CleartextKeysetHandle::GetKeysetHandle(keyset); +} + +} // namespace + +crypto::tink::util::StatusOr<std::unique_ptr<KeysetDeriver>> +KeysetDeriverWrapper::Wrap( + std::unique_ptr<PrimitiveSet<KeysetDeriver>> deriver_set) const { + util::Status status = Validate(deriver_set.get()); + if (!status.ok()) return status; + return {absl::make_unique<KeysetDeriverSetWrapper>(std::move(deriver_set))}; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/keyderivation/keyset_deriver_wrapper.h b/cc/keyderivation/keyset_deriver_wrapper.h new file mode 100644 index 0000000..256440a --- /dev/null +++ b/cc/keyderivation/keyset_deriver_wrapper.h
@@ -0,0 +1,44 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_KEYDERIVATION_KEYSET_DERIVER_WRAPPER_H_ +#define TINK_KEYDERIVATION_KEYSET_DERIVER_WRAPPER_H_ + +#include <memory> + +#include "tink/keyderivation/keyset_deriver.h" +#include "tink/primitive_set.h" +#include "tink/primitive_wrapper.h" + +namespace crypto { +namespace tink { + +// A KeysetDeriverWrapper wraps the KeysetDeriver primitive. +// +// The wrapper derives a key from each key in a keyset. It returns the resulting +// keys in a new keyset. Each of the derived keys inherits key_id, status, and +// output_prefix_type from the key from which it was derived. +class KeysetDeriverWrapper + : public PrimitiveWrapper<KeysetDeriver, KeysetDeriver> { + public: + crypto::tink::util::StatusOr<std::unique_ptr<KeysetDeriver>> Wrap( + std::unique_ptr<PrimitiveSet<KeysetDeriver>> deriver_set) const override; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_KEYDERIVATION_KEYSET_DERIVER_WRAPPER_H_
diff --git a/cc/keyderivation/keyset_deriver_wrapper_test.cc b/cc/keyderivation/keyset_deriver_wrapper_test.cc new file mode 100644 index 0000000..dde29fb --- /dev/null +++ b/cc/keyderivation/keyset_deriver_wrapper_test.cc
@@ -0,0 +1,197 @@ +// Copyright 2019 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/keyderivation/keyset_deriver_wrapper.h" + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/cleartext_keyset_handle.h" +#include "tink/keyderivation/keyset_deriver.h" +#include "tink/primitive_set.h" +#include "tink/util/test_matchers.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::Keyset; +using ::google::crypto::tink::KeysetInfo; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::HasSubstr; + +// TODO(b/255828521): Move this to a shared location once KeysetDeriver is in +// the public API. +class DummyDeriver : public KeysetDeriver { + public: + explicit DummyDeriver(absl::string_view name) : name_(name) {} + util::StatusOr<std::unique_ptr<KeysetHandle>> DeriveKeyset( + absl::string_view salt) const override { + Keyset::Key key; + key.mutable_key_data()->set_type_url( + absl::StrCat(name_.size(), ":", name_, salt)); + key.set_status(KeyStatusType::UNKNOWN_STATUS); + key.set_key_id(0); + key.set_output_prefix_type(OutputPrefixType::UNKNOWN_PREFIX); + + Keyset keyset; + *keyset.add_key() = key; + keyset.set_primary_key_id(0); + return CleartextKeysetHandle::GetKeysetHandle(keyset); + } + + private: + std::string name_; +}; + +TEST(KeysetDeriverWrapperTest, WrapNullptr) { + EXPECT_THAT(KeysetDeriverWrapper().Wrap(nullptr).status(), + StatusIs(absl::StatusCode::kInternal, HasSubstr("non-NULL"))); +} + +TEST(KeysetDeriverWrapperTest, WrapEmpty) { + EXPECT_THAT( + KeysetDeriverWrapper() + .Wrap(absl::make_unique<PrimitiveSet<KeysetDeriver>>()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("no primary"))); +} + +TEST(KeysetDeriverWrapperTest, WrapNoPrimary) { + auto deriver_set = absl::make_unique<PrimitiveSet<KeysetDeriver>>(); + KeysetInfo::KeyInfo key_info; + key_info.set_key_id(1234); + key_info.set_status(KeyStatusType::ENABLED); + key_info.set_output_prefix_type(OutputPrefixType::TINK); + + EXPECT_THAT( + deriver_set->AddPrimitive(absl::make_unique<DummyDeriver>(""), key_info) + .status(), + IsOk()); + + EXPECT_THAT( + KeysetDeriverWrapper().Wrap(std::move(deriver_set)).status(), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("no primary"))); +} + +TEST(KeysetDeriverWrapperTest, WrapSingle) { + auto deriver_set = absl::make_unique<PrimitiveSet<KeysetDeriver>>(); + KeysetInfo::KeyInfo key_info; + key_info.set_key_id(1234); + key_info.set_status(KeyStatusType::ENABLED); + key_info.set_output_prefix_type(OutputPrefixType::TINK); + key_info.set_type_url( + "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey"); + + auto entry_or = deriver_set->AddPrimitive( + absl::make_unique<DummyDeriver>("wrap_single_key"), key_info); + ASSERT_THAT(entry_or, IsOk()); + EXPECT_THAT(deriver_set->set_primary(entry_or.value()), IsOk()); + + auto wrapper_deriver_or = KeysetDeriverWrapper().Wrap(std::move(deriver_set)); + + ASSERT_THAT(wrapper_deriver_or, IsOk()); + + auto derived_keyset_or = + wrapper_deriver_or.value()->DeriveKeyset("wrap_single_salt"); + + ASSERT_THAT(derived_keyset_or, IsOk()); + + Keyset keyset = CleartextKeysetHandle::GetKeyset(*derived_keyset_or.value()); + + EXPECT_THAT(keyset.primary_key_id(), Eq(1234)); + ASSERT_THAT(keyset.key_size(), Eq(1)); + EXPECT_THAT(keyset.key(0).key_data().type_url(), + Eq("15:wrap_single_keywrap_single_salt")); + EXPECT_THAT(keyset.key(0).status(), Eq(KeyStatusType::ENABLED)); + EXPECT_THAT(keyset.key(0).key_id(), Eq(1234)); + EXPECT_THAT(keyset.key(0).output_prefix_type(), Eq(OutputPrefixType::TINK)); +} + +TEST(KeysetDeriverWrapperTest, WrapMultiple) { + auto pset = absl::make_unique<PrimitiveSet<KeysetDeriver>>(); + std::vector<KeysetInfo::KeyInfo> key_infos; + + KeysetInfo::KeyInfo key_info; + key_info.set_key_id(1010101); + key_info.set_status(KeyStatusType::ENABLED); + key_info.set_output_prefix_type(OutputPrefixType::RAW); + key_info.set_type_url( + "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey"); + ASSERT_THAT( + pset->AddPrimitive(absl::make_unique<DummyDeriver>("k1"), key_info) + .status(), + IsOk()); + key_infos.push_back(key_info); + + key_info.set_key_id(2020202); + key_info.set_status(KeyStatusType::ENABLED); + key_info.set_output_prefix_type(OutputPrefixType::LEGACY); + key_info.set_type_url( + "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey"); + util::StatusOr<PrimitiveSet<KeysetDeriver>::Entry<KeysetDeriver>*> entry = + pset->AddPrimitive(absl::make_unique<DummyDeriver>("k2"), key_info); + ASSERT_THAT(entry, IsOk()); + ASSERT_THAT(pset->set_primary(*entry), IsOk()); + key_infos.push_back(key_info); + + key_info.set_key_id(3030303); + key_info.set_status(KeyStatusType::ENABLED); + key_info.set_output_prefix_type(OutputPrefixType::TINK); + key_info.set_type_url( + "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey"); + ASSERT_THAT( + pset->AddPrimitive(absl::make_unique<DummyDeriver>("k3"), key_info), + IsOk()); + key_infos.push_back(key_info); + + util::StatusOr<std::unique_ptr<KeysetDeriver>> wrapper_deriver = + KeysetDeriverWrapper().Wrap(std::move(pset)); + ASSERT_THAT(wrapper_deriver, IsOk()); + + util::StatusOr<std::unique_ptr<KeysetHandle>> derived_keyset = + (*wrapper_deriver)->DeriveKeyset("salt"); + ASSERT_THAT(derived_keyset, IsOk()); + Keyset keyset = CleartextKeysetHandle::GetKeyset(**derived_keyset); + + EXPECT_THAT(keyset.primary_key_id(), Eq(2020202)); + ASSERT_THAT(keyset.key_size(), Eq(3)); + + for (int i = 0; i < keyset.key().size(); i++) { + std::string type_url = absl::StrCat("2:k", i + 1, "salt"); + EXPECT_THAT(keyset.key(i).key_data().type_url(), Eq(type_url)); + + Keyset::Key key = keyset.key(i); + key_info = key_infos[i]; + EXPECT_THAT(key.status(), Eq(key_info.status())); + EXPECT_THAT(key.key_id(), Eq(key_info.key_id())); + EXPECT_THAT(key.output_prefix_type(), Eq(key_info.output_prefix_type())); + } +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/keyderivation/subtle/BUILD.bazel b/cc/keyderivation/subtle/BUILD.bazel new file mode 100644 index 0000000..5b01f6e --- /dev/null +++ b/cc/keyderivation/subtle/BUILD.bazel
@@ -0,0 +1 @@ +licenses(["notice"])
diff --git a/cc/keyderivation/subtle/CMakeLists.txt b/cc/keyderivation/subtle/CMakeLists.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cc/keyderivation/subtle/CMakeLists.txt
diff --git a/cc/keyset_handle.h b/cc/keyset_handle.h index 19c25bd..8d594e9 100644 --- a/cc/keyset_handle.h +++ b/cc/keyset_handle.h
@@ -12,20 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. // -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// #ifndef TINK_KEYSET_HANDLE_H_ #define TINK_KEYSET_HANDLE_H_ +#include <cstdint> +#include <memory> #include <string> #include <utility> +#include <vector> #include "absl/base/attributes.h" #include "absl/container/flat_hash_map.h" +#include "absl/memory/memory.h" #include "absl/status/status.h" #include "tink/aead.h" +#include "tink/configuration.h" +#include "tink/internal/configuration_impl.h" #include "tink/internal/key_info.h" +#include "tink/key.h" +#include "tink/key_gen_configuration.h" #include "tink/key_manager.h" +#include "tink/key_status.h" #include "tink/keyset_reader.h" #include "tink/keyset_writer.h" #include "tink/primitive_set.h" @@ -40,6 +49,60 @@ // key material. class KeysetHandle { public: + // Represents a single entry in a `KeysetHandle`. Some current behavior will + // be changed in the future. + class Entry { + public: + // May return an internal class in case there is no implementation of the + // corresponding key class yet. Returned value only valid for lifetime + // of entry object. + std::shared_ptr<const Key> GetKey() const { return key_; } + + // Status indicates whether or not a key should still be used. + KeyStatus GetStatus() const { return status_; } + + // ID should be unique (though currently Tink still accepts keysets with + // repeated IDs). + int GetId() const { return id_; } + + // Should return true for exactly one entry (though currently Tink still + // accepts keysets which have no entry marked as primary). + bool IsPrimary() const { return is_primary_; } + + private: + friend class KeysetHandle; + friend class KeysetHandleBuilder; + + Entry(std::shared_ptr<const Key> key, KeyStatus status, int id, + bool is_primary) + : key_(std::move(key)), + status_(status), + id_(id), + is_primary_(is_primary) {} + + std::shared_ptr<const Key> key_; + KeyStatus status_; + int id_; + bool is_primary_; + }; + + // Returns the number of entries in this keyset. + int size() const { return keyset_.key_size(); } + // Validates single `KeysetHandle::Entry` at `index` by making sure that the + // key entry's type URL is printable and that it has a valid key status. + crypto::tink::util::Status ValidateAt(int index) const; + // Validates each individual `KeysetHandle::Entry` in keyset handle by calling + // `ValidateAt()`. Also, checks that there is a single enabled primary key. + crypto::tink::util::Status Validate() const; + // Returns entry for primary key in this keyset. Crashes if `Validate()` + // does not return an OK status. Call `Validate()` prior to calling this + // method to avoid potentially crashing your program. + Entry GetPrimary() const; + // Returns the `KeysetHandle::Entry` at `index`. Crashes if + // `ValidateAt(index)` does not return an OK status. Call `ValidateAt(index)` + // prior to calling this method to avoid potentially crashing your program. + Entry operator[](int index) const; + // Creates a KeysetHandle from an encrypted keyset obtained via `reader` // using `master_key_aead` to decrypt the keyset, with monitoring annotations // `monitoring_annotations`; by default, `monitoring_annotations` is empty. @@ -69,10 +132,19 @@ const absl::flat_hash_map<std::string, std::string>& monitoring_annotations = {}); - // Returns a KeysetHandle for a new keyset that contains a single fresh key - // generated according to `key_template`. The keyset is annotated for - // monitoring with `monitoring_annotations`; by default, - // `monitoring_annotations` is empty. + // Returns a KeysetHandle containing a single new key generated according to + // `key_template` and using `config`. The keyset is annotated for monitoring + // with `monitoring_annotations`, which is empty by default. + static crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> + GenerateNew(const google::crypto::tink::KeyTemplate& key_template, + const crypto::tink::KeyGenConfiguration& config, + const absl::flat_hash_map<std::string, std::string>& + monitoring_annotations = {}); + + // TODO(b/265865177): Deprecate. + // Returns a KeysetHandle containing a single new key generated according to + // `key_template`. The keyset is annotated for monitoring with + // `monitoring_annotations`, which is empty by default. static crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> GenerateNew(const google::crypto::tink::KeyTemplate& key_template, const absl::flat_hash_map<std::string, std::string>& @@ -108,10 +180,14 @@ crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> GetPublicKeysetHandle() const; - // Creates a wrapped primitive corresponding to this keyset or fails with - // a non-ok status. Uses the KeyManager and PrimitiveWrapper objects in the - // global registry to create the primitive. This function is the most common - // way of creating a primitive. + // Creates a wrapped primitive using this keyset handle and config, which + // stores necessary primitive wrappers and key type managers. + template <class P> + crypto::tink::util::StatusOr<std::unique_ptr<P>> GetPrimitive( + const Configuration& config) const; + + // Creates a wrapped primitive using this keyset handle and the global + // registry, which stores necessary primitive wrappers and key type managers. template <class P> crypto::tink::util::StatusOr<std::unique_ptr<P>> GetPrimitive() const; @@ -128,15 +204,27 @@ // The classes below need access to get_keyset(); friend class CleartextKeysetHandle; friend class KeysetManager; - friend class RegistryImpl; // TestKeysetHandle::GetKeyset() provides access to get_keyset(). friend class TestKeysetHandle; + // KeysetHandleBuilder::Build() needs access to KeysetHandle(Keyset). + friend class KeysetHandleBuilder; + // Creates a handle that contains the given keyset. - explicit KeysetHandle(google::crypto::tink::Keyset keyset); - // Creates a handle that contains the given keyset. - explicit KeysetHandle(std::unique_ptr<google::crypto::tink::Keyset> keyset); + explicit KeysetHandle(google::crypto::tink::Keyset keyset) + : keyset_(std::move(keyset)) {} + explicit KeysetHandle(std::unique_ptr<google::crypto::tink::Keyset> keyset) + : keyset_(std::move(*keyset)) {} + // Creates a handle that contains the given `keyset` and `entries`. + explicit KeysetHandle( + google::crypto::tink::Keyset keyset, + const std::vector<std::shared_ptr<const Entry>>& entries) + : keyset_(std::move(keyset)), entries_(entries) {} + explicit KeysetHandle( + std::unique_ptr<google::crypto::tink::Keyset> keyset, + const std::vector<std::shared_ptr<const Entry>>& entries) + : keyset_(std::move(*keyset)), entries_(entries) {} // Creates a handle that contains the given `keyset` and // `monitoring_annotations`. KeysetHandle(google::crypto::tink::Keyset keyset, @@ -149,16 +237,45 @@ monitoring_annotations) : keyset_(std::move(*keyset)), monitoring_annotations_(monitoring_annotations) {} + // Creates a handle that contains the given `keyset`, `entries`, and + // `monitoring_annotations`. + KeysetHandle(google::crypto::tink::Keyset keyset, + const std::vector<std::shared_ptr<const Entry>>& entries, + const absl::flat_hash_map<std::string, std::string>& + monitoring_annotations) + : keyset_(std::move(keyset)), + entries_(entries), + monitoring_annotations_(monitoring_annotations) {} + KeysetHandle(std::unique_ptr<google::crypto::tink::Keyset> keyset, + const std::vector<std::shared_ptr<const Entry>>& entries, + const absl::flat_hash_map<std::string, std::string>& + monitoring_annotations) + : keyset_(std::move(*keyset)), + entries_(entries), + monitoring_annotations_(monitoring_annotations) {} - // Helper function which generates a key from a template, then adds it - // to the keyset. TODO(tholenst): Change this to a proper member operating - // on the internal keyset. + // Generates a key from `key_template` and adds it `keyset`. static crypto::tink::util::StatusOr<uint32_t> AddToKeyset( const google::crypto::tink::KeyTemplate& key_template, bool as_primary, + const crypto::tink::KeyGenConfiguration& config, google::crypto::tink::Keyset* keyset); + // Creates list of KeysetHandle::Entry entries derived from `keyset` in order. + static crypto::tink::util::StatusOr<std::vector<std::shared_ptr<const Entry>>> + GetEntriesFromKeyset(const google::crypto::tink::Keyset& keyset); + + // Creates KeysetHandle::Entry for `key`, which will be set to primary if + // its key id equals `primary_key_id`. + static util::StatusOr<Entry> CreateEntry( + const google::crypto::tink::Keyset::Key& key, uint32_t primary_key_id); + + // Generates a key from `key_template` and adds it to the keyset handle. + crypto::tink::util::StatusOr<uint32_t> AddKey( + const google::crypto::tink::KeyTemplate& key_template, bool as_primary, + const crypto::tink::KeyGenConfiguration& config); + // Returns keyset held by this handle. - const google::crypto::tink::Keyset& get_keyset() const; + const google::crypto::tink::Keyset& get_keyset() const { return keyset_; } // Creates a set of primitives corresponding to the keys with // (status == ENABLED) in the keyset given in 'keyset_handle', @@ -171,7 +288,17 @@ crypto::tink::util::StatusOr<std::unique_ptr<PrimitiveSet<P>>> GetPrimitives( const KeyManager<P>* custom_manager) const; + // Creates KeysetHandle::Entry from `keyset_` at `index`. + Entry CreateEntryAt(int index) const; + google::crypto::tink::Keyset keyset_; + // If this keyset handle has been created with a constructor that does not + // accept an entries argument, then `entries` will be empty and operator[] + // will fall back to creating the key entry on demand from `keyset_`. + // + // If `entries_` is not empty, then it should contain exactly one key entry + // for each key proto in `keyset_`. + std::vector<std::shared_ptr<const Entry>> entries_; absl::flat_hash_map<std::string, std::string> monitoring_annotations_; }; @@ -183,8 +310,8 @@ KeysetHandle::GetPrimitives(const KeyManager<P>* custom_manager) const { crypto::tink::util::Status status = ValidateKeyset(get_keyset()); if (!status.ok()) return status; - std::unique_ptr<PrimitiveSet<P>> primitives( - new PrimitiveSet<P>(monitoring_annotations_)); + typename PrimitiveSet<P>::Builder primitives_builder; + primitives_builder.AddAnnotations(monitoring_annotations_); for (const google::crypto::tink::Keyset::Key& key : get_keyset().key()) { if (key.status() == google::crypto::tink::KeyStatusType::ENABLED) { std::unique_ptr<P> primitive; @@ -198,23 +325,57 @@ if (!primitive_result.ok()) return primitive_result.status(); primitive = std::move(primitive_result.value()); } - auto entry_result = - primitives->AddPrimitive(std::move(primitive), KeyInfoFromKey(key)); - if (!entry_result.ok()) return entry_result.status(); if (key.key_id() == get_keyset().primary_key_id()) { - auto primary_result = primitives->set_primary(entry_result.value()); - if (!primary_result.ok()) return primary_result; + primitives_builder.AddPrimaryPrimitive(std::move(primitive), + KeyInfoFromKey(key)); + } else { + primitives_builder.AddPrimitive(std::move(primitive), + KeyInfoFromKey(key)); } } } - return std::move(primitives); + auto primitives = std::move(primitives_builder).Build(); + if (!primitives.ok()) return primitives.status(); + return absl::make_unique<PrimitiveSet<P>>(*std::move(primitives)); } template <class P> +crypto::tink::util::StatusOr<std::unique_ptr<P>> KeysetHandle::GetPrimitive( + const Configuration& config) const { + if (crypto::tink::internal::ConfigurationImpl::GetGlobalRegistryMode( + config)) { + return crypto::tink::internal::RegistryImpl::GlobalInstance().WrapKeyset<P>( + keyset_, monitoring_annotations_); + } + + crypto::tink::util::StatusOr< + const crypto::tink::internal::KeysetWrapperStore*> + wrapper_store = + crypto::tink::internal::ConfigurationImpl::GetKeysetWrapperStore( + config); + if (!wrapper_store.ok()) { + return wrapper_store.status(); + } + crypto::tink::util::StatusOr<const crypto::tink::internal::KeysetWrapper<P>*> + wrapper = (*wrapper_store)->Get<P>(); + if (!wrapper.ok()) { + return wrapper.status(); + } + return (*wrapper)->Wrap(keyset_, monitoring_annotations_); +} + +// TODO(b/265865177): Deprecate. +template <class P> crypto::tink::util::StatusOr<std::unique_ptr<P>> KeysetHandle::GetPrimitive() const { - return internal::RegistryImpl::GlobalInstance().WrapKeyset<P>( - keyset_, monitoring_annotations_); + // TODO(b/265705174): Replace with ConfigGlobalRegistry instance. + crypto::tink::Configuration config; + crypto::tink::util::Status status = + crypto::tink::internal::ConfigurationImpl::SetGlobalRegistryMode(config); + if (!status.ok()) { + return status; + } + return GetPrimitive<P>(config); } template <class P>
diff --git a/cc/keyset_handle_builder.h b/cc/keyset_handle_builder.h new file mode 100644 index 0000000..72d3af8 --- /dev/null +++ b/cc/keyset_handle_builder.h
@@ -0,0 +1,169 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_KEYSET_HANDLE_BUILDER_H_ +#define TINK_KEYSET_HANDLE_BUILDER_H_ + +#include <memory> +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include "tink/internal/keyset_handle_builder_entry.h" +#include "tink/key.h" +#include "tink/key_status.h" +#include "tink/keyset_handle.h" +#include "tink/parameters.h" + +namespace crypto { +namespace tink { + +// Creates new `KeysetHandle` objects. +class KeysetHandleBuilder { + public: + // Movable, but not copyable. + KeysetHandleBuilder(KeysetHandleBuilder&& other) = default; + KeysetHandleBuilder& operator=(KeysetHandleBuilder&& other) = default; + KeysetHandleBuilder(const KeysetHandleBuilder& other) = delete; + KeysetHandleBuilder& operator=(const KeysetHandleBuilder& other) = delete; + + // Creates initially empty keyset handle builder. + KeysetHandleBuilder() = default; + // Creates keyset handle builder by initially moving keys from `handle`. + explicit KeysetHandleBuilder(const KeysetHandle& handle); + + // Represents a single entry in a `KeysetHandleBuilder`. + class Entry { + public: + // Movable, but not copyable. + Entry(Entry&& other) = default; + Entry& operator=(Entry&& other) = default; + Entry(const Entry& other) = delete; + Entry& operator=(const Entry& other) = delete; + + // Creates new KeysetHandleBuilder::Entry from a given `key`. Also, sets + // key `status` and whether or not the key `is_primary`. + static Entry CreateFromKey(std::shared_ptr<const Key> key, KeyStatus status, + bool is_primary); + + template <typename CopyableKey> + inline static Entry CreateFromCopyableKey(CopyableKey key, KeyStatus status, + bool is_primary) { + auto copyable_key = absl::make_unique<CopyableKey>(std::move(key)); + return CreateFromKey(std::move(copyable_key), status, is_primary); + } + + // Creates new KeysetHandleBuilder::Entry from given `parameters`. Also, + // sets key `status` and whether or not the key `is_primary`. If `id` + // does not have a value, then the key will be assigned a random id. + static Entry CreateFromParams(std::shared_ptr<const Parameters> parameters, + KeyStatus status, bool is_primary, + absl::optional<int> id = absl::nullopt); + + template <typename CopyableParameters> + inline static Entry CreateFromCopyableParams( + CopyableParameters parameters, KeyStatus status, bool is_primary, + absl::optional<int> id = absl::nullopt) { + auto copyable_params = + absl::make_unique<CopyableParameters>(std::move(parameters)); + return CreateFromParams(std::move(copyable_params), status, is_primary, + id); + } + + // Sets the key status of this entry. + void SetStatus(KeyStatus status) { entry_->SetStatus(status); } + // Returns key status of this entry. + KeyStatus GetStatus() const { return entry_->GetStatus(); } + + // Assigns a fixed id when this keyset is built. + void SetFixedId(int id) { entry_->SetFixedId(id); } + // Assigns an unused random id when this keyset is built. + void SetRandomId() { entry_->SetRandomId(); } + + // Sets this entry as the primary key. + void SetPrimary() { entry_->SetPrimary(); } + // Unsets this entry as the primary key. + void UnsetPrimary() { entry_->UnsetPrimary(); } + // Returns whether or not this entry has been marked as a primary. + bool IsPrimary() const { return entry_->IsPrimary(); } + + private: + friend class KeysetHandleBuilder; + + explicit Entry(std::unique_ptr<internal::KeysetHandleBuilderEntry> entry) + : entry_(std::move(entry)) {} + + // Returns whether or not this entry has a randomly assigned id. + bool HasRandomId() { + return entry_->GetKeyIdStrategyEnum() == + internal::KeyIdStrategyEnum::kRandomId; + } + + internal::KeyIdStrategy GetKeyIdStrategy() { + return entry_->GetKeyIdStrategy(); + } + + crypto::tink::util::StatusOr<google::crypto::tink::Keyset::Key> + CreateKeysetKey(int id) { + return entry_->CreateKeysetKey(id); + } + + std::unique_ptr<internal::KeysetHandleBuilderEntry> entry_; + bool added_to_builder_ = false; + }; + + // Adds an `entry` to the keyset builder. Crashes if `entry` has already been + // added to a keyset handle builder. + KeysetHandleBuilder& AddEntry(KeysetHandleBuilder::Entry entry); + // Removes an entry at `index` from keyset builder. + KeysetHandleBuilder& RemoveEntry(int index); + + // Returns the number of Entry objects in this keyset builder. + int size() const { return entries_.size(); } + + // Returns entry from keyset builder at `index`. + KeysetHandleBuilder::Entry& operator[](int index) { return entries_[index]; } + + // Creates a new `KeysetHandle` object. + // + // Note: Since KeysetHandleBuilder::Entry objects might have randomly + // generated IDs, Build() can only be called once on a single + // KeysetHandleBuilder object. Otherwise, the KeysetHandleBuilder::Entry + // IDs would randomly change for each call to Build(), which would result + // in incompatible keysets. + crypto::tink::util::StatusOr<KeysetHandle> Build(); + + private: + // Select the next key id based on the given strategy. + crypto::tink::util::StatusOr<int> NextIdFromKeyIdStrategy( + internal::KeyIdStrategy strategy, const std::set<int>& ids_so_far); + + // Unset primary flag on all entries. + void ClearPrimary(); + + // Verify that entries with fixed IDs do not follow entries with random IDs. + crypto::tink::util::Status CheckIdAssignments(); + + std::vector<KeysetHandleBuilder::Entry> entries_; + + bool build_called_ = false; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_KEYSET_HANDLE_BUILDER_H_
diff --git a/cc/keyset_manager.h b/cc/keyset_manager.h index d3bfcff..645fc83 100644 --- a/cc/keyset_manager.h +++ b/cc/keyset_manager.h
@@ -16,6 +16,8 @@ #ifndef TINK_KEYSET_MANAGER_H_ #define TINK_KEYSET_MANAGER_H_ +#include <memory> + #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" #include "tink/util/status.h" @@ -34,7 +36,7 @@ class KeysetManager { public: // Constructs a KeysetManager with an empty Keyset. - KeysetManager() {} + KeysetManager() = default; // Creates a new KeysetManager that contains a Keyset with a single key // generated freshly according the specification in 'key_template'.
diff --git a/cc/keyset_reader.h b/cc/keyset_reader.h index 037697b..02e2659 100644 --- a/cc/keyset_reader.h +++ b/cc/keyset_reader.h
@@ -17,6 +17,8 @@ #ifndef TINK_KEYSET_READER_H_ #define TINK_KEYSET_READER_H_ +#include <memory> + #include "tink/util/statusor.h" #include "proto/tink.pb.h" @@ -37,7 +39,7 @@ std::unique_ptr<google::crypto::tink::EncryptedKeyset>> ReadEncrypted() = 0; - virtual ~KeysetReader() {} + virtual ~KeysetReader() = default; }; } // namespace tink
diff --git a/cc/keyset_writer.h b/cc/keyset_writer.h index 3c30bd1..96df7db 100644 --- a/cc/keyset_writer.h +++ b/cc/keyset_writer.h
@@ -35,7 +35,7 @@ virtual crypto::tink::util::Status Write(const google::crypto::tink::EncryptedKeyset& encrypted_keyset) = 0; - virtual ~KeysetWriter() {} + virtual ~KeysetWriter() = default; }; } // namespace tink
diff --git a/cc/kms_client.h b/cc/kms_client.h index dc7a59d..6753804 100644 --- a/cc/kms_client.h +++ b/cc/kms_client.h
@@ -38,7 +38,7 @@ virtual crypto::tink::util::StatusOr<std::unique_ptr<Aead>> GetAead(absl::string_view key_uri) const = 0; - virtual ~KmsClient() {} + virtual ~KmsClient() = default; }; } // namespace tink
diff --git a/cc/kms_clients.h b/cc/kms_clients.h index 3b2ac13..772360b 100644 --- a/cc/kms_clients.h +++ b/cc/kms_clients.h
@@ -17,6 +17,7 @@ #ifndef TINK_KMS_CLIENTS_H_ #define TINK_KMS_CLIENTS_H_ +#include <memory> #include <utility> #include <vector>
diff --git a/cc/mac.h b/cc/mac.h index 6bc02b8..a50adc9 100644 --- a/cc/mac.h +++ b/cc/mac.h
@@ -42,7 +42,7 @@ absl::string_view mac_value, absl::string_view data) const = 0; - virtual ~Mac() {} + virtual ~Mac() = default; }; } // namespace tink
diff --git a/cc/mac/BUILD.bazel b/cc/mac/BUILD.bazel index b681672..77f5534 100644 --- a/cc/mac/BUILD.bazel +++ b/cc/mac/BUILD.bazel
@@ -32,7 +32,9 @@ visibility = ["//visibility:public"], deps = [ ":aes_cmac_key_manager", + ":aes_cmac_proto_serialization", ":hmac_key_manager", + ":hmac_proto_serialization", ":mac_wrapper", "//:registry", "//config:config_util", @@ -172,8 +174,121 @@ deps = [ ":mac_parameters", "//:crypto_format", + "//internal:util", "//util:status", "//util:statusor", + "@com_google_absl//absl/log", + ], +) + +cc_library( + name = "aes_cmac_key", + srcs = ["aes_cmac_key.cc"], + hdrs = ["aes_cmac_key.h"], + include_prefix = "tink/mac", + deps = [ + ":aes_cmac_parameters", + ":mac_key", + "//:partial_key_access_token", + "//:restricted_data", + "//subtle:subtle_util", + "//util:status", + "//util:statusor", + "@boringssl//:crypto", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:optional", + ], +) + +cc_library( + name = "aes_cmac_proto_serialization", + srcs = ["aes_cmac_proto_serialization.cc"], + hdrs = ["aes_cmac_proto_serialization.h"], + include_prefix = "tink/mac", + deps = [ + ":aes_cmac_key", + ":aes_cmac_parameters", + "//:partial_key_access", + "//:restricted_data", + "//:secret_key_access_token", + "//internal:key_parser", + "//internal:key_serializer", + "//internal:mutable_serialization_registry", + "//internal:parameters_parser", + "//internal:parameters_serializer", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:aes_cmac_cc_proto", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + ], +) + +cc_library( + name = "hmac_parameters", + srcs = ["hmac_parameters.cc"], + hdrs = ["hmac_parameters.h"], + include_prefix = "tink/mac", + deps = [ + ":mac_parameters", + "//:crypto_format", + "//internal:util", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/log", + ], +) + +cc_library( + name = "hmac_key", + srcs = ["hmac_key.cc"], + hdrs = ["hmac_key.h"], + include_prefix = "tink/mac", + deps = [ + ":hmac_parameters", + ":mac_key", + "//:partial_key_access_token", + "//:restricted_data", + "//subtle:subtle_util", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:optional", + ], +) + +cc_library( + name = "hmac_proto_serialization", + srcs = ["hmac_proto_serialization.cc"], + hdrs = ["hmac_proto_serialization.h"], + include_prefix = "tink/mac", + deps = [ + ":hmac_key", + ":hmac_parameters", + "//:partial_key_access", + "//:restricted_data", + "//:secret_key_access_token", + "//internal:key_parser", + "//internal:key_serializer", + "//internal:mutable_serialization_registry", + "//internal:parameters_parser", + "//internal:parameters_serializer", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:common_cc_proto", + "//proto:hmac_cc_proto", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", ], ) @@ -207,16 +322,26 @@ srcs = ["mac_config_test.cc"], tags = ["fips"], deps = [ + ":aes_cmac_key", ":aes_cmac_key_manager", + ":aes_cmac_parameters", + ":hmac_key", ":hmac_key_manager", + ":hmac_parameters", ":mac_config", ":mac_key_templates", "//:chunked_mac", - "//:config", + "//:insecure_secret_key_access", "//:keyset_handle", "//:mac", + "//:partial_key_access", "//:registry", - "//config:tink_fips", + "//internal:fips_utils", + "//internal:mutable_serialization_registry", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:common_cc_proto", + "//proto:tink_cc_proto", "//util:status", "//util:test_matchers", "//util:test_util", @@ -324,3 +449,90 @@ "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "aes_cmac_key_test", + size = "small", + srcs = ["aes_cmac_key_test.cc"], + deps = [ + ":aes_cmac_key", + ":aes_cmac_parameters", + "//:partial_key_access", + "//:restricted_data", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "aes_cmac_proto_serialization_test", + size = "small", + srcs = ["aes_cmac_proto_serialization_test.cc"], + deps = [ + ":aes_cmac_key", + ":aes_cmac_parameters", + ":aes_cmac_proto_serialization", + "//:insecure_secret_key_access", + "//:partial_key_access", + "//:restricted_data", + "//internal:mutable_serialization_registry", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:aes_cmac_cc_proto", + "//proto:tink_cc_proto", + "//subtle:random", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "hmac_parameters_test", + size = "small", + srcs = ["hmac_parameters_test.cc"], + deps = [ + ":hmac_parameters", + "//util:statusor", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "hmac_key_test", + srcs = ["hmac_key_test.cc"], + deps = [ + ":hmac_key", + ":hmac_parameters", + "//:partial_key_access", + "//:restricted_data", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "hmac_proto_serialization_test", + srcs = ["hmac_proto_serialization_test.cc"], + deps = [ + ":hmac_key", + ":hmac_parameters", + ":hmac_proto_serialization", + "//:insecure_secret_key_access", + "//:partial_key_access", + "//:restricted_data", + "//internal:mutable_serialization_registry", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:common_cc_proto", + "//proto:hmac_cc_proto", + "//proto:tink_cc_proto", + "//subtle:random", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/cc/mac/BUILD.gn b/cc/mac/BUILD.gn index 6129f46..601f2fc 100644 --- a/cc/mac/BUILD.gn +++ b/cc/mac/BUILD.gn
@@ -41,7 +41,9 @@ ] public_deps = [ ":aes_cmac_key_manager", + ":aes_cmac_proto_serialization", ":hmac_key_manager", + ":hmac_proto_serialization", ":mac_wrapper", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/memory:memory", @@ -117,3 +119,175 @@ ] public_configs = [ "//third_party/tink:tink_config" ] } + +# CC Library : mac_parameters +source_set("mac_parameters") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "mac_parameters.h" ] + public_deps = [ "//third_party/tink/cc:parameters" ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : mac_key +source_set("mac_key") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ "mac_key.h" ] + public_deps = [ + ":mac_parameters", + "//third_party/tink/cc:key", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : aes_cmac_parameters +source_set("aes_cmac_parameters") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "aes_cmac_parameters.cc", + "aes_cmac_parameters.h", + ] + public_deps = [ + ":mac_parameters", + "//third_party/abseil-cpp/absl/log:log", + "//third_party/tink/cc:crypto_format", + "//third_party/tink/cc/internal:util", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : aes_cmac_key +source_set("aes_cmac_key") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "aes_cmac_key.cc", + "aes_cmac_key.h", + ] + public_deps = [ + ":aes_cmac_parameters", + ":mac_key", + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/strings:str_format", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/boringssl:crypto", + "//third_party/tink/cc:partial_key_access_token", + "//third_party/tink/cc:restricted_data", + "//third_party/tink/cc/subtle:subtle_util", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : aes_cmac_proto_serialization +source_set("aes_cmac_proto_serialization") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "aes_cmac_proto_serialization.cc", + "aes_cmac_proto_serialization.h", + ] + public_deps = [ + ":aes_cmac_key", + ":aes_cmac_parameters", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc:partial_key_access", + "//third_party/tink/cc:restricted_data", + "//third_party/tink/cc:secret_key_access_token", + "//third_party/tink/cc/internal:key_parser", + "//third_party/tink/cc/internal:key_serializer", + "//third_party/tink/cc/internal:mutable_serialization_registry", + "//third_party/tink/cc/internal:parameters_parser", + "//third_party/tink/cc/internal:parameters_serializer", + "//third_party/tink/cc/internal:proto_key_serialization", + "//third_party/tink/cc/internal:proto_parameters_serialization", + "//third_party/tink/cc/proto:aes_cmac_proto", + "//third_party/tink/cc/proto:tink_proto", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : hmac_parameters +source_set("hmac_parameters") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "hmac_parameters.cc", + "hmac_parameters.h", + ] + public_deps = [ + ":mac_parameters", + "//third_party/abseil-cpp/absl/log:log", + "//third_party/tink/cc:crypto_format", + "//third_party/tink/cc/internal:util", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : hmac_key +source_set("hmac_key") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "hmac_key.cc", + "hmac_key.h", + ] + public_deps = [ + ":hmac_parameters", + ":mac_key", + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/strings:str_format", + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc:partial_key_access_token", + "//third_party/tink/cc:restricted_data", + "//third_party/tink/cc/subtle:subtle_util", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +} + +# CC Library : hmac_proto_serialization +source_set("hmac_proto_serialization") { + configs += [ "//build/config:no_rtti" ] + configs -= [ "//build/config:no_rtti" ] + sources = [ + "hmac_proto_serialization.cc", + "hmac_proto_serialization.h", + ] + public_deps = [ + ":hmac_key", + ":hmac_parameters", + "//third_party/abseil-cpp/absl/status:status", + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/tink/cc:partial_key_access", + "//third_party/tink/cc:restricted_data", + "//third_party/tink/cc:secret_key_access_token", + "//third_party/tink/cc/internal:key_parser", + "//third_party/tink/cc/internal:key_serializer", + "//third_party/tink/cc/internal:mutable_serialization_registry", + "//third_party/tink/cc/internal:parameters_parser", + "//third_party/tink/cc/internal:parameters_serializer", + "//third_party/tink/cc/internal:proto_key_serialization", + "//third_party/tink/cc/internal:proto_parameters_serialization", + "//third_party/tink/cc/proto:common_proto", + "//third_party/tink/cc/proto:hmac_proto", + "//third_party/tink/cc/proto:tink_proto", + "//third_party/tink/cc/util:status", + "//third_party/tink/cc/util:statusor", + ] + public_configs = [ "//third_party/tink:tink_config" ] +}
diff --git a/cc/mac/CMakeLists.txt b/cc/mac/CMakeLists.txt index ab763f1..95e5335 100644 --- a/cc/mac/CMakeLists.txt +++ b/cc/mac/CMakeLists.txt
@@ -30,7 +30,9 @@ mac_config.h DEPS tink::mac::aes_cmac_key_manager + tink::mac::aes_cmac_proto_serialization tink::mac::hmac_key_manager + tink::mac::hmac_proto_serialization tink::mac::mac_wrapper absl::core_headers absl::memory @@ -151,6 +153,7 @@ DEPS absl::strings tink::core::mac + TESTONLY ) tink_cc_library( @@ -160,11 +163,119 @@ aes_cmac_parameters.h DEPS tink::mac::mac_parameters + absl::log tink::core::crypto_format + tink::internal::util tink::util::status tink::util::statusor ) +tink_cc_library( + NAME aes_cmac_key + SRCS + aes_cmac_key.cc + aes_cmac_key.h + DEPS + tink::mac::aes_cmac_parameters + tink::mac::mac_key + absl::core_headers + absl::strings + absl::str_format + absl::optional + crypto + tink::core::partial_key_access_token + tink::core::restricted_data + tink::subtle::subtle_util + tink::util::status + tink::util::statusor +) + +tink_cc_library( + NAME aes_cmac_proto_serialization + SRCS + aes_cmac_proto_serialization.cc + aes_cmac_proto_serialization.h + DEPS + tink::mac::aes_cmac_key + tink::mac::aes_cmac_parameters + absl::status + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::core::secret_key_access_token + tink::internal::key_parser + tink::internal::key_serializer + tink::internal::mutable_serialization_registry + tink::internal::parameters_parser + tink::internal::parameters_serializer + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::util::status + tink::util::statusor + tink::proto::aes_cmac_cc_proto + tink::proto::tink_cc_proto +) + +tink_cc_library( + NAME hmac_parameters + SRCS + hmac_parameters.cc + hmac_parameters.h + DEPS + tink::mac::mac_parameters + absl::log + tink::core::crypto_format + tink::internal::util + tink::util::status + tink::util::statusor +) + +tink_cc_library( + NAME hmac_key + SRCS + hmac_key.cc + hmac_key.h + DEPS + tink::mac::hmac_parameters + tink::mac::mac_key + absl::core_headers + absl::strings + absl::str_format + absl::optional + tink::core::partial_key_access_token + tink::core::restricted_data + tink::subtle::subtle_util + tink::util::status + tink::util::statusor +) + +tink_cc_library( + NAME hmac_proto_serialization + SRCS + hmac_proto_serialization.cc + hmac_proto_serialization.h + DEPS + tink::mac::hmac_key + tink::mac::hmac_parameters + absl::status + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::core::secret_key_access_token + tink::internal::key_parser + tink::internal::key_serializer + tink::internal::mutable_serialization_registry + tink::internal::parameters_parser + tink::internal::parameters_serializer + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::util::status + tink::util::statusor + tink::proto::common_cc_proto + tink::proto::hmac_cc_proto + tink::proto::tink_cc_proto +) + # tests tink_cc_test( @@ -193,22 +304,32 @@ SRCS mac_config_test.cc DEPS + tink::mac::aes_cmac_key tink::mac::aes_cmac_key_manager + tink::mac::aes_cmac_parameters + tink::mac::hmac_key tink::mac::hmac_key_manager + tink::mac::hmac_parameters tink::mac::mac_config tink::mac::mac_key_templates gmock absl::status crypto tink::core::chunked_mac - tink::core::config + tink::core::insecure_secret_key_access tink::core::keyset_handle tink::core::mac + tink::core::partial_key_access tink::core::registry - tink::config::tink_fips + tink::internal::fips_utils + tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization tink::util::status tink::util::test_matchers tink::util::test_util + tink::proto::common_cc_proto + tink::proto::tink_cc_proto ) tink_cc_test( @@ -304,3 +425,87 @@ tink::util::statusor tink::util::test_matchers ) + +tink_cc_test( + NAME aes_cmac_key_test + SRCS + aes_cmac_key_test.cc + DEPS + tink::mac::aes_cmac_key + tink::mac::aes_cmac_parameters + gmock + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_test( + NAME aes_cmac_proto_serialization_test + SRCS + aes_cmac_proto_serialization_test.cc + DEPS + tink::mac::aes_cmac_key + tink::mac::aes_cmac_parameters + tink::mac::aes_cmac_proto_serialization + gmock + tink::core::insecure_secret_key_access + tink::core::partial_key_access + tink::core::restricted_data + tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::subtle::random + tink::util::test_matchers + tink::proto::aes_cmac_cc_proto + tink::proto::tink_cc_proto +) + +tink_cc_test( + NAME hmac_parameters_test + SRCS + hmac_parameters_test.cc + DEPS + tink::mac::hmac_parameters + gmock + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_test( + NAME hmac_key_test + SRCS + hmac_key_test.cc + DEPS + tink::mac::hmac_key + tink::mac::hmac_parameters + gmock + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::util::statusor + tink::util::test_matchers +) + +tink_cc_test( + NAME hmac_proto_serialization_test + SRCS + hmac_proto_serialization_test.cc + DEPS + tink::mac::hmac_key + tink::mac::hmac_parameters + tink::mac::hmac_proto_serialization + gmock + tink::core::insecure_secret_key_access + tink::core::partial_key_access + tink::core::restricted_data + tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::subtle::random + tink::util::test_matchers + tink::proto::common_cc_proto + tink::proto::hmac_cc_proto + tink::proto::tink_cc_proto +)
diff --git a/cc/mac/aes_cmac_key.cc b/cc/mac/aes_cmac_key.cc new file mode 100644 index 0000000..cd16cac --- /dev/null +++ b/cc/mac/aes_cmac_key.cc
@@ -0,0 +1,108 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/aes_cmac_key.h" + +#include <memory> +#include <string> + +#include "absl/base/attributes.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_format.h" +#include "absl/types/optional.h" +#include "tink/mac/aes_cmac_parameters.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/subtle/subtle_util.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +util::StatusOr<AesCmacKey> AesCmacKey::Create( + const AesCmacParameters& parameters, const RestrictedData& key_bytes, + absl::optional<int> id_requirement, PartialKeyAccessToken token) { + if (parameters.KeySizeInBytes() != key_bytes.size()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Key size does not match AES-CMAC parameters"); + } + if (parameters.HasIdRequirement() && !id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key without ID requirement with parameters with ID " + "requirement"); + } + if (!parameters.HasIdRequirement() && id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key with ID requirement with parameters without ID " + "requirement"); + } + util::StatusOr<std::string> output_prefix = + ComputeOutputPrefix(parameters, id_requirement); + if (!output_prefix.ok()) { + return output_prefix.status(); + } + return AesCmacKey(parameters, key_bytes, id_requirement, + *std::move(output_prefix)); +} + +util::StatusOr<std::string> AesCmacKey::ComputeOutputPrefix( + const AesCmacParameters& parameters, absl::optional<int> id_requirement) { + switch (parameters.GetVariant()) { + case AesCmacParameters::Variant::kNoPrefix: + return std::string(""); // Empty prefix. + case AesCmacParameters::Variant::kLegacy: + ABSL_FALLTHROUGH_INTENDED; + case AesCmacParameters::Variant::kCrunchy: + if (!id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "id requirement must have value with kCrunchy or kLegacy"); + } + return absl::StrCat(absl::HexStringToBytes("00"), + subtle::BigEndian32(*id_requirement)); + case AesCmacParameters::Variant::kTink: + if (!id_requirement.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "id requirement must have value with kTink"); + } + return absl::StrCat(absl::HexStringToBytes("01"), + subtle::BigEndian32(*id_requirement)); + default: + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Invalid variant: ", parameters.GetVariant())); + } +} + +bool AesCmacKey::operator==(const Key& other) const { + const AesCmacKey* that = dynamic_cast<const AesCmacKey*>(&other); + if (that == nullptr) { + return false; + } + if (GetParameters() != that->GetParameters()) { + return false; + } + if (id_requirement_ != that->id_requirement_) { + return false; + } + return key_bytes_ == that->key_bytes_; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/aes_cmac_key.h b/cc/mac/aes_cmac_key.h new file mode 100644 index 0000000..c29500b --- /dev/null +++ b/cc/mac/aes_cmac_key.h
@@ -0,0 +1,88 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_MAC_AES_CMAC_KEY_H_ +#define TINK_MAC_AES_CMAC_KEY_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "absl/types/optional.h" +#include "tink/mac/aes_cmac_parameters.h" +#include "tink/mac/mac_key.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +class AesCmacKey : public MacKey { + public: + // Copyable and movable. + AesCmacKey(const AesCmacKey& other) = default; + AesCmacKey& operator=(const AesCmacKey& other) = default; + AesCmacKey(AesCmacKey&& other) = default; + AesCmacKey& operator=(AesCmacKey&& other) = default; + + // Creates a new AES-CMAC key. If the parameters specify a variant that uses + // a prefix, then the id is used to compute this prefix. + static util::StatusOr<AesCmacKey> Create(const AesCmacParameters& parameters, + const RestrictedData& key_bytes, + absl::optional<int> id_requirement, + PartialKeyAccessToken token); + + // Returns the underlying AES key. + util::StatusOr<RestrictedData> GetKeyBytes( + PartialKeyAccessToken token) const { + return key_bytes_; + } + + absl::string_view GetOutputPrefix() const override { return output_prefix_; } + + const AesCmacParameters& GetParameters() const override { + return parameters_; + } + + absl::optional<int> GetIdRequirement() const override { + return id_requirement_; + } + + bool operator==(const Key& other) const override; + + private: + AesCmacKey(const AesCmacParameters& parameters, + const RestrictedData& key_bytes, + absl::optional<int> id_requirement, std::string output_prefix) + : parameters_(parameters), + key_bytes_(key_bytes), + id_requirement_(id_requirement), + output_prefix_(std::move(output_prefix)) {} + + static util::StatusOr<std::string> ComputeOutputPrefix( + const AesCmacParameters& parameters, absl::optional<int> id_requirement); + + AesCmacParameters parameters_; + RestrictedData key_bytes_; + absl::optional<int> id_requirement_; + std::string output_prefix_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_MAC_AES_CMAC_KEY_H_
diff --git a/cc/mac/aes_cmac_key_manager.h b/cc/mac/aes_cmac_key_manager.h index 8617b46..7ee5a96 100644 --- a/cc/mac/aes_cmac_key_manager.h +++ b/cc/mac/aes_cmac_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_MAC_AES_CMAC_KEY_MANAGER_H_ #define TINK_MAC_AES_CMAC_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/mac/aes_cmac_key_test.cc b/cc/mac/aes_cmac_key_test.cc new file mode 100644 index 0000000..8bb87cf --- /dev/null +++ b/cc/mac/aes_cmac_key_test.cc
@@ -0,0 +1,253 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/aes_cmac_key.h" + +#include <memory> +#include <string> +#include <tuple> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/types/optional.h" +#include "tink/mac/aes_cmac_parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::Range; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + AesCmacParameters::Variant variant; + absl::optional<int> id_requirement; + std::string output_prefix; +}; + +using AesCmacKeyTest = TestWithParam<std::tuple<int, int, TestCase>>; + +INSTANTIATE_TEST_SUITE_P( + AesCmacKeyTestSuite, AesCmacKeyTest, + Combine(Values(16, 32), Range(10, 16), + Values(TestCase{AesCmacParameters::Variant::kTink, 0x02030400, + std::string("\x01\x02\x03\x04\x00", 5)}, + TestCase{AesCmacParameters::Variant::kCrunchy, 0x01030005, + std::string("\x00\x01\x03\x00\x05", 5)}, + TestCase{AesCmacParameters::Variant::kLegacy, 0x01020304, + std::string("\x00\x01\x02\x03\x04", 5)}, + TestCase{AesCmacParameters::Variant::kNoPrefix, + absl::nullopt, ""}))); + +TEST_P(AesCmacKeyTest, CreateSucceeds) { + int key_size; + int cryptographic_tag_size; + TestCase test_case; + std::tie(key_size, cryptographic_tag_size, test_case) = GetParam(); + + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + key_size, cryptographic_tag_size, test_case.variant); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + EXPECT_THAT(key->GetParameters(), Eq(*params)); + EXPECT_THAT(key->GetIdRequirement(), Eq(test_case.id_requirement)); + EXPECT_THAT(key->GetOutputPrefix(), Eq(test_case.output_prefix)); +} + +TEST(AesCmacKeyTest, CreateKeyWithMismatchedKeySizeFails) { + // Key size parameter is 32 bytes. + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + // Key material is 16 bytes (another valid key length). + RestrictedData mismatched_secret = RestrictedData(/*num_random_bytes=*/16); + + EXPECT_THAT(AesCmacKey::Create(*params, mismatched_secret, + /*id_requirement=*/123, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(AesCmacKeyTest, CreateKeyWithWrongIdRequirementFails) { + util::StatusOr<AesCmacParameters> no_prefix_params = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kNoPrefix); + ASSERT_THAT(no_prefix_params, IsOk()); + + util::StatusOr<AesCmacParameters> tink_params = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(tink_params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + EXPECT_THAT(AesCmacKey::Create(*no_prefix_params, secret, + /*id_requirement=*/123, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesCmacKey::Create(*tink_params, secret, + /*id_requirement=*/absl::nullopt, + GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesCmacKeyTest, GetAesCmacKey) { + int key_size; + int cryptographic_tag_size; + TestCase test_case; + std::tie(key_size, cryptographic_tag_size, test_case) = GetParam(); + + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + key_size, cryptographic_tag_size, test_case.variant); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + EXPECT_THAT(key->GetKeyBytes(GetPartialKeyAccess()), IsOkAndHolds(secret)); +} + +TEST_P(AesCmacKeyTest, KeyEquals) { + int key_size; + int cryptographic_tag_size; + TestCase test_case; + std::tie(key_size, cryptographic_tag_size, test_case) = GetParam(); + + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + key_size, cryptographic_tag_size, test_case.variant); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<AesCmacKey> other_key = AesCmacKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key == *other_key); + EXPECT_TRUE(*other_key == *key); + EXPECT_FALSE(*key != *other_key); + EXPECT_FALSE(*other_key != *key); +} + +TEST(AesCmacKeyTest, DifferentFormatNotEqual) { + util::StatusOr<AesCmacParameters> legacy_params = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kLegacy); + ASSERT_THAT(legacy_params, IsOk()); + + util::StatusOr<AesCmacParameters> tink_params = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(tink_params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr<AesCmacKey> key = + AesCmacKey::Create(*legacy_params, secret, /*id_requirement=*/0x01020304, + GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<AesCmacKey> other_key = + AesCmacKey::Create(*tink_params, secret, /*id_requirement=*/0x01020304, + GetPartialKeyAccess()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(AesCmacKeyTest, DifferentSecretDataNotEqual) { + util::StatusOr<AesCmacParameters> params = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret1 = RestrictedData(/*num_random_bytes=*/32); + RestrictedData secret2 = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *params, secret1, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<AesCmacKey> other_key = AesCmacKey::Create( + *params, secret2, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(AesCmacKeyTest, DifferentIdRequirementNotEqual) { + util::StatusOr<AesCmacParameters> params = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *params, secret, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<AesCmacKey> other_key = AesCmacKey::Create( + *params, secret, /*id_requirement=*/0x02030405, GetPartialKeyAccess()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/aes_cmac_parameters.cc b/cc/mac/aes_cmac_parameters.cc index c4d8429..4064d2f 100644 --- a/cc/mac/aes_cmac_parameters.cc +++ b/cc/mac/aes_cmac_parameters.cc
@@ -22,7 +22,10 @@ #include <ostream> #include <set> +#include "absl/log/log.h" +#include "absl/strings/str_cat.h" #include "tink/crypto_format.h" +#include "tink/internal/util.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -30,7 +33,14 @@ namespace tink { util::StatusOr<AesCmacParameters> AesCmacParameters::Create( - int cryptographic_tag_size_in_bytes, Variant variant) { + int key_size_in_bytes, int cryptographic_tag_size_in_bytes, + Variant variant) { + if (key_size_in_bytes != 16 && key_size_in_bytes != 32) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Key size should be either 16 or 32 bytes, got ", + key_size_in_bytes, " bytes.")); + } if (cryptographic_tag_size_in_bytes < 10) { return util::Status( absl::StatusCode::kInvalidArgument, @@ -51,7 +61,8 @@ absl::StatusCode::kInvalidArgument, "Cannot create AES-CMAC parameters with unknown variant."); } - return AesCmacParameters(cryptographic_tag_size_in_bytes, variant); + return AesCmacParameters(key_size_in_bytes, cryptographic_tag_size_in_bytes, + variant); } int AesCmacParameters::TotalTagSizeInBytes() const { @@ -64,8 +75,7 @@ return CryptographicTagSizeInBytes(); default: // Parameters objects with unknown variants should never be created. - std::cerr << "AES-CMAC parameters has an unknown variant." << std::endl; - std::exit(1); + internal::LogFatal("AES-CMAC parameters has an unknown variant."); } } @@ -75,9 +85,17 @@ if (that == nullptr) { return false; } - return cryptographic_tag_size_in_bytes_ == - that->cryptographic_tag_size_in_bytes_ && - variant_ == that->variant_; + if (key_size_in_bytes_ != that->key_size_in_bytes_) { + return false; + } + if (cryptographic_tag_size_in_bytes_ != + that->cryptographic_tag_size_in_bytes_) { + return false; + } + if (variant_ != that->variant_) { + return false; + } + return true; } } // namespace tink
diff --git a/cc/mac/aes_cmac_parameters.h b/cc/mac/aes_cmac_parameters.h index a4a09a7..06a560a 100644 --- a/cc/mac/aes_cmac_parameters.h +++ b/cc/mac/aes_cmac_parameters.h
@@ -53,14 +53,18 @@ AesCmacParameters(AesCmacParameters&& other) = default; AesCmacParameters& operator=(AesCmacParameters&& other) = default; - // Creates a new AES-CMAC parameters object. Returns an error status if - // `cryptographic_tag_size_in_bytes` falls outside [10,...,16]. Otherwise, - // returns the parameters object. + // Creates a new AES-CMAC parameters object unless an error occurs. An error + // occurs under one of the following conditions: + // 1. `key_size_in_bytes` is a value other than 16 or 32 + // 2. `cryptographic_tag_size_in_bytes` falls outside [10,...,16] static util::StatusOr<AesCmacParameters> Create( - int cryptographic_tag_size_in_bytes, Variant variant); + int key_size_in_bytes, int cryptographic_tag_size_in_bytes, + Variant variant); Variant GetVariant() const { return variant_; } + int KeySizeInBytes() const { return key_size_in_bytes_; } + // Returns the size of the tag, which is computed cryptographically from the // message. Note that this may differ from the total size of the tag, as for // some keys, Tink prefixes the tag with a key dependent output prefix. @@ -79,10 +83,13 @@ bool operator==(const Parameters& other) const override; private: - AesCmacParameters(int cryptographic_tag_size_in_bytes, Variant variant) - : cryptographic_tag_size_in_bytes_(cryptographic_tag_size_in_bytes), + AesCmacParameters(int key_size_in_bytes, int cryptographic_tag_size_in_bytes, + Variant variant) + : key_size_in_bytes_(key_size_in_bytes), + cryptographic_tag_size_in_bytes_(cryptographic_tag_size_in_bytes), variant_(variant) {} + int key_size_in_bytes_; int cryptographic_tag_size_in_bytes_; Variant variant_; };
diff --git a/cc/mac/aes_cmac_parameters_test.cc b/cc/mac/aes_cmac_parameters_test.cc index db1f2c3..7284b7c 100644 --- a/cc/mac/aes_cmac_parameters_test.cc +++ b/cc/mac/aes_cmac_parameters_test.cc
@@ -38,38 +38,48 @@ struct CreateTestCase { AesCmacParameters::Variant variant; - int cryptographic_tag_size_in_bytes; - int total_tag_size_in_bytes; + int key_size; + int cryptographic_tag_size; + int total_tag_size; bool has_id_requirement; }; -class AesCmacParametersCreateTest : public TestWithParam<CreateTestCase> {}; +using AesCmacParametersCreateTest = TestWithParam<CreateTestCase>; INSTANTIATE_TEST_SUITE_P( AesCmacParametersCreateTestSuite, AesCmacParametersCreateTest, - Values(CreateTestCase{AesCmacParameters::Variant::kTink, 10, 15, true}, - CreateTestCase{AesCmacParameters::Variant::kCrunchy, 12, 17, true}, - CreateTestCase{AesCmacParameters::Variant::kLegacy, 14, 19, true}, - CreateTestCase{AesCmacParameters::Variant::kNoPrefix, 16, 16, - false})); + Values(CreateTestCase{AesCmacParameters::Variant::kTink, /*key_size=*/16, + /*cryptographic_tag_size=*/10, /*total_tag_size=*/15, + /*has_id_requirement=*/true}, + CreateTestCase{AesCmacParameters::Variant::kCrunchy, /*key_size=*/16, + /*cryptographic_tag_size=*/12, /*total_tag_size=*/17, + /*has_id_requirement=*/true}, + CreateTestCase{AesCmacParameters::Variant::kLegacy, /*key_size=*/32, + /*cryptographic_tag_size=*/14, /*total_tag_size=*/19, + /*has_id_requirement=*/true}, + CreateTestCase{AesCmacParameters::Variant::kNoPrefix, + /*key_size=*/32, /*cryptographic_tag_size=*/16, + /*total_tag_size=*/16, + /*has_id_requirement=*/false})); TEST_P(AesCmacParametersCreateTest, Create) { CreateTestCase test_case = GetParam(); util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create( - test_case.cryptographic_tag_size_in_bytes, test_case.variant); + test_case.key_size, test_case.cryptographic_tag_size, test_case.variant); ASSERT_THAT(parameters, IsOk()); EXPECT_THAT(parameters->GetVariant(), Eq(test_case.variant)); + EXPECT_THAT(parameters->KeySizeInBytes(), Eq(test_case.key_size)); EXPECT_THAT(parameters->CryptographicTagSizeInBytes(), - Eq(test_case.cryptographic_tag_size_in_bytes)); - EXPECT_THAT(parameters->TotalTagSizeInBytes(), - Eq(test_case.total_tag_size_in_bytes)); + Eq(test_case.cryptographic_tag_size)); + EXPECT_THAT(parameters->TotalTagSizeInBytes(), Eq(test_case.total_tag_size)); EXPECT_THAT(parameters->HasIdRequirement(), Eq(test_case.has_id_requirement)); } TEST(AesCmacParametersTest, CreateWithInvalidVariantFails) { EXPECT_THAT(AesCmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/12, AesCmacParameters::Variant:: kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements) @@ -77,39 +87,69 @@ StatusIs(absl::StatusCode::kInvalidArgument)); } +TEST(AesCmacParametersTest, CreateWithInvalidKeySizeFails) { + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/15, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/17, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/31, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/33, + /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + TEST(AesCmacParametersTest, CreateWithInvalidTagSizeFails) { // Too small. - EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/7, + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/7, AesCmacParameters::Variant::kNoPrefix) .status(), StatusIs(absl::StatusCode::kInvalidArgument)); - EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/8, + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/8, AesCmacParameters::Variant::kNoPrefix) .status(), StatusIs(absl::StatusCode::kInvalidArgument)); - EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/9, + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/9, AesCmacParameters::Variant::kNoPrefix) .status(), StatusIs(absl::StatusCode::kInvalidArgument)); // Too big; - EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/17, + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/17, AesCmacParameters::Variant::kNoPrefix) .status(), StatusIs(absl::StatusCode::kInvalidArgument)); - EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/18, + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/18, AesCmacParameters::Variant::kNoPrefix) .status(), StatusIs(absl::StatusCode::kInvalidArgument)); - EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/19, + EXPECT_THAT(AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/19, AesCmacParameters::Variant::kNoPrefix) .status(), StatusIs(absl::StatusCode::kInvalidArgument)); } TEST(AesCmacParametersTest, CopyConstructor) { - util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create( - /*cryptographic_tag_size_in_bytes=*/12, - AesCmacParameters::Variant::kTink); + util::StatusOr<AesCmacParameters> parameters = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/12, + AesCmacParameters::Variant::kTink); ASSERT_THAT(parameters, IsOk()); AesCmacParameters copy(*parameters); @@ -122,9 +162,10 @@ } TEST(AesCmacParametersTest, CopyAssignment) { - util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create( - /*cryptographic_tag_size_in_bytes=*/12, - AesCmacParameters::Variant::kTink); + util::StatusOr<AesCmacParameters> parameters = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/12, + AesCmacParameters::Variant::kTink); ASSERT_THAT(parameters, IsOk()); AesCmacParameters copy = *parameters; @@ -136,28 +177,29 @@ EXPECT_THAT(copy.HasIdRequirement(), Eq(parameters->HasIdRequirement())); } -class AesCmacParametersVariantTest - : public TestWithParam<std::tuple<AesCmacParameters::Variant, int>> {}; +using AesCmacParametersVariantTest = + TestWithParam<std::tuple<int, int, AesCmacParameters::Variant>>; -INSTANTIATE_TEST_SUITE_P(AesCmacParametersVariantTestSuite, - AesCmacParametersVariantTest, - Combine(Values(AesCmacParameters::Variant::kTink, - AesCmacParameters::Variant::kCrunchy, - AesCmacParameters::Variant::kLegacy, - AesCmacParameters::Variant::kNoPrefix), - Range(10, 16))); +INSTANTIATE_TEST_SUITE_P( + AesCmacParametersVariantTestSuite, AesCmacParametersVariantTest, + Combine(Values(16, 32), Range(10, 16), + Values(AesCmacParameters::Variant::kTink, + AesCmacParameters::Variant::kCrunchy, + AesCmacParameters::Variant::kLegacy, + AesCmacParameters::Variant::kNoPrefix))); TEST_P(AesCmacParametersVariantTest, ParametersEquals) { - AesCmacParameters::Variant variant; + int key_size; int cryptographic_tag_size; - std::tie(variant, cryptographic_tag_size) = GetParam(); + AesCmacParameters::Variant variant; + std::tie(key_size, cryptographic_tag_size, variant) = GetParam(); util::StatusOr<AesCmacParameters> parameters = - AesCmacParameters::Create(cryptographic_tag_size, variant); + AesCmacParameters::Create(key_size, cryptographic_tag_size, variant); ASSERT_THAT(parameters, IsOk()); util::StatusOr<AesCmacParameters> other_parameters = - AesCmacParameters::Create(cryptographic_tag_size, variant); + AesCmacParameters::Create(key_size, cryptographic_tag_size, variant); ASSERT_THAT(other_parameters, IsOk()); EXPECT_TRUE(*parameters == *other_parameters); @@ -166,16 +208,34 @@ EXPECT_FALSE(*other_parameters != *parameters); } -TEST(AesCmacParametersTest, TagSizeNotEqual) { - util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create( - /*cryptographic_tag_size_in_bytes=*/10, - AesCmacParameters::Variant::kNoPrefix); +TEST(AesCmacParametersTest, KeySizeNotEqual) { + util::StatusOr<AesCmacParameters> parameters = + AesCmacParameters::Create(/*key_size_in_bytes=*/16, + /*cryptographic_tag_size_in_bytes=*/10, + AesCmacParameters::Variant::kNoPrefix); ASSERT_THAT(parameters, IsOk()); util::StatusOr<AesCmacParameters> other_parameters = - AesCmacParameters::Create( - /*cryptographic_tag_size_in_bytes=*/11, - AesCmacParameters::Variant::kNoPrefix); + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, + AesCmacParameters::Variant::kNoPrefix); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +TEST(AesCmacParametersTest, TagSizeNotEqual) { + util::StatusOr<AesCmacParameters> parameters = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, + AesCmacParameters::Variant::kNoPrefix); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<AesCmacParameters> other_parameters = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/11, + AesCmacParameters::Variant::kNoPrefix); ASSERT_THAT(other_parameters, IsOk()); EXPECT_TRUE(*parameters != *other_parameters); @@ -183,15 +243,16 @@ } TEST(AesCmacParametersTest, VariantNotEqual) { - util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create( - /*cryptographic_tag_size_in_bytes=*/10, - AesCmacParameters::Variant::kNoPrefix); + util::StatusOr<AesCmacParameters> parameters = + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, + AesCmacParameters::Variant::kNoPrefix); ASSERT_THAT(parameters, IsOk()); util::StatusOr<AesCmacParameters> other_parameters = - AesCmacParameters::Create( - /*cryptographic_tag_size_in_bytes=*/10, - AesCmacParameters::Variant::kTink); + AesCmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, + AesCmacParameters::Variant::kTink); ASSERT_THAT(other_parameters, IsOk()); EXPECT_TRUE(*parameters != *other_parameters);
diff --git a/cc/mac/aes_cmac_proto_serialization.cc b/cc/mac/aes_cmac_proto_serialization.cc new file mode 100644 index 0000000..5c7b77b --- /dev/null +++ b/cc/mac/aes_cmac_proto_serialization.cc
@@ -0,0 +1,248 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/aes_cmac_proto_serialization.h" + +#include <string> + +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/mac/aes_cmac_key.h" +#include "tink/mac/aes_cmac_parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/aes_cmac.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::google::crypto::tink::AesCmacKeyFormat; +using ::google::crypto::tink::AesCmacParams; +using ::google::crypto::tink::OutputPrefixType; + +using AesCmacProtoParametersParserImpl = + internal::ParametersParserImpl<internal::ProtoParametersSerialization, + AesCmacParameters>; +using AesCmacProtoParametersSerializerImpl = + internal::ParametersSerializerImpl<AesCmacParameters, + internal::ProtoParametersSerialization>; +using AesCmacProtoKeyParserImpl = + internal::KeyParserImpl<internal::ProtoKeySerialization, AesCmacKey>; +using AesCmacProtoKeySerializerImpl = + internal::KeySerializerImpl<AesCmacKey, internal::ProtoKeySerialization>; + +const absl::string_view kTypeUrl = + "type.googleapis.com/google.crypto.tink.AesCmacKey"; + +util::StatusOr<AesCmacParameters::Variant> ToVariant( + OutputPrefixType output_prefix_type) { + switch (output_prefix_type) { + case OutputPrefixType::CRUNCHY: + return AesCmacParameters::Variant::kCrunchy; + case OutputPrefixType::LEGACY: + return AesCmacParameters::Variant::kLegacy; + case OutputPrefixType::RAW: + return AesCmacParameters::Variant::kNoPrefix; + case OutputPrefixType::TINK: + return AesCmacParameters::Variant::kTink; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine AesCmacParameters::Variant"); + } +} + +util::StatusOr<OutputPrefixType> ToOutputPrefixType( + AesCmacParameters::Variant variant) { + switch (variant) { + case AesCmacParameters::Variant::kCrunchy: + return OutputPrefixType::CRUNCHY; + case AesCmacParameters::Variant::kLegacy: + return OutputPrefixType::LEGACY; + case AesCmacParameters::Variant::kNoPrefix: + return OutputPrefixType::RAW; + case AesCmacParameters::Variant::kTink: + return OutputPrefixType::TINK; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine output prefix type"); + } +} + +util::StatusOr<AesCmacParameters> ParseParameters( + const internal::ProtoParametersSerialization& serialization) { + if (serialization.GetKeyTemplate().type_url() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing AesCmacParameters."); + } + + AesCmacKeyFormat proto_key_format; + if (!proto_key_format.ParseFromString( + serialization.GetKeyTemplate().value())) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse AesCmacKeyFormat proto"); + } + + util::StatusOr<AesCmacParameters::Variant> variant = + ToVariant(serialization.GetKeyTemplate().output_prefix_type()); + if (!variant.ok()) return variant.status(); + + return AesCmacParameters::Create(proto_key_format.key_size(), + proto_key_format.params().tag_size(), + *variant); +} + +util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters( + const AesCmacParameters& parameters) { + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(parameters.GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + + AesCmacParams proto_params; + proto_params.set_tag_size(parameters.CryptographicTagSizeInBytes()); + AesCmacKeyFormat proto_key_format; + proto_key_format.set_key_size(parameters.KeySizeInBytes()); + *proto_key_format.mutable_params() = proto_params; + + return internal::ProtoParametersSerialization::Create( + kTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString()); +} + +util::StatusOr<AesCmacKey> ParseKey( + const internal::ProtoKeySerialization& serialization, + absl::optional<SecretKeyAccessToken> token) { + if (serialization.TypeUrl() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing AesCmacKey."); + } + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + google::crypto::tink::AesCmacKey proto_key; + RestrictedData restricted_data = serialization.SerializedKeyProto(); + // OSS proto library complains if input is not converted to a string. + if (!proto_key.ParseFromString( + std::string(restricted_data.GetSecret(*token)))) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse AesCmacKey proto"); + } + if (proto_key.version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + util::StatusOr<AesCmacParameters::Variant> variant = + ToVariant(serialization.GetOutputPrefixType()); + if (!variant.ok()) return variant.status(); + + util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create( + proto_key.key_value().length(), proto_key.params().tag_size(), *variant); + if (!parameters.ok()) return parameters.status(); + + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *parameters, RestrictedData(proto_key.key_value(), *token), + serialization.IdRequirement(), GetPartialKeyAccess()); + if (!key.ok()) return key.status(); + + return *key; +} + +util::StatusOr<internal::ProtoKeySerialization> SerializeKey( + const AesCmacKey& key, absl::optional<SecretKeyAccessToken> token) { + util::StatusOr<RestrictedData> restricted_input = + key.GetKeyBytes(GetPartialKeyAccess()); + if (!restricted_input.ok()) return restricted_input.status(); + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + + AesCmacParams proto_params; + proto_params.set_tag_size(key.GetParameters().CryptographicTagSizeInBytes()); + google::crypto::tink::AesCmacKey proto_key; + *proto_key.mutable_params() = proto_params; + proto_key.set_version(0); + // OSS proto library complains if input is not converted to a string. + proto_key.set_key_value(std::string(restricted_input->GetSecret(*token))); + + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(key.GetParameters().GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + + RestrictedData restricted_output = + RestrictedData(proto_key.SerializeAsString(), *token); + return internal::ProtoKeySerialization::Create( + kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC, + *output_prefix_type, key.GetIdRequirement()); +} + +AesCmacProtoParametersParserImpl* AesCmacProtoParametersParser() { + static auto* parser = + new AesCmacProtoParametersParserImpl(kTypeUrl, ParseParameters); + return parser; +} + +AesCmacProtoParametersSerializerImpl* AesCmacProtoParametersSerializer() { + static auto* serializer = + new AesCmacProtoParametersSerializerImpl(kTypeUrl, SerializeParameters); + return serializer; +} + +AesCmacProtoKeyParserImpl* AesCmacProtoKeyParser() { + static auto* parser = new AesCmacProtoKeyParserImpl(kTypeUrl, ParseKey); + return parser; +} + +AesCmacProtoKeySerializerImpl* AesCmacProtoKeySerializer() { + static auto* serializer = new AesCmacProtoKeySerializerImpl(SerializeKey); + return serializer; +} + +} // namespace + +util::Status RegisterAesCmacProtoSerialization() { + util::Status status = + internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersParser(AesCmacProtoParametersParser()); + if (!status.ok()) return status; + + status = + internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersSerializer(AesCmacProtoParametersSerializer()); + if (!status.ok()) return status; + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeyParser(AesCmacProtoKeyParser()); + if (!status.ok()) return status; + + return internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeySerializer(AesCmacProtoKeySerializer()); +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/aes_cmac_proto_serialization.h b/cc/mac/aes_cmac_proto_serialization.h new file mode 100644 index 0000000..d34dcb6 --- /dev/null +++ b/cc/mac/aes_cmac_proto_serialization.h
@@ -0,0 +1,31 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_MAC_AES_CMAC_PROTO_SERIALIZATION_H_ +#define TINK_MAC_AES_CMAC_PROTO_SERIALIZATION_H_ + +#include "tink/util/status.h" + +namespace crypto { +namespace tink { + +// Registers proto parsers and serializers for AES-CMAC parameters and keys. +crypto::tink::util::Status RegisterAesCmacProtoSerialization(); + +} // namespace tink +} // namespace crypto + +#endif // TINK_MAC_AES_CMAC_PROTO_SERIALIZATION_H_
diff --git a/cc/mac/aes_cmac_proto_serialization_test.cc b/cc/mac/aes_cmac_proto_serialization_test.cc new file mode 100644 index 0000000..cda1093 --- /dev/null +++ b/cc/mac/aes_cmac_proto_serialization_test.cc
@@ -0,0 +1,373 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/aes_cmac_proto_serialization.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/mac/aes_cmac_key.h" +#include "tink/mac/aes_cmac_parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/subtle/random.h" +#include "tink/util/test_matchers.h" +#include "proto/aes_cmac.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::subtle::Random; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesCmacKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::NotNull; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + AesCmacParameters::Variant variant; + OutputPrefixType output_prefix_type; + int key_size; + int tag_size; + int total_size; + absl::optional<int> id; + std::string output_prefix; +}; + +class AesCmacProtoSerializationTest : public TestWithParam<TestCase> { + protected: + void SetUp() override { + internal::MutableSerializationRegistry::GlobalInstance().Reset(); + } +}; + +INSTANTIATE_TEST_SUITE_P( + AesCmacProtoSerializationTestSuite, AesCmacProtoSerializationTest, + Values(TestCase{AesCmacParameters::Variant::kTink, OutputPrefixType::TINK, + /*key_size=*/16, /*tag_size=*/10, /*total_size=*/15, + /*id=*/0x02030400, + /*output_prefix=*/std::string("\x01\x02\x03\x04\x00", 5)}, + TestCase{AesCmacParameters::Variant::kCrunchy, + OutputPrefixType::CRUNCHY, /*key_size=*/16, + /*tag_size=*/12, /*total_size=*/17, /*id=*/0x01030005, + /*output_prefix=*/std::string("\x00\x01\x03\x00\x05", 5)}, + TestCase{AesCmacParameters::Variant::kLegacy, + OutputPrefixType::LEGACY, /*key_size=*/32, + /*cryptographic_tag_size=*/14, /*total_tag_size=*/19, + /*id=*/0x01020304, + /*output_prefix=*/std::string("\x00\x01\x02\x03\x04", 5)}, + TestCase{AesCmacParameters::Variant::kNoPrefix, + OutputPrefixType::RAW, /*key_size=*/32, + /*cryptographic_tag_size=*/16, /*total_tag_size=*/16, + /*id=*/absl::nullopt, /*output_prefix=*/""})); + +TEST_P(AesCmacProtoSerializationTest, ParseParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + AesCmacKeyFormat key_format_proto; + key_format_proto.set_key_size(test_case.key_size); + key_format_proto.mutable_params()->set_tag_size(test_case.tag_size); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesCmacKey", + test_case.output_prefix_type, key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(params, IsOk()); + EXPECT_THAT((*params)->HasIdRequirement(), test_case.id.has_value()); + + const AesCmacParameters* cmac_params = + dynamic_cast<const AesCmacParameters*>(params->get()); + ASSERT_THAT(cmac_params, NotNull()); + EXPECT_THAT(cmac_params->GetVariant(), Eq(test_case.variant)); + EXPECT_THAT(cmac_params->KeySizeInBytes(), Eq(test_case.key_size)); + EXPECT_THAT(cmac_params->CryptographicTagSizeInBytes(), + Eq(test_case.tag_size)); + EXPECT_THAT(cmac_params->TotalTagSizeInBytes(), Eq(test_case.total_size)); +} + +TEST_F(AesCmacProtoSerializationTest, ParseParametersWithInvalidSerialization) { + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + AesCmacKeyFormat key_format_proto; + key_format_proto.set_key_size(16); + key_format_proto.mutable_params()->set_tag_size(10); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesCmacKey", + OutputPrefixType::RAW, "invalid_serialization"); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesCmacProtoSerializationTest, ParseParametersWithUnkownOutputPrefix) { + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + AesCmacKeyFormat key_format_proto; + key_format_proto.set_key_size(16); + key_format_proto.mutable_params()->set_tag_size(10); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesCmacKey", + OutputPrefixType::UNKNOWN_PREFIX, + key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesCmacProtoSerializationTest, SerializeParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create( + test_case.key_size, test_case.tag_size, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>( + *parameters); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.AesCmacKey")); + + const internal::ProtoParametersSerialization* proto_serialization = + dynamic_cast<const internal::ProtoParametersSerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->GetKeyTemplate().type_url(), + Eq("type.googleapis.com/google.crypto.tink.AesCmacKey")); + EXPECT_THAT(proto_serialization->GetKeyTemplate().output_prefix_type(), + Eq(test_case.output_prefix_type)); + + AesCmacKeyFormat key_format; + ASSERT_THAT( + key_format.ParseFromString(proto_serialization->GetKeyTemplate().value()), + IsTrue()); + ASSERT_THAT(key_format.key_size(), Eq(test_case.key_size)); + ASSERT_THAT(key_format.params().tag_size(), Eq(test_case.tag_size)); +} + +TEST_P(AesCmacProtoSerializationTest, ParseKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + google::crypto::tink::AesCmacKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_tag_size(test_case.tag_size); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesCmacKey", serialized_key, + KeyData::SYMMETRIC, test_case.output_prefix_type, test_case.id); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(test_case.id)); + EXPECT_THAT((*key)->GetParameters().HasIdRequirement(), + test_case.id.has_value()); + + const AesCmacKey* cmac_key = dynamic_cast<const AesCmacKey*>(key->get()); + ASSERT_THAT(cmac_key, NotNull()); + util::StatusOr<RestrictedData> parsed_key = + cmac_key->GetKeyBytes(GetPartialKeyAccess()); + ASSERT_THAT(parsed_key, IsOk()); + EXPECT_THAT(parsed_key->GetSecret(InsecureSecretKeyAccess::Get()), + Eq(raw_key_bytes)); + EXPECT_THAT(cmac_key->GetOutputPrefix(), Eq(test_case.output_prefix)); + EXPECT_THAT(cmac_key->GetParameters().GetVariant(), Eq(test_case.variant)); + EXPECT_THAT(cmac_key->GetParameters().KeySizeInBytes(), + Eq(test_case.key_size)); + EXPECT_THAT(cmac_key->GetParameters().CryptographicTagSizeInBytes(), + Eq(test_case.tag_size)); + EXPECT_THAT(cmac_key->GetParameters().TotalTagSizeInBytes(), + test_case.total_size); + EXPECT_THAT(cmac_key->GetParameters().HasIdRequirement(), + test_case.id.has_value()); +} + +TEST_F(AesCmacProtoSerializationTest, ParseKeyWithInvalidSerialization) { + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + RestrictedData serialized_key = + RestrictedData("invalid_serialization", InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesCmacKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesCmacProtoSerializationTest, ParseKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::AesCmacKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_tag_size(10); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesCmacKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, absl::nullopt); + ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesCmacProtoSerializationTest, ParseKeyWithInvalidVersion) { + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::AesCmacKey key_proto; + key_proto.set_version(1); // Invalid version number. + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_tag_size(10); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesCmacKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesCmacProtoSerializationTest, SerializeKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create( + test_case.key_size, test_case.tag_size, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.AesCmacKey")); + + const internal::ProtoKeySerialization* proto_serialization = + dynamic_cast<const internal::ProtoKeySerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->TypeUrl(), + Eq("type.googleapis.com/google.crypto.tink.AesCmacKey")); + EXPECT_THAT(proto_serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(proto_serialization->GetOutputPrefixType(), + Eq(test_case.output_prefix_type)); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(test_case.id)); + + google::crypto::tink::AesCmacKey proto_key; + // OSS proto library complains if input is not converted to a string. + ASSERT_THAT(proto_key.ParseFromString(std::string( + proto_serialization->SerializedKeyProto().GetSecret( + InsecureSecretKeyAccess::Get()))), + IsTrue()); + EXPECT_THAT(proto_key.key_value().size(), Eq(test_case.key_size)); + EXPECT_THAT(proto_key.params().tag_size(), Eq(test_case.tag_size)); +} + +TEST_F(AesCmacProtoSerializationTest, SerializeKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterAesCmacProtoSerialization(), IsOk()); + + util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create( + /*key_size_in_bytes=*/16, /*cryptographic_tag_size_in_bytes=*/10, + AesCmacParameters::Variant::kNoPrefix); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + util::StatusOr<AesCmacKey> key = AesCmacKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>(*key, absl::nullopt); + ASSERT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/failing_mac.cc b/cc/mac/failing_mac.cc index 999bb83..5a1b49c 100644 --- a/cc/mac/failing_mac.cc +++ b/cc/mac/failing_mac.cc
@@ -15,6 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/mac/failing_mac.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/mac/failing_mac.h b/cc/mac/failing_mac.h index 4eed37d..84bde37 100644 --- a/cc/mac/failing_mac.h +++ b/cc/mac/failing_mac.h
@@ -16,6 +16,7 @@ #ifndef TINK_MAC_FAILING_MAC_H_ #define TINK_MAC_FAILING_MAC_H_ +#include <memory> #include <string> #include "absl/strings/string_view.h"
diff --git a/cc/mac/hmac_key.cc b/cc/mac/hmac_key.cc new file mode 100644 index 0000000..388dc31 --- /dev/null +++ b/cc/mac/hmac_key.cc
@@ -0,0 +1,109 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/hmac_key.h" + +#include <memory> +#include <string> + +#include "absl/base/attributes.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_format.h" +#include "absl/types/optional.h" +#include "tink/mac/hmac_parameters.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/subtle/subtle_util.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +util::StatusOr<HmacKey> HmacKey::Create(const HmacParameters& parameters, + const RestrictedData& key_bytes, + absl::optional<int> id_requirement, + PartialKeyAccessToken token) { + if (parameters.KeySizeInBytes() != key_bytes.size()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Key size does not match HMAC parameters"); + } + if (parameters.HasIdRequirement() && !id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key without ID requirement with parameters with ID " + "requirement"); + } + if (!parameters.HasIdRequirement() && id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key with ID requirement with parameters without ID " + "requirement"); + } + util::StatusOr<std::string> output_prefix = + ComputeOutputPrefix(parameters, id_requirement); + if (!output_prefix.ok()) { + return output_prefix.status(); + } + return HmacKey(parameters, key_bytes, id_requirement, + *std::move(output_prefix)); +} + +util::StatusOr<std::string> HmacKey::ComputeOutputPrefix( + const HmacParameters& parameters, absl::optional<int> id_requirement) { + switch (parameters.GetVariant()) { + case HmacParameters::Variant::kNoPrefix: + return std::string(""); // Empty prefix. + case HmacParameters::Variant::kLegacy: + ABSL_FALLTHROUGH_INTENDED; + case HmacParameters::Variant::kCrunchy: + if (!id_requirement.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "id requirement must have value with kCrunchy or kLegacy"); + } + return absl::StrCat(absl::HexStringToBytes("00"), + subtle::BigEndian32(*id_requirement)); + case HmacParameters::Variant::kTink: + if (!id_requirement.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "id requirement must have value with kTink"); + } + return absl::StrCat(absl::HexStringToBytes("01"), + subtle::BigEndian32(*id_requirement)); + default: + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Invalid variant: ", parameters.GetVariant())); + } +} + +bool HmacKey::operator==(const Key& other) const { + const HmacKey* that = dynamic_cast<const HmacKey*>(&other); + if (that == nullptr) { + return false; + } + if (GetParameters() != that->GetParameters()) { + return false; + } + if (id_requirement_ != that->id_requirement_) { + return false; + } + return key_bytes_ == that->key_bytes_; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/hmac_key.h b/cc/mac/hmac_key.h new file mode 100644 index 0000000..30f6bf5 --- /dev/null +++ b/cc/mac/hmac_key.h
@@ -0,0 +1,85 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_MAC_HMAC_KEY_H_ +#define TINK_MAC_HMAC_KEY_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "absl/types/optional.h" +#include "tink/mac/hmac_parameters.h" +#include "tink/mac/mac_key.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +class HmacKey : public MacKey { + public: + // Copyable and movable. + HmacKey(const HmacKey& other) = default; + HmacKey& operator=(const HmacKey& other) = default; + HmacKey(HmacKey&& other) = default; + HmacKey& operator=(HmacKey&& other) = default; + + // Creates a new HMAC key. If the parameters specify a variant that uses + // a prefix, then the id is used to compute this prefix. + static util::StatusOr<HmacKey> Create(const HmacParameters& parameters, + const RestrictedData& key_bytes, + absl::optional<int> id_requirement, + PartialKeyAccessToken token); + + // Returns the underlying HMAC key bytes. + util::StatusOr<RestrictedData> GetKeyBytes( + PartialKeyAccessToken token) const { + return key_bytes_; + } + + absl::string_view GetOutputPrefix() const override { return output_prefix_; } + + const HmacParameters& GetParameters() const override { return parameters_; } + + absl::optional<int> GetIdRequirement() const override { + return id_requirement_; + } + + bool operator==(const Key& other) const override; + + private: + HmacKey(const HmacParameters& parameters, const RestrictedData& key_bytes, + absl::optional<int> id_requirement, std::string output_prefix) + : parameters_(parameters), + key_bytes_(key_bytes), + id_requirement_(id_requirement), + output_prefix_(std::move(output_prefix)) {} + + static util::StatusOr<std::string> ComputeOutputPrefix( + const HmacParameters& parameters, absl::optional<int> id_requirement); + + HmacParameters parameters_; + RestrictedData key_bytes_; + absl::optional<int> id_requirement_; + std::string output_prefix_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_MAC_HMAC_KEY_H_
diff --git a/cc/mac/hmac_key_manager.h b/cc/mac/hmac_key_manager.h index b696a53..0dc34a9 100644 --- a/cc/mac/hmac_key_manager.h +++ b/cc/mac/hmac_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_MAC_HMAC_KEY_MANAGER_H_ #define TINK_MAC_HMAC_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/mac/hmac_key_manager_test.cc b/cc/mac/hmac_key_manager_test.cc index db17da7..7367623 100644 --- a/cc/mac/hmac_key_manager_test.cc +++ b/cc/mac/hmac_key_manager_test.cc
@@ -17,13 +17,14 @@ #include "tink/mac/hmac_key_manager.h" #include <memory> +#include <sstream> #include <string> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/status/status.h" -#include "tink/core/key_manager_impl.h" #include "tink/chunked_mac.h" +#include "tink/core/key_manager_impl.h" #include "tink/mac.h" #include "tink/util/istream_input_stream.h" #include "tink/util/secret_data.h" @@ -42,7 +43,6 @@ using ::google::crypto::tink::HashType; using ::google::crypto::tink::HmacKey; using ::google::crypto::tink::HmacKeyFormat; -using ::google::crypto::tink::KeyData; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::Not;
diff --git a/cc/mac/hmac_key_test.cc b/cc/mac/hmac_key_test.cc new file mode 100644 index 0000000..e35aca7 --- /dev/null +++ b/cc/mac/hmac_key_test.cc
@@ -0,0 +1,255 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/hmac_key.h" + +#include <memory> +#include <string> +#include <tuple> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/types/optional.h" +#include "tink/mac/hmac_parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::Range; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + HmacParameters::Variant variant; + absl::optional<int> id_requirement; + std::string output_prefix; +}; + +using HmacKeyTest = + TestWithParam<std::tuple<int, int, HmacParameters::HashType, TestCase>>; + +INSTANTIATE_TEST_SUITE_P( + HmacKeyTestSuite, HmacKeyTest, + Combine(Values(16, 32), Range(10, 20), + Values(HmacParameters::HashType::kSha1, + HmacParameters::HashType::kSha224, + HmacParameters::HashType::kSha256, + HmacParameters::HashType::kSha384, + HmacParameters::HashType::kSha512), + Values(TestCase{HmacParameters::Variant::kTink, 0x02030400, + std::string("\x01\x02\x03\x04\x00", 5)}, + TestCase{HmacParameters::Variant::kCrunchy, 0x01030005, + std::string("\x00\x01\x03\x00\x05", 5)}, + TestCase{HmacParameters::Variant::kLegacy, 0x01020304, + std::string("\x00\x01\x02\x03\x04", 5)}, + TestCase{HmacParameters::Variant::kNoPrefix, absl::nullopt, + ""}))); + +TEST_P(HmacKeyTest, CreateSucceeds) { + int key_size; + int cryptographic_tag_size; + HmacParameters::HashType hash_type; + TestCase test_case; + std::tie(key_size, cryptographic_tag_size, hash_type, test_case) = GetParam(); + + util::StatusOr<HmacParameters> params = HmacParameters::Create( + key_size, cryptographic_tag_size, hash_type, test_case.variant); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + util::StatusOr<HmacKey> key = HmacKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + EXPECT_THAT(key->GetParameters(), Eq(*params)); + EXPECT_THAT(key->GetIdRequirement(), Eq(test_case.id_requirement)); + EXPECT_THAT(key->GetOutputPrefix(), Eq(test_case.output_prefix)); +} + +TEST(HmacKeyTest, CreateKeyWithMismatchedKeySizeFails) { + // Key size parameter is 32 bytes. + util::StatusOr<HmacParameters> params = HmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + HmacParameters::HashType::kSha256, HmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + // Key material is 16 bytes (another valid key length). + RestrictedData mismatched_secret = RestrictedData(/*num_random_bytes=*/16); + + EXPECT_THAT(HmacKey::Create(*params, mismatched_secret, + /*id_requirement=*/123, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(HmacKeyTest, CreateKeyWithWrongIdRequirementFails) { + util::StatusOr<HmacParameters> no_prefix_params = HmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + HmacParameters::HashType::kSha512, HmacParameters::Variant::kNoPrefix); + ASSERT_THAT(no_prefix_params, IsOk()); + + util::StatusOr<HmacParameters> tink_params = HmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + HmacParameters::HashType::kSha512, HmacParameters::Variant::kTink); + ASSERT_THAT(tink_params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + EXPECT_THAT(HmacKey::Create(*no_prefix_params, secret, + /*id_requirement=*/123, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + EXPECT_THAT( + HmacKey::Create(*tink_params, secret, + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(HmacKeyTest, GetKeyBytes) { + int key_size; + int cryptographic_tag_size; + HmacParameters::HashType hash_type; + TestCase test_case; + std::tie(key_size, cryptographic_tag_size, hash_type, test_case) = GetParam(); + + util::StatusOr<HmacParameters> params = HmacParameters::Create( + key_size, cryptographic_tag_size, hash_type, test_case.variant); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + + util::StatusOr<HmacKey> key = HmacKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + EXPECT_THAT(key->GetKeyBytes(GetPartialKeyAccess()), IsOkAndHolds(secret)); +} + +TEST_P(HmacKeyTest, KeyEquals) { + int key_size; + int cryptographic_tag_size; + HmacParameters::HashType hash_type; + TestCase test_case; + std::tie(key_size, cryptographic_tag_size, hash_type, test_case) = GetParam(); + + util::StatusOr<HmacParameters> params = HmacParameters::Create( + key_size, cryptographic_tag_size, hash_type, test_case.variant); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(key_size); + util::StatusOr<HmacKey> key = HmacKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<HmacKey> other_key = HmacKey::Create( + *params, secret, test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key == *other_key); + EXPECT_TRUE(*other_key == *key); + EXPECT_FALSE(*key != *other_key); + EXPECT_FALSE(*other_key != *key); +} + +TEST(HmacKeyTest, DifferentFormatNotEqual) { + util::StatusOr<HmacParameters> legacy_params = HmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + HmacParameters::HashType::kSha256, HmacParameters::Variant::kLegacy); + ASSERT_THAT(legacy_params, IsOk()); + + util::StatusOr<HmacParameters> tink_params = HmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + HmacParameters::HashType::kSha256, HmacParameters::Variant::kTink); + ASSERT_THAT(tink_params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr<HmacKey> key = + HmacKey::Create(*legacy_params, secret, /*id_requirement=*/0x01020304, + GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<HmacKey> other_key = + HmacKey::Create(*tink_params, secret, /*id_requirement=*/0x01020304, + GetPartialKeyAccess()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(HmacKeyTest, DifferentSecretDataNotEqual) { + util::StatusOr<HmacParameters> params = HmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + HmacParameters::HashType::kSha384, HmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret1 = RestrictedData(/*num_random_bytes=*/32); + RestrictedData secret2 = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr<HmacKey> key = HmacKey::Create( + *params, secret1, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<HmacKey> other_key = HmacKey::Create( + *params, secret2, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(HmacKeyTest, DifferentIdRequirementNotEqual) { + util::StatusOr<HmacParameters> params = HmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + HmacParameters::HashType::kSha224, HmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr<HmacKey> key = HmacKey::Create( + *params, secret, /*id_requirement=*/0x01020304, GetPartialKeyAccess()); + ASSERT_THAT(key.status(), IsOk()); + + util::StatusOr<HmacKey> other_key = HmacKey::Create( + *params, secret, /*id_requirement=*/0x02030405, GetPartialKeyAccess()); + ASSERT_THAT(other_key.status(), IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/hmac_parameters.cc b/cc/mac/hmac_parameters.cc new file mode 100644 index 0000000..ad94b7b --- /dev/null +++ b/cc/mac/hmac_parameters.cc
@@ -0,0 +1,126 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/hmac_parameters.h" + +#include <cstdlib> +#include <iostream> +#include <map> +#include <memory> +#include <ostream> +#include <set> + +#include "absl/log/log.h" +#include "absl/strings/str_cat.h" +#include "tink/crypto_format.h" +#include "tink/internal/util.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace { + +util::Status ValidateTagSizeBytes(int cryptographic_tag_size_in_bytes, + HmacParameters::HashType hash_type) { + if (cryptographic_tag_size_in_bytes < 10) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Tag size should be at least 10 bytes, got ", + cryptographic_tag_size_in_bytes, " bytes.")); + } + std::map<HmacParameters::HashType, uint32_t> max_tag_size = { + {HmacParameters::HashType::kSha1, 20}, + {HmacParameters::HashType::kSha224, 28}, + {HmacParameters::HashType::kSha256, 32}, + {HmacParameters::HashType::kSha384, 48}, + {HmacParameters::HashType::kSha512, 64}}; + if (max_tag_size.find(hash_type) == max_tag_size.end()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Cannot create HMAC parameters with given hash type. ", + hash_type, " not supported.")); + } + if (cryptographic_tag_size_in_bytes > max_tag_size[hash_type]) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Tag size is too big for given ", hash_type, " , got ", + cryptographic_tag_size_in_bytes, " bytes.")); + } + return util::OkStatus(); +} + +} // namespace + +util::StatusOr<HmacParameters> HmacParameters::Create( + int key_size_in_bytes, int cryptographic_tag_size_in_bytes, + HashType hash_type, Variant variant) { + if (key_size_in_bytes < 16) { + return util::Status(absl::StatusCode::kInvalidArgument, + absl::StrCat("Key size must be at least 16 bytes, got ", + key_size_in_bytes, " bytes.")); + } + util::Status status = + ValidateTagSizeBytes(cryptographic_tag_size_in_bytes, hash_type); + if (!status.ok()) return status; + static const std::set<Variant>* supported_variants = + new std::set<Variant>({Variant::kTink, Variant::kCrunchy, + Variant::kLegacy, Variant::kNoPrefix}); + if (supported_variants->find(variant) == supported_variants->end()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Cannot create HMAC parameters with unknown variant."); + } + return HmacParameters(key_size_in_bytes, cryptographic_tag_size_in_bytes, + hash_type, variant); +} + +int HmacParameters::TotalTagSizeInBytes() const { + switch (variant_) { + case Variant::kTink: + case Variant::kCrunchy: + case Variant::kLegacy: + return CryptographicTagSizeInBytes() + CryptoFormat::kNonRawPrefixSize; + case Variant::kNoPrefix: + return CryptographicTagSizeInBytes(); + default: + // Parameters objects with unknown variants should never be created. + internal::LogFatal("HMAC parameters has an unknown variant."); + } +} + +bool HmacParameters::operator==(const Parameters& other) const { + const HmacParameters* that = dynamic_cast<const HmacParameters*>(&other); + if (that == nullptr) { + return false; + } + if (key_size_in_bytes_ != that->key_size_in_bytes_) { + return false; + } + if (cryptographic_tag_size_in_bytes_ != + that->cryptographic_tag_size_in_bytes_) { + return false; + } + if (hash_type_ != that->hash_type_) { + return false; + } + if (variant_ != that->variant_) { + return false; + } + return true; +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/hmac_parameters.h b/cc/mac/hmac_parameters.h new file mode 100644 index 0000000..1dc7a39 --- /dev/null +++ b/cc/mac/hmac_parameters.h
@@ -0,0 +1,115 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_MAC_HMAC_PARAMETERS_H_ +#define TINK_MAC_HMAC_PARAMETERS_H_ + +#include <memory> + +#include "tink/mac/mac_parameters.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +// Describes the parameters of an `HmacKey`. +class HmacParameters : public MacParameters { + public: + // Describes the details of a MAC computation. + // + // The usual HMAC key is used for variant `NO_PREFIX`. Other variants + // slightly change how the MAC is computed, or add a prefix to every + // computation depending on the key id. + enum class Variant : int { + // Prepends '0x01<big endian key id>' to tag. + kTink = 1, + // Prepends '0x00<big endian key id>' to tag. + kCrunchy = 2, + // Appends a 0-byte to input message BEFORE computing the tag, then + // prepends '0x00<big endian key id>' to tag. + kLegacy = 3, + // Does not prepend any prefix (i.e., keys must have no ID requirement). + kNoPrefix = 4, + // Added to guard from failures that may be caused by future expansions. + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements = 20, + }; + + // Describes the hash algorithm used. + enum class HashType : int { + kSha1 = 1, + kSha224 = 2, + kSha256 = 3, + kSha384 = 4, + kSha512 = 5, + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements = 20, + }; + + // Copyable and movable. + HmacParameters(const HmacParameters& other) = default; + HmacParameters& operator=(const HmacParameters& other) = default; + HmacParameters(HmacParameters&& other) = default; + HmacParameters& operator=(HmacParameters&& other) = default; + + // Creates a new HMAC parameters object unless an error occurs. An error + // occurs under one of the following conditions: + // 1. `key_size_in_bytes` is a value smaller than 16 bytes + // 2. `cryptographic_tag_size_in_bytes` is either less than 10 bytes or + // greater than the maximum value accepted by the corresponding hash algorithm + static util::StatusOr<HmacParameters> Create( + int key_size_in_bytes, int cryptographic_tag_size_in_bytes, + HashType hash_type, Variant variant); + + Variant GetVariant() const { return variant_; } + + HashType GetHashType() const { return hash_type_; } + + int KeySizeInBytes() const { return key_size_in_bytes_; } + + // Returns the size of the tag, which is computed cryptographically from the + // message. Note that this may differ from the total size of the tag, as for + // some keys, Tink prefixes the tag with a key dependent output prefix. + int CryptographicTagSizeInBytes() const { + return cryptographic_tag_size_in_bytes_; + } + + // Returns the size of the cryptographic tag plus the size of the prefix with + // which this key prefixes every cryptographic tag. + int TotalTagSizeInBytes() const; + + bool HasIdRequirement() const override { + return variant_ != Variant::kNoPrefix; + } + + bool operator==(const Parameters& other) const override; + + private: + HmacParameters(int key_size_in_bytes, int cryptographic_tag_size_in_bytes, + HashType hash_type, Variant variant) + : key_size_in_bytes_(key_size_in_bytes), + cryptographic_tag_size_in_bytes_(cryptographic_tag_size_in_bytes), + hash_type_(hash_type), + variant_(variant) {} + + int key_size_in_bytes_; + int cryptographic_tag_size_in_bytes_; + HashType hash_type_; + Variant variant_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_MAC_HMAC_PARAMETERS_H_
diff --git a/cc/mac/hmac_parameters_test.cc b/cc/mac/hmac_parameters_test.cc new file mode 100644 index 0000000..e9dfeb0 --- /dev/null +++ b/cc/mac/hmac_parameters_test.cc
@@ -0,0 +1,308 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/hmac_parameters.h" + +#include <memory> +#include <tuple> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::Range; +using ::testing::TestWithParam; +using ::testing::Values; + +struct CreateTestCase { + HmacParameters::Variant variant; + int key_size; + int cryptographic_tag_size; + int total_tag_size; + HmacParameters::HashType hash_type; + bool has_id_requirement; +}; + +using HmacParametersCreateTest = TestWithParam<CreateTestCase>; + +INSTANTIATE_TEST_SUITE_P( + HmacParametersCreateTestSuite, HmacParametersCreateTest, + Values(CreateTestCase{HmacParameters::Variant::kNoPrefix, /*key_size=*/16, + /*cryptographic_tag_size=*/20, /*total_tag_size=*/20, + HmacParameters::HashType::kSha1, + /*has_id_requirement=*/false}, + CreateTestCase{HmacParameters::Variant::kTink, /*key_size=*/16, + /*cryptographic_tag_size=*/28, /*total_tag_size=*/33, + HmacParameters::HashType::kSha224, + /*has_id_requirement=*/true}, + CreateTestCase{HmacParameters::Variant::kCrunchy, /*key_size=*/16, + /*cryptographic_tag_size=*/32, /*total_tag_size=*/37, + HmacParameters::HashType::kSha256, + /*has_id_requirement=*/true}, + CreateTestCase{HmacParameters::Variant::kLegacy, /*key_size=*/32, + /*cryptographic_tag_size=*/48, /*total_tag_size=*/53, + HmacParameters::HashType::kSha384, + /*has_id_requirement=*/true}, + CreateTestCase{HmacParameters::Variant::kNoPrefix, + /*key_size=*/32, /*cryptographic_tag_size=*/64, + /*total_tag_size=*/64, + HmacParameters::HashType::kSha512, + /*has_id_requirement=*/false})); + +TEST_P(HmacParametersCreateTest, Create) { + CreateTestCase test_case = GetParam(); + + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + test_case.key_size, test_case.cryptographic_tag_size, test_case.hash_type, + test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + EXPECT_THAT(parameters->GetVariant(), Eq(test_case.variant)); + EXPECT_THAT(parameters->KeySizeInBytes(), Eq(test_case.key_size)); + EXPECT_THAT(parameters->CryptographicTagSizeInBytes(), + Eq(test_case.cryptographic_tag_size)); + EXPECT_THAT(parameters->TotalTagSizeInBytes(), Eq(test_case.total_tag_size)); + EXPECT_THAT(parameters->GetHashType(), Eq(test_case.hash_type)); + EXPECT_THAT(parameters->HasIdRequirement(), Eq(test_case.has_id_requirement)); +} + +TEST(HmacParametersTest, CreateWithInvalidVariantFails) { + EXPECT_THAT(HmacParameters::Create( + /*key_size_in_bytes=*/16, + /*cryptographic_tag_size_in_bytes=*/12, + HmacParameters::HashType::kSha256, + HmacParameters::Variant:: + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(HmacParametersTest, CreateWithInvalidHashTypeFails) { + EXPECT_THAT(HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/12, + HmacParameters::HashType:: + kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements, + HmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(HmacParametersTest, CreateWithInvalidKeySizeFails) { + EXPECT_THAT(HmacParameters::Create(/*key_size_in_bytes=*/15, + /*cryptographic_tag_size_in_bytes=*/16, + HmacParameters::HashType::kSha256, + HmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(HmacParametersTest, CreateWithInvalidTagSizeFails) { + // Too small. + EXPECT_THAT(HmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/7, + HmacParameters::HashType::kSha224, + HmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Too big for kSha1. + EXPECT_THAT(HmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/21, + HmacParameters::HashType::kSha1, + HmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Too big for kSha224. + EXPECT_THAT(HmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/29, + HmacParameters::HashType::kSha224, + HmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Too big for kSha256; + EXPECT_THAT(HmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/33, + HmacParameters::HashType::kSha256, + HmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Too big for kSha384; + EXPECT_THAT(HmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/49, + HmacParameters::HashType::kSha384, + HmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + // Too big for kSha512; + EXPECT_THAT(HmacParameters::Create(/*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/65, + HmacParameters::HashType::kSha512, + HmacParameters::Variant::kNoPrefix) + .status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(HmacParametersTest, CopyConstructor) { + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/12, HmacParameters::HashType::kSha256, + HmacParameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + HmacParameters copy(*parameters); + EXPECT_THAT(copy.GetVariant(), Eq(parameters->GetVariant())); + EXPECT_THAT(copy.CryptographicTagSizeInBytes(), + Eq(parameters->CryptographicTagSizeInBytes())); + EXPECT_THAT(copy.TotalTagSizeInBytes(), + Eq(parameters->TotalTagSizeInBytes())); + EXPECT_THAT(copy.GetHashType(), Eq(parameters->GetHashType())); + EXPECT_THAT(copy.HasIdRequirement(), Eq(parameters->HasIdRequirement())); +} + +TEST(HmacParametersTest, CopyAssignment) { + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/12, HmacParameters::HashType::kSha512, + HmacParameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + HmacParameters copy = *parameters; + EXPECT_THAT(copy.GetVariant(), Eq(parameters->GetVariant())); + EXPECT_THAT(copy.CryptographicTagSizeInBytes(), + Eq(parameters->CryptographicTagSizeInBytes())); + EXPECT_THAT(copy.TotalTagSizeInBytes(), + Eq(parameters->TotalTagSizeInBytes())); + EXPECT_THAT(copy.GetHashType(), Eq(parameters->GetHashType())); + EXPECT_THAT(copy.HasIdRequirement(), Eq(parameters->HasIdRequirement())); +} + +using HmacParametersVariantTest = TestWithParam< + std::tuple<int, int, HmacParameters::HashType, HmacParameters::Variant>>; + +INSTANTIATE_TEST_SUITE_P(HmacParametersVariantTestSuite, + HmacParametersVariantTest, + Combine(Range(16, 32), Range(10, 20), + Values(HmacParameters::HashType::kSha1, + HmacParameters::HashType::kSha224, + HmacParameters::HashType::kSha256, + HmacParameters::HashType::kSha384, + HmacParameters::HashType::kSha512), + Values(HmacParameters::Variant::kTink, + HmacParameters::Variant::kCrunchy, + HmacParameters::Variant::kLegacy, + HmacParameters::Variant::kNoPrefix))); + +TEST_P(HmacParametersVariantTest, ParametersEquals) { + int key_size; + int cryptographic_tag_size; + HmacParameters::HashType hash_type; + HmacParameters::Variant variant; + std::tie(key_size, cryptographic_tag_size, hash_type, variant) = GetParam(); + + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + key_size, cryptographic_tag_size, hash_type, variant); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<HmacParameters> other_parameters = HmacParameters::Create( + key_size, cryptographic_tag_size, hash_type, variant); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters == *other_parameters); + EXPECT_TRUE(*other_parameters == *parameters); + EXPECT_FALSE(*parameters != *other_parameters); + EXPECT_FALSE(*other_parameters != *parameters); +} + +TEST(HmacParametersTest, KeySizeNotEqual) { + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + /*key_size_in_bytes=*/16, + /*cryptographic_tag_size_in_bytes=*/10, HmacParameters::HashType::kSha224, + HmacParameters::Variant::kNoPrefix); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<HmacParameters> other_parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, HmacParameters::HashType::kSha224, + HmacParameters::Variant::kNoPrefix); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +TEST(HmacParametersTest, HashTypeNotEqual) { + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, HmacParameters::HashType::kSha256, + HmacParameters::Variant::kNoPrefix); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<HmacParameters> other_parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, HmacParameters::HashType::kSha512, + HmacParameters::Variant::kNoPrefix); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +TEST(HmacParametersTest, TagSizeNotEqual) { + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, HmacParameters::HashType::kSha256, + HmacParameters::Variant::kNoPrefix); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<HmacParameters> other_parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/11, HmacParameters::HashType::kSha256, + HmacParameters::Variant::kNoPrefix); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +TEST(HmacParametersTest, VariantNotEqual) { + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, HmacParameters::HashType::kSha256, + HmacParameters::Variant::kNoPrefix); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<HmacParameters> other_parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, + /*cryptographic_tag_size_in_bytes=*/10, HmacParameters::HashType::kSha256, + HmacParameters::Variant::kTink); + ASSERT_THAT(other_parameters, IsOk()); + + EXPECT_TRUE(*parameters != *other_parameters); + EXPECT_FALSE(*parameters == *other_parameters); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/hmac_proto_serialization.cc b/cc/mac/hmac_proto_serialization.cc new file mode 100644 index 0000000..aaa6ced --- /dev/null +++ b/cc/mac/hmac_proto_serialization.cc
@@ -0,0 +1,305 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/hmac_proto_serialization.h" + +#include <string> + +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/parameters_parser.h" +#include "tink/internal/parameters_serializer.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/mac/hmac_key.h" +#include "tink/mac/hmac_parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/common.pb.h" +#include "proto/hmac.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::google::crypto::tink::HashType; +using ::google::crypto::tink::HmacKeyFormat; +using ::google::crypto::tink::HmacParams; +using ::google::crypto::tink::OutputPrefixType; + +using HmacProtoParametersParserImpl = + internal::ParametersParserImpl<internal::ProtoParametersSerialization, + HmacParameters>; +using HmacProtoParametersSerializerImpl = + internal::ParametersSerializerImpl<HmacParameters, + internal::ProtoParametersSerialization>; +using HmacProtoKeyParserImpl = + internal::KeyParserImpl<internal::ProtoKeySerialization, HmacKey>; +using HmacProtoKeySerializerImpl = + internal::KeySerializerImpl<HmacKey, internal::ProtoKeySerialization>; + +const absl::string_view kTypeUrl = + "type.googleapis.com/google.crypto.tink.HmacKey"; + +util::StatusOr<HmacParameters::Variant> ToVariant( + OutputPrefixType output_prefix_type) { + switch (output_prefix_type) { + case OutputPrefixType::CRUNCHY: + return HmacParameters::Variant::kCrunchy; + case OutputPrefixType::LEGACY: + return HmacParameters::Variant::kLegacy; + case OutputPrefixType::RAW: + return HmacParameters::Variant::kNoPrefix; + case OutputPrefixType::TINK: + return HmacParameters::Variant::kTink; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine HmacParameters::Variant"); + } +} + +util::StatusOr<OutputPrefixType> ToOutputPrefixType( + HmacParameters::Variant variant) { + switch (variant) { + case HmacParameters::Variant::kCrunchy: + return OutputPrefixType::CRUNCHY; + case HmacParameters::Variant::kLegacy: + return OutputPrefixType::LEGACY; + case HmacParameters::Variant::kNoPrefix: + return OutputPrefixType::RAW; + case HmacParameters::Variant::kTink: + return OutputPrefixType::TINK; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine output prefix type"); + } +} + +util::StatusOr<HmacParameters::HashType> ToHashType(HashType hash_type) { + switch (hash_type) { + case HashType::SHA1: + return HmacParameters::HashType::kSha1; + case HashType::SHA224: + return HmacParameters::HashType::kSha224; + case HashType::SHA256: + return HmacParameters::HashType::kSha256; + case HashType::SHA384: + return HmacParameters::HashType::kSha384; + case HashType::SHA512: + return HmacParameters::HashType::kSha512; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine HashType"); + } +} + +util::StatusOr<HashType> ToProtoHashType(HmacParameters::HashType hash_type) { + switch (hash_type) { + case HmacParameters::HashType::kSha1: + return HashType::SHA1; + case HmacParameters::HashType::kSha224: + return HashType::SHA224; + case HmacParameters::HashType::kSha256: + return HashType::SHA256; + case HmacParameters::HashType::kSha384: + return HashType::SHA384; + case HmacParameters::HashType::kSha512: + return HashType::SHA512; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine HmacParameters::HashType"); + } +} + +util::StatusOr<HmacParameters> ParseParameters( + const internal::ProtoParametersSerialization& serialization) { + if (serialization.GetKeyTemplate().type_url() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing HmacParameters."); + } + + HmacKeyFormat proto_key_format; + if (!proto_key_format.ParseFromString( + serialization.GetKeyTemplate().value())) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse HmacKeyFormat proto"); + } + if (proto_key_format.version() != 0) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Parsing HmacParameters failed: only version 0 is accepted"); + } + + util::StatusOr<HmacParameters::Variant> variant = + ToVariant(serialization.GetKeyTemplate().output_prefix_type()); + if (!variant.ok()) return variant.status(); + + util::StatusOr<HmacParameters::HashType> hash_type = + ToHashType(proto_key_format.params().hash()); + if (!hash_type.ok()) return variant.status(); + + return HmacParameters::Create(proto_key_format.key_size(), + proto_key_format.params().tag_size(), + *hash_type, *variant); +} + +util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters( + const HmacParameters& parameters) { + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(parameters.GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + util::StatusOr<HashType> proto_hash_type = + ToProtoHashType(parameters.GetHashType()); + if (!proto_hash_type.ok()) return proto_hash_type.status(); + + HmacParams proto_params; + proto_params.set_tag_size(parameters.CryptographicTagSizeInBytes()); + proto_params.set_hash(*proto_hash_type); + HmacKeyFormat proto_key_format; + proto_key_format.set_key_size(parameters.KeySizeInBytes()); + proto_key_format.set_version(0); + *proto_key_format.mutable_params() = proto_params; + + return internal::ProtoParametersSerialization::Create( + kTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString()); +} + +util::StatusOr<HmacKey> ParseKey( + const internal::ProtoKeySerialization& serialization, + absl::optional<SecretKeyAccessToken> token) { + if (serialization.TypeUrl() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing HmacKey."); + } + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + + google::crypto::tink::HmacKey proto_key; + RestrictedData restricted_data = serialization.SerializedKeyProto(); + // OSS proto library complains if input is not converted to a string. + if (!proto_key.ParseFromString( + std::string(restricted_data.GetSecret(*token)))) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse HmacKey proto"); + } + if (proto_key.version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + util::StatusOr<HmacParameters::Variant> variant = + ToVariant(serialization.GetOutputPrefixType()); + if (!variant.ok()) return variant.status(); + util::StatusOr<HmacParameters::HashType> hash_type = + ToHashType(proto_key.params().hash()); + if (!hash_type.ok()) return variant.status(); + + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + proto_key.key_value().length(), proto_key.params().tag_size(), *hash_type, + *variant); + if (!parameters.ok()) return parameters.status(); + + return HmacKey::Create(*parameters, + RestrictedData(proto_key.key_value(), *token), + serialization.IdRequirement(), GetPartialKeyAccess()); +} + +util::StatusOr<internal::ProtoKeySerialization> SerializeKey( + const HmacKey& key, absl::optional<SecretKeyAccessToken> token) { + util::StatusOr<RestrictedData> restricted_input = + key.GetKeyBytes(GetPartialKeyAccess()); + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + if (!restricted_input.ok()) return restricted_input.status(); + util::StatusOr<HashType> proto_hash_type = + ToProtoHashType(key.GetParameters().GetHashType()); + if (!proto_hash_type.ok()) return proto_hash_type.status(); + + HmacParams proto_params; + proto_params.set_tag_size(key.GetParameters().CryptographicTagSizeInBytes()); + proto_params.set_hash(*proto_hash_type); + google::crypto::tink::HmacKey proto_key; + *proto_key.mutable_params() = proto_params; + proto_key.set_version(0); + // OSS proto library complains if input is not converted to a string. + proto_key.set_key_value(std::string(restricted_input->GetSecret(*token))); + + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(key.GetParameters().GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + + RestrictedData restricted_output = + RestrictedData(proto_key.SerializeAsString(), *token); + return internal::ProtoKeySerialization::Create( + kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC, + *output_prefix_type, key.GetIdRequirement()); +} + +HmacProtoParametersParserImpl* HmacProtoParametersParser() { + static auto* parser = + new HmacProtoParametersParserImpl(kTypeUrl, ParseParameters); + return parser; +} + +HmacProtoParametersSerializerImpl* HmacProtoParametersSerializer() { + static auto* serializer = + new HmacProtoParametersSerializerImpl(kTypeUrl, SerializeParameters); + return serializer; +} + +HmacProtoKeyParserImpl* HmacProtoKeyParser() { + static auto* parser = new HmacProtoKeyParserImpl(kTypeUrl, ParseKey); + return parser; +} + +HmacProtoKeySerializerImpl* HmacProtoKeySerializer() { + static auto* serializer = new HmacProtoKeySerializerImpl(SerializeKey); + return serializer; +} + +} // namespace + +util::Status RegisterHmacProtoSerialization() { + util::Status status = + internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersParser(HmacProtoParametersParser()); + if (!status.ok()) return status; + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersSerializer(HmacProtoParametersSerializer()); + if (!status.ok()) return status; + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeyParser(HmacProtoKeyParser()); + if (!status.ok()) return status; + + return internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeySerializer(HmacProtoKeySerializer()); +} + +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/hmac_proto_serialization.h b/cc/mac/hmac_proto_serialization.h new file mode 100644 index 0000000..2996827 --- /dev/null +++ b/cc/mac/hmac_proto_serialization.h
@@ -0,0 +1,31 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_MAC_HMAC_PROTO_SERIALIZATION_H_ +#define TINK_MAC_HMAC_PROTO_SERIALIZATION_H_ + +#include "tink/util/status.h" + +namespace crypto { +namespace tink { + +// Registers proto parsers and serializers for HMAC parameters and keys. +crypto::tink::util::Status RegisterHmacProtoSerialization(); + +} // namespace tink +} // namespace crypto + +#endif // TINK_MAC_HMAC_PROTO_SERIALIZATION_H_
diff --git a/cc/mac/hmac_proto_serialization_test.cc b/cc/mac/hmac_proto_serialization_test.cc new file mode 100644 index 0000000..4bcc684 --- /dev/null +++ b/cc/mac/hmac_proto_serialization_test.cc
@@ -0,0 +1,409 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/mac/hmac_proto_serialization.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/mac/hmac_key.h" +#include "tink/mac/hmac_parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/subtle/random.h" +#include "tink/util/test_matchers.h" +#include "proto/common.pb.h" +#include "proto/hmac.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::subtle::Random; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::HashType; +using ::google::crypto::tink::HmacKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::NotNull; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + HmacParameters::Variant variant; + OutputPrefixType output_prefix_type; + HmacParameters::HashType hash_type; + HashType proto_hash_type; + int key_size; + int tag_size; + int total_size; + absl::optional<int> id; + std::string output_prefix; +}; + +class HmacProtoSerializationTest : public TestWithParam<TestCase> { + protected: + void SetUp() override { + internal::MutableSerializationRegistry::GlobalInstance().Reset(); + } +}; + +INSTANTIATE_TEST_SUITE_P( + HmacProtoSerializationTestSuite, HmacProtoSerializationTest, + Values(TestCase{HmacParameters::Variant::kTink, OutputPrefixType::TINK, + HmacParameters::HashType::kSha1, HashType::SHA1, + /*key_size=*/16, /*cryptographic_tag_size=*/10, + /*total_size=*/15, /*id=*/0x02030400, + /*output_prefix=*/std::string("\x01\x02\x03\x04\x00", 5)}, + TestCase{HmacParameters::Variant::kCrunchy, + OutputPrefixType::CRUNCHY, + HmacParameters::HashType::kSha224, HashType::SHA224, + /*key_size=*/16, /*tag_size=*/12, /*total_size=*/17, + /*id=*/0x01030005, + /*output_prefix=*/std::string("\x00\x01\x03\x00\x05", 5)}, + TestCase{HmacParameters::Variant::kLegacy, OutputPrefixType::LEGACY, + HmacParameters::HashType::kSha256, HashType::SHA256, + /*key_size=*/32, /*cryptographic_tag_size=*/14, + /*total_tag_size=*/19, /*id=*/0x01020304, + /*output_prefix=*/std::string("\x00\x01\x02\x03\x04", 5)}, + TestCase{HmacParameters::Variant::kNoPrefix, OutputPrefixType::RAW, + HmacParameters::HashType::kSha384, HashType::SHA384, + /*key_size=*/32, /*cryptographic_tag_size=*/16, + /*total_tag_size=*/16, /*id=*/absl::nullopt, + /*output_prefix=*/""}, + TestCase{HmacParameters::Variant::kNoPrefix, OutputPrefixType::RAW, + HmacParameters::HashType::kSha512, HashType::SHA512, + /*key_size=*/32, /*cryptographic_tag_size=*/20, + /*total_tag_size=*/20, /*id=*/absl::nullopt, + /*output_prefix=*/""})); + +TEST_P(HmacProtoSerializationTest, ParseParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + HmacKeyFormat key_format_proto; + key_format_proto.set_key_size(test_case.key_size); + key_format_proto.mutable_params()->set_tag_size(test_case.tag_size); + key_format_proto.mutable_params()->set_hash(test_case.proto_hash_type); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.HmacKey", + test_case.output_prefix_type, key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> parsed_parameters = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(parsed_parameters, IsOk()); + EXPECT_THAT((*parsed_parameters)->HasIdRequirement(), + test_case.id.has_value()); + + util::StatusOr<HmacParameters> expected_parameters = + HmacParameters::Create(test_case.key_size, test_case.tag_size, + test_case.hash_type, test_case.variant); + ASSERT_THAT(expected_parameters, IsOk()); + ASSERT_THAT(**parsed_parameters, Eq(*expected_parameters)); +} + +TEST_F(HmacProtoSerializationTest, ParseParametersWithInvalidSerialization) { + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.HmacKey", + OutputPrefixType::RAW, "invalid_serialization"); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(HmacProtoSerializationTest, ParseParametersWithInvalidVersion) { + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + HmacKeyFormat key_format_proto; + key_format_proto.set_key_size(16); + key_format_proto.set_version(1); // Invalid version. + key_format_proto.mutable_params()->set_tag_size(10); + key_format_proto.mutable_params()->set_hash(HashType::SHA256); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.HmacKey", + OutputPrefixType::RAW, key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(HmacProtoSerializationTest, ParseParametersWithUnkownOutputPrefix) { + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + HmacKeyFormat key_format_proto; + key_format_proto.set_key_size(16); + key_format_proto.mutable_params()->set_tag_size(10); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.HmacKey", + OutputPrefixType::UNKNOWN_PREFIX, + key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(HmacProtoSerializationTest, SerializeParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + util::StatusOr<HmacParameters> parameters = + HmacParameters::Create(test_case.key_size, test_case.tag_size, + test_case.hash_type, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>( + *parameters); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.HmacKey")); + + const internal::ProtoParametersSerialization* proto_serialization = + dynamic_cast<const internal::ProtoParametersSerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->GetKeyTemplate().type_url(), + Eq("type.googleapis.com/google.crypto.tink.HmacKey")); + EXPECT_THAT(proto_serialization->GetKeyTemplate().output_prefix_type(), + Eq(test_case.output_prefix_type)); + + HmacKeyFormat key_format; + ASSERT_THAT( + key_format.ParseFromString(proto_serialization->GetKeyTemplate().value()), + IsTrue()); + ASSERT_THAT(key_format.key_size(), Eq(test_case.key_size)); + ASSERT_THAT(key_format.params().tag_size(), Eq(test_case.tag_size)); + ASSERT_THAT(key_format.params().hash(), Eq(test_case.proto_hash_type)); +} + +TEST_P(HmacProtoSerializationTest, ParseKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + google::crypto::tink::HmacKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_tag_size(test_case.tag_size); + key_proto.mutable_params()->set_hash(test_case.proto_hash_type); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.HmacKey", serialized_key, + KeyData::SYMMETRIC, test_case.output_prefix_type, test_case.id); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> parsed_key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_key, IsOk()); + EXPECT_THAT((*parsed_key)->GetParameters().HasIdRequirement(), + test_case.id.has_value()); + EXPECT_THAT((*parsed_key)->GetIdRequirement(), Eq(test_case.id)); + + util::StatusOr<HmacParameters> expected_parameters = + HmacParameters::Create(test_case.key_size, test_case.tag_size, + test_case.hash_type, test_case.variant); + ASSERT_THAT(expected_parameters, IsOk()); + util::StatusOr<HmacKey> expected_key = HmacKey::Create( + *expected_parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + test_case.id, GetPartialKeyAccess()); + + ASSERT_THAT(expected_key, IsOk()); + ASSERT_THAT(**parsed_key, Eq(*expected_key)); +} + +TEST_F(HmacProtoSerializationTest, ParseKeyWithInvalidSerialization) { + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::HmacKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_tag_size(10); + key_proto.mutable_params()->set_hash(HashType::SHA256); + RestrictedData serialized_key = + RestrictedData("invalid_serialization", InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.HmacKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(HmacProtoSerializationTest, ParseKeyWithInvalidVersion) { + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::HmacKey key_proto; + key_proto.set_version(1); // Invalid version number. + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_tag_size(10); + key_proto.mutable_params()->set_hash(HashType::SHA256); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.HmacKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(HmacProtoSerializationTest, ParseKeyWithoutSecretKeyAccess) { + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::HmacKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_tag_size(10); + key_proto.mutable_params()->set_hash(HashType::SHA256); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.HmacKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, absl::nullopt); + ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(HmacProtoSerializationTest, SerializeKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + util::StatusOr<HmacParameters> parameters = + HmacParameters::Create(test_case.key_size, test_case.tag_size, + test_case.hash_type, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + util::StatusOr<HmacKey> key = HmacKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.HmacKey")); + + const internal::ProtoKeySerialization* proto_serialization = + dynamic_cast<const internal::ProtoKeySerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->TypeUrl(), + Eq("type.googleapis.com/google.crypto.tink.HmacKey")); + EXPECT_THAT(proto_serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(proto_serialization->GetOutputPrefixType(), + Eq(test_case.output_prefix_type)); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(test_case.id)); + + google::crypto::tink::HmacKey proto_key; + // OSS proto library complains if input is not converted to a string. + ASSERT_THAT(proto_key.ParseFromString(std::string( + proto_serialization->SerializedKeyProto().GetSecret( + InsecureSecretKeyAccess::Get()))), + IsTrue()); + EXPECT_THAT(proto_key.key_value().size(), Eq(test_case.key_size)); + EXPECT_THAT(proto_key.params().tag_size(), Eq(test_case.tag_size)); + EXPECT_THAT(proto_key.params().hash(), Eq(test_case.proto_hash_type)); +} + +TEST_F(HmacProtoSerializationTest, SerializeKeyWithoutSecretKeyAccess) { + ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk()); + + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + /*key_size_in_bytes=*/16, /*cryptographic_tag_size_in_bytes=*/10, + HmacParameters::HashType::kSha256, HmacParameters::Variant::kNoPrefix); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + util::StatusOr<HmacKey> key = HmacKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>(*key, absl::nullopt); + ASSERT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace tink +} // namespace crypto
diff --git a/cc/mac/internal/chunked_mac_impl.h b/cc/mac/internal/chunked_mac_impl.h index 940cf47..6a5d0a8 100644 --- a/cc/mac/internal/chunked_mac_impl.h +++ b/cc/mac/internal/chunked_mac_impl.h
@@ -14,8 +14,8 @@ // /////////////////////////////////////////////////////////////////////////////// -#ifndef TINK_CHUNKEDMAC_INTERNAL_CHUNKED_MAC_IMPL_H_ -#define TINK_CHUNKEDMAC_INTERNAL_CHUNKED_MAC_IMPL_H_ +#ifndef TINK_MAC_INTERNAL_CHUNKED_MAC_IMPL_H_ +#define TINK_MAC_INTERNAL_CHUNKED_MAC_IMPL_H_ #include <memory> #include <string> @@ -92,4 +92,4 @@ } // namespace tink } // namespace crypto -#endif // TINK_CHUNKEDMAC_INTERNAL_CHUNKED_MAC_IMPL_H_ +#endif // TINK_MAC_INTERNAL_CHUNKED_MAC_IMPL_H_
diff --git a/cc/mac/internal/chunked_mac_wrapper.cc b/cc/mac/internal/chunked_mac_wrapper.cc index 5988daa..c95853c 100644 --- a/cc/mac/internal/chunked_mac_wrapper.cc +++ b/cc/mac/internal/chunked_mac_wrapper.cc
@@ -155,7 +155,7 @@ util::StatusOr<std::unique_ptr<ChunkedMacVerification>> CreateVerification( absl::string_view tag) const override; - ~ChunkedMacSetWrapper() override {} + ~ChunkedMacSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<ChunkedMac>> mac_set_;
diff --git a/cc/mac/internal/chunked_mac_wrapper.h b/cc/mac/internal/chunked_mac_wrapper.h index 2e8e0dd..ad86442 100644 --- a/cc/mac/internal/chunked_mac_wrapper.h +++ b/cc/mac/internal/chunked_mac_wrapper.h
@@ -14,8 +14,8 @@ // /////////////////////////////////////////////////////////////////////////////// -#ifndef TINK_CHUNKEDMAC_INTERNAL_CHUNKED_MAC_WRAPPER_H_ -#define TINK_CHUNKEDMAC_INTERNAL_CHUNKED_MAC_WRAPPER_H_ +#ifndef TINK_MAC_INTERNAL_CHUNKED_MAC_WRAPPER_H_ +#define TINK_MAC_INTERNAL_CHUNKED_MAC_WRAPPER_H_ #include <memory> @@ -47,4 +47,4 @@ } // namespace tink } // namespace crypto -#endif // TINK_CHUNKEDMAC_INTERNAL_CHUNKED_MAC_WRAPPER_H_ +#endif // TINK_MAC_INTERNAL_CHUNKED_MAC_WRAPPER_H_
diff --git a/cc/mac/mac_config.cc b/cc/mac/mac_config.cc index 6b87fd3..5f9997a 100644 --- a/cc/mac/mac_config.cc +++ b/cc/mac/mac_config.cc
@@ -20,7 +20,9 @@ #include "tink/config/config_util.h" #include "tink/config/tink_fips.h" #include "tink/mac/aes_cmac_key_manager.h" +#include "tink/mac/aes_cmac_proto_serialization.h" #include "tink/mac/hmac_key_manager.h" +#include "tink/mac/hmac_proto_serialization.h" #include "tink/mac/internal/chunked_mac_wrapper.h" #include "tink/mac/mac_wrapper.h" #include "tink/registry.h" @@ -33,12 +35,6 @@ namespace tink { // static -const RegistryConfig& MacConfig::Latest() { - static const RegistryConfig* config = new RegistryConfig(); - return *config; -} - -// static util::Status MacConfig::Register() { // Register primitive wrappers. auto status = @@ -55,6 +51,9 @@ true); if (!status.ok()) return status; + status = RegisterHmacProtoSerialization(); + if (!status.ok()) return status; + if (IsFipsModeEnabled()) { return util::OkStatus(); } @@ -64,6 +63,9 @@ absl::make_unique<AesCmacKeyManager>(), true); if (!status.ok()) return status; + status = RegisterAesCmacProtoSerialization(); + if (!status.ok()) return status; + return util::OkStatus(); }
diff --git a/cc/mac/mac_config.h b/cc/mac/mac_config.h index 7213e61..1e482b0 100644 --- a/cc/mac/mac_config.h +++ b/cc/mac/mac_config.h
@@ -34,14 +34,6 @@ // class MacConfig { public: - static constexpr char kCatalogueName[] = "TinkMac"; - static constexpr char kPrimitiveName[] = "Mac"; - - // Returns config of Mac implementations supported - // in the current Tink release. - ABSL_DEPRECATED("This is not supported anymore.") - static const google::crypto::tink::RegistryConfig& Latest(); - // Registers Mac primitive wrapper and key managers for all Mac key types // from the current Tink release. static crypto::tink::util::Status Register();
diff --git a/cc/mac/mac_config_test.cc b/cc/mac/mac_config_test.cc index 6c63278..4f002f7 100644 --- a/cc/mac/mac_config_test.cc +++ b/cc/mac/mac_config_test.cc
@@ -24,17 +24,27 @@ #include "gtest/gtest.h" #include "absl/status/status.h" #include "tink/chunked_mac.h" -#include "tink/config.h" -#include "tink/config/tink_fips.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/fips_utils.h" +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/proto_key_serialization.h" +#include "tink/internal/proto_parameters_serialization.h" #include "tink/keyset_handle.h" #include "tink/mac.h" +#include "tink/mac/aes_cmac_key.h" #include "tink/mac/aes_cmac_key_manager.h" +#include "tink/mac/aes_cmac_parameters.h" +#include "tink/mac/hmac_key.h" #include "tink/mac/hmac_key_manager.h" +#include "tink/mac/hmac_parameters.h" #include "tink/mac/mac_key_templates.h" +#include "tink/partial_key_access.h" #include "tink/registry.h" #include "tink/util/status.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" +#include "proto/common.pb.h" +#include "proto/tink.pb.h" namespace crypto { namespace tink { @@ -43,8 +53,10 @@ using ::crypto::tink::test::DummyMac; using ::crypto::tink::test::IsOk; using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeyData; using ::google::crypto::tink::KeysetInfo; using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::HashType; using ::google::crypto::tink::KeyTemplate; using ::google::crypto::tink::OutputPrefixType; using ::testing::Values; @@ -53,11 +65,12 @@ protected: void SetUp() override { Registry::Reset(); + internal::MutableSerializationRegistry::GlobalInstance().Reset(); } }; TEST_F(MacConfigTest, Basic) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Not supported in FIPS-only mode"; } @@ -97,7 +110,7 @@ // Tests that the MacWrapper has been properly registered and we can wrap // primitives. TEST_F(MacConfigTest, MacWrappersRegistered) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Not supported in FIPS-only mode"; } @@ -128,6 +141,201 @@ DummyMac("dummy").VerifyMac(mac_result.value(), "faked text").ok()); } +TEST_F(MacConfigTest, AesCmacProtoParamsSerializationRegistered) { + if (internal::IsFipsModeEnabled()) { + GTEST_SKIP() << "Not supported in FIPS-only mode"; + } + + util::StatusOr<internal::ProtoParametersSerialization> + proto_params_serialization = + internal::ProtoParametersSerialization::Create( + MacKeyTemplates::AesCmac()); + ASSERT_THAT(proto_params_serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> parsed_params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *proto_params_serialization); + ASSERT_THAT(parsed_params.status(), StatusIs(absl::StatusCode::kNotFound)); + + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_params = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>(*params); + ASSERT_THAT(serialized_params.status(), + StatusIs(absl::StatusCode::kNotFound)); + + ASSERT_THAT(MacConfig::Register(), IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> parsed_params2 = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *proto_params_serialization); + ASSERT_THAT(parsed_params2, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_params2 = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>(*params); + ASSERT_THAT(serialized_params2, IsOk()); +} + +TEST_F(MacConfigTest, AesCmacProtoKeySerializationRegistered) { + if (internal::IsFipsModeEnabled()) { + GTEST_SKIP() << "Not supported in FIPS-only mode"; + } + + google::crypto::tink::AesCmacKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(subtle::Random::GetRandomBytes(32)); + key_proto.mutable_params()->set_tag_size(16); + + util::StatusOr<internal::ProtoKeySerialization> proto_key_serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesCmacKey", + RestrictedData(key_proto.SerializeAsString(), + InsecureSecretKeyAccess::Get()), + KeyData::SYMMETRIC, OutputPrefixType::TINK, /*id_requirement=*/123); + ASSERT_THAT(proto_key_serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> parsed_key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *proto_key_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_key.status(), StatusIs(absl::StatusCode::kNotFound)); + + util::StatusOr<AesCmacParameters> params = AesCmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/16, + AesCmacParameters::Variant::kTink); + ASSERT_THAT(params, IsOk()); + + util::StatusOr<AesCmacKey> key = + AesCmacKey::Create(*params, + RestrictedData(subtle::Random::GetRandomBytes(32), + InsecureSecretKeyAccess::Get()), + /*id_requirement=*/123, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_key = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialized_key.status(), StatusIs(absl::StatusCode::kNotFound)); + + ASSERT_THAT(MacConfig::Register(), IsOk()); + + util::StatusOr<std::unique_ptr<Key>> parsed_key2 = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *proto_key_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_key2, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_key2 = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialized_key2, IsOk()); +} + +TEST_F(MacConfigTest, HmacProtoParamsSerializationRegistered) { + if (internal::IsFipsModeEnabled()) { + GTEST_SKIP() << "Not supported in FIPS-only mode"; + } + + util::StatusOr<internal::ProtoParametersSerialization> + proto_params_serialization = + internal::ProtoParametersSerialization::Create( + MacKeyTemplates::HmacSha256()); + ASSERT_THAT(proto_params_serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> parsed_params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *proto_params_serialization); + ASSERT_THAT(parsed_params.status(), StatusIs(absl::StatusCode::kNotFound)); + + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/32, + HmacParameters::HashType::kSha256, HmacParameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_parameters = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>( + *parameters); + ASSERT_THAT(serialized_parameters.status(), + StatusIs(absl::StatusCode::kNotFound)); + + // Register parser and serializer. + ASSERT_THAT(MacConfig::Register(), IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> parsed_params2 = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *proto_params_serialization); + ASSERT_THAT(parsed_params2, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_params2 = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>( + *parameters); + ASSERT_THAT(serialized_params2, IsOk()); +} + +TEST_F(MacConfigTest, HmacProtoKeySerializationRegistered) { + if (internal::IsFipsModeEnabled()) { + GTEST_SKIP() << "Not supported in FIPS-only mode"; + } + + google::crypto::tink::HmacKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(subtle::Random::GetRandomBytes(32)); + key_proto.mutable_params()->set_tag_size(32); + key_proto.mutable_params()->set_hash(HashType::SHA256); + + util::StatusOr<internal::ProtoKeySerialization> proto_key_serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.HmacKey", + RestrictedData(key_proto.SerializeAsString(), + InsecureSecretKeyAccess::Get()), + KeyData::SYMMETRIC, OutputPrefixType::TINK, /*id_requirement=*/123); + ASSERT_THAT(proto_key_serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> parsed_key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *proto_key_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_key.status(), StatusIs(absl::StatusCode::kNotFound)); + + util::StatusOr<HmacParameters> parameters = HmacParameters::Create( + /*key_size_in_bytes=*/32, /*cryptographic_tag_size_in_bytes=*/32, + HmacParameters::HashType::kSha256, HmacParameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<HmacKey> key = + HmacKey::Create(*parameters, + RestrictedData(subtle::Random::GetRandomBytes(32), + InsecureSecretKeyAccess::Get()), + /*id_requirement=*/123, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_key = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialized_key.status(), StatusIs(absl::StatusCode::kNotFound)); + + // Register parser and serializer. + ASSERT_THAT(MacConfig::Register(), IsOk()); + + util::StatusOr<std::unique_ptr<Key>> parsed_key2 = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *proto_key_serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_key2, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialized_key2 = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialized_key2, IsOk()); +} + class ChunkedMacConfigTest : public ::testing::TestWithParam<KeyTemplate> { protected: void SetUp() override { Registry::Reset(); } @@ -140,7 +348,7 @@ // Tests that the ChunkedMacWrapper has been properly registered and we can get // primitives. TEST_P(ChunkedMacConfigTest, ChunkedMacWrappersRegistered) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Not supported in FIPS-only mode"; } @@ -172,7 +380,7 @@ // FIPS-only mode tests TEST_F(MacConfigTest, RegisterNonFipsTemplates) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-only mode"; } @@ -188,7 +396,7 @@ } TEST_F(MacConfigTest, RegisterFipsValidTemplates) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-only mode"; }
diff --git a/cc/mac/mac_factory.cc b/cc/mac/mac_factory.cc index 954ac82..6f4a420 100644 --- a/cc/mac/mac_factory.cc +++ b/cc/mac/mac_factory.cc
@@ -16,13 +16,14 @@ #include "tink/mac/mac_factory.h" +#include <memory> + #include "tink/mac.h" -#include "tink/registry.h" #include "tink/mac/mac_wrapper.h" +#include "tink/registry.h" #include "tink/util/status.h" #include "tink/util/statusor.h" - namespace crypto { namespace tink {
diff --git a/cc/mac/mac_factory.h b/cc/mac/mac_factory.h index 1fc427b..bf30bcb 100644 --- a/cc/mac/mac_factory.h +++ b/cc/mac/mac_factory.h
@@ -17,6 +17,8 @@ #ifndef TINK_MAC_MAC_FACTORY_H_ #define TINK_MAC_MAC_FACTORY_H_ +#include <memory> + #include "absl/base/macros.h" #include "tink/key_manager.h" #include "tink/keyset_handle.h"
diff --git a/cc/mac/mac_factory_test.cc b/cc/mac/mac_factory_test.cc index 7e70f5c..27b5d6c 100644 --- a/cc/mac/mac_factory_test.cc +++ b/cc/mac/mac_factory_test.cc
@@ -33,7 +33,6 @@ #include "proto/hmac.pb.h" #include "proto/tink.pb.h" -using crypto::tink::TestKeysetHandle; using crypto::tink::test::AddRawKey; using crypto::tink::test::AddTinkKey; using google::crypto::tink::HashType;
diff --git a/cc/mac/mac_key.h b/cc/mac/mac_key.h index 5e14e5f..d02933c 100644 --- a/cc/mac/mac_key.h +++ b/cc/mac/mac_key.h
@@ -40,7 +40,7 @@ // may be a prefix of another). To avoid this, built-in Tink keys use the // convention that the prefix is either '0x00<big endian key id>' or // '0x01<big endian key id>'. - virtual util::StatusOr<std::string> GetOutputPrefix() const = 0; + virtual absl::string_view GetOutputPrefix() const = 0; const MacParameters& GetParameters() const override = 0;
diff --git a/cc/mac/mac_wrapper.cc b/cc/mac/mac_wrapper.cc index f1140bd..039b332 100644 --- a/cc/mac/mac_wrapper.cc +++ b/cc/mac/mac_wrapper.cc
@@ -16,18 +16,19 @@ #include "tink/mac/mac_wrapper.h" +#include <memory> #include <string> #include <utility> #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "tink/crypto_format.h" -#include "tink/internal/util.h" -#include "tink/mac.h" -#include "tink/primitive_set.h" #include "tink/internal/monitoring_util.h" #include "tink/internal/registry_impl.h" +#include "tink/internal/util.h" +#include "tink/mac.h" #include "tink/monitoring/monitoring.h" +#include "tink/primitive_set.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "proto/tink.pb.h" @@ -59,7 +60,7 @@ crypto::tink::util::Status VerifyMac(absl::string_view mac_value, absl::string_view data) const override; - ~MacSetWrapper() override {} + ~MacSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<Mac>> mac_set_;
diff --git a/cc/mac/mac_wrapper.h b/cc/mac/mac_wrapper.h index c7379dc..3876544 100644 --- a/cc/mac/mac_wrapper.h +++ b/cc/mac/mac_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_MAC_MAC_WRAPPER_H_ #define TINK_MAC_MAC_WRAPPER_H_ +#include <memory> + #include "absl/strings/string_view.h" #include "tink/mac.h" #include "tink/primitive_set.h"
diff --git a/cc/mac/mac_wrapper_test.cc b/cc/mac/mac_wrapper_test.cc index 66069f1..d240e50 100644 --- a/cc/mac/mac_wrapper_test.cc +++ b/cc/mac/mac_wrapper_test.cc
@@ -16,6 +16,7 @@ #include "tink/mac/mac_wrapper.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/monitoring/BUILD.bazel b/cc/monitoring/BUILD.bazel index 22d3886..63fc32f 100644 --- a/cc/monitoring/BUILD.bazel +++ b/cc/monitoring/BUILD.bazel
@@ -7,6 +7,8 @@ hdrs = ["monitoring.h"], include_prefix = "tink/monitoring", deps = [ + "//:key_status", + "//internal:key_status_util", "//util:statusor", "@com_google_absl//absl/container:flat_hash_map", ],
diff --git a/cc/monitoring/BUILD.gn b/cc/monitoring/BUILD.gn index b7ce0fc..eae3e72 100644 --- a/cc/monitoring/BUILD.gn +++ b/cc/monitoring/BUILD.gn
@@ -12,6 +12,8 @@ sources = [ "monitoring.h" ] public_deps = [ "//third_party/abseil-cpp/absl/container:flat_hash_map", + "//third_party/tink/cc:key_status", + "//third_party/tink/cc/internal:key_status_util", "//third_party/tink/cc/util:statusor", ] public_configs = [ "//third_party/tink:tink_config" ]
diff --git a/cc/monitoring/CMakeLists.txt b/cc/monitoring/CMakeLists.txt index 9613885..60ad02c 100644 --- a/cc/monitoring/CMakeLists.txt +++ b/cc/monitoring/CMakeLists.txt
@@ -6,6 +6,8 @@ monitoring.h DEPS absl::flat_hash_map + tink::core::key_status + tink::internal::key_status_util tink::util::statusor ) @@ -16,4 +18,5 @@ DEPS tink::monitoring::monitoring gmock + TESTONLY )
diff --git a/cc/monitoring/monitoring.h b/cc/monitoring/monitoring.h index e25e99a..4c4dcc5 100644 --- a/cc/monitoring/monitoring.h +++ b/cc/monitoring/monitoring.h
@@ -17,10 +17,13 @@ #define TINK_MONITORING_MONITORING_H_ #include <cstdint> +#include <memory> #include <string> #include <vector> #include "absl/container/flat_hash_map.h" +#include "tink/internal/key_status_util.h" +#include "tink/key_status.h" #include "tink/util/statusor.h" namespace crypto { @@ -33,41 +36,32 @@ // Description about each entry of the KeySet. class Entry { public: - // Enum representation of KeyStatusType in tink/proto/tink.proto. Using an - // enum class prevents unintentional implicit conversions. - enum class KeyStatus : int { - kEnabled = 1, // Can be used for cryptographic operations. - kDisabled = 2, // Cannot be used (but can become kEnabled again). - kDestroyed = 3, // Key data does not exist in this Keyset any more. - // Added to guard from failures that may be caused by future expansions. - kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements = 20, - }; - - // Constructs a new KeySet entry with a given `status`, `key_id` and key - // format `parameters_as_string`. - Entry(KeyStatus status, uint32_t key_id, - absl::string_view parameters_as_string) + // Constructs a new KeySet entry with a given `status`, `key_id`, + // `key_type`, and `key_prefix`. + Entry(KeyStatus status, uint32_t key_id, absl::string_view key_type, + absl::string_view key_prefix) : status_(status), key_id_(key_id), - parameters_as_string_(parameters_as_string) {} + key_type_(key_type), + key_prefix_(key_prefix) {} // Returns the status of this entry. - KeyStatus GetStatus() const { return status_; } + std::string GetStatus() const { return internal::ToKeyStatusName(status_); } // Returns the ID of the entry within the keyset. uint32_t GetKeyId() const { return key_id_; } - // Returns the parameters in a serialized form. - // - // *WARNING* the actual content of `parameters_as_string_` is considered - // unstable and might change in future versions of Tink. A user should not - // rely on a specific representation of the key_format. - std::string GetParametersAsString() const { return parameters_as_string_; } + // Returns the key type. + std::string GetKeyType() const { return key_type_; } + // Returns the key prefix. + std::string GetKeyPrefix() const { return key_prefix_; } private: const KeyStatus status_; // Identifies a key within a keyset. const uint32_t key_id_; - // This field stores information about the parameters. - const std::string parameters_as_string_; + // This field stores the key type. + const std::string key_type_; + // Stores the key output prefix. + const std::string key_prefix_; }; // Constructs a MonitoringKeySetInfo object with the given
diff --git a/cc/monitoring/monitoring_client_mocks.h b/cc/monitoring/monitoring_client_mocks.h index 8a59b47..34b5cbd 100644 --- a/cc/monitoring/monitoring_client_mocks.h +++ b/cc/monitoring/monitoring_client_mocks.h
@@ -13,8 +13,8 @@ // limitations under the License. // /////////////////////////////////////////////////////////////////////////////// -#ifndef TINK_MONITORING_MOCK_MONITORING_CLIENT_FACTORY_H_ -#define TINK_MONITORING_MOCK_MONITORING_CLIENT_FACTORY_H_ +#ifndef TINK_MONITORING_MONITORING_CLIENT_MOCKS_H_ +#define TINK_MONITORING_MONITORING_CLIENT_MOCKS_H_ #include <cstdint> @@ -42,4 +42,4 @@ } // namespace tink } // namespace crypto -#endif // TINK_MONITORING_MOCK_MONITORING_CLIENT_FACTORY_H_ +#endif // TINK_MONITORING_MONITORING_CLIENT_MOCKS_H_
diff --git a/cc/output_stream.h b/cc/output_stream.h index c7de9b3..969e153 100644 --- a/cc/output_stream.h +++ b/cc/output_stream.h
@@ -27,8 +27,8 @@ // Protocol Buffers' google::protobuf::io::ZeroCopyOutputStream. class OutputStream { public: - OutputStream() {} - virtual ~OutputStream() {} + OutputStream() = default; + virtual ~OutputStream() = default; // Obtains a buffer into which data can be written. Any data written // into this buffer will eventually (maybe instantly, maybe later on)
diff --git a/cc/output_stream_with_result.h b/cc/output_stream_with_result.h index 882a075..ee7759f 100644 --- a/cc/output_stream_with_result.h +++ b/cc/output_stream_with_result.h
@@ -59,7 +59,7 @@ class OutputStreamWithResult : public OutputStream { public: OutputStreamWithResult() : closed_(false) {} - ~OutputStreamWithResult() override {} + ~OutputStreamWithResult() override = default; // The return type is StatusOr<T> if T != Status, and Status otherwise. using ResultType =
diff --git a/cc/partial_key_access.h b/cc/partial_key_access.h new file mode 100644 index 0000000..f029faf --- /dev/null +++ b/cc/partial_key_access.h
@@ -0,0 +1,41 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_PARTIAL_KEY_ACCESS_H_ +#define TINK_PARTIAL_KEY_ACCESS_H_ + +#include "tink/partial_key_access_token.h" + +namespace crypto { +namespace tink { + +// Returns a `PartialKeyAccessToken`. +// +// Accessing parts of keys can produce unexpected incompatibilities: +// https://developers.google.com/tink/design/access_control#accessing_partial_keys +// +// This function can be used to access partial key material. Within Google, +// access to this function is restricted by the build system. Outside of Google, +// users can search their codebase for `GetPartialKeyAccess()` to find +// instances where it is used. +inline PartialKeyAccessToken GetPartialKeyAccess() { + return PartialKeyAccessToken(); +} + +} // namespace tink +} // namespace crypto + +#endif // TINK_PARTIAL_KEY_ACCESS_H_
diff --git a/cc/partial_key_access_token.h b/cc/partial_key_access_token.h new file mode 100644 index 0000000..fe5a180 --- /dev/null +++ b/cc/partial_key_access_token.h
@@ -0,0 +1,42 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_PARTIAL_KEY_ACCESS_TOKEN_H_ +#define TINK_PARTIAL_KEY_ACCESS_TOKEN_H_ + +namespace crypto { +namespace tink { + +class PartialKeyAccessToken { + public: + // Copyable and movable. + PartialKeyAccessToken(const PartialKeyAccessToken& other) = default; + PartialKeyAccessToken& operator=(const PartialKeyAccessToken& other) = + default; + PartialKeyAccessToken(PartialKeyAccessToken&& other) = default; + PartialKeyAccessToken& operator=(PartialKeyAccessToken&& other) = default; + + private: + // `GetPartialKeyAccess()` requires access to the constructor. + friend PartialKeyAccessToken GetPartialKeyAccess(); + + PartialKeyAccessToken() = default; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_PARTIAL_KEY_ACCESS_TOKEN_H_
diff --git a/cc/prf/BUILD.bazel b/cc/prf/BUILD.bazel index cf4d7b6..40eb6ab 100644 --- a/cc/prf/BUILD.bazel +++ b/cc/prf/BUILD.bazel
@@ -90,11 +90,15 @@ ":prf_set", "//:primitive_set", "//:primitive_wrapper", + "//internal:monitoring_util", + "//internal:registry_impl", + "//monitoring", "//proto:tink_cc_proto", "//util:status", "//util:statusor", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", ], ) @@ -195,10 +199,14 @@ ":prf_set", ":prf_set_wrapper", "//:primitive_set", + "//:registry", + "//monitoring:monitoring_client_mocks", "//proto:tink_cc_proto", + "//util:status", "//util:statusor", "//util:test_matchers", "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -265,9 +273,8 @@ ":prf_config", ":prf_key_templates", ":prf_set", - "//:config", "//:tink_cc", - "//config:tink_fips", + "//internal:fips_utils", "//util:status", "//util:test_matchers", "//util:test_util",
diff --git a/cc/prf/CMakeLists.txt b/cc/prf/CMakeLists.txt index 028587e..ebe61bc 100644 --- a/cc/prf/CMakeLists.txt +++ b/cc/prf/CMakeLists.txt
@@ -79,8 +79,12 @@ tink::prf::prf_set absl::memory absl::status + absl::statusor tink::core::primitive_set tink::core::primitive_wrapper + tink::internal::monitoring_util + tink::internal::registry_impl + tink::monitoring::monitoring tink::util::status tink::util::statusor tink::proto::tink_cc_proto @@ -183,8 +187,12 @@ tink::prf::prf_set_wrapper gmock absl::memory + absl::status absl::strings tink::core::primitive_set + tink::core::registry + tink::monitoring::monitoring_client_mocks + tink::util::status tink::util::statusor tink::util::test_matchers tink::proto::tink_cc_proto @@ -255,8 +263,7 @@ absl::status crypto tink::core::cc - tink::core::config - tink::config::tink_fips + tink::internal::fips_utils tink::util::status tink::util::test_matchers tink::util::test_util
diff --git a/cc/prf/aes_cmac_prf_key_manager.h b/cc/prf/aes_cmac_prf_key_manager.h index fdb5844..3d7c26a 100644 --- a/cc/prf/aes_cmac_prf_key_manager.h +++ b/cc/prf/aes_cmac_prf_key_manager.h
@@ -17,6 +17,7 @@ #define TINK_PRF_AES_CMAC_PRF_KEY_MANAGER_H_ #include <algorithm> +#include <memory> #include <string> #include <vector>
diff --git a/cc/prf/aes_cmac_prf_key_manager_test.cc b/cc/prf/aes_cmac_prf_key_manager_test.cc index e08f861..7ca8c40 100644 --- a/cc/prf/aes_cmac_prf_key_manager_test.cc +++ b/cc/prf/aes_cmac_prf_key_manager_test.cc
@@ -15,6 +15,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "tink/prf/aes_cmac_prf_key_manager.h" +#include <memory> #include <sstream> #include <string>
diff --git a/cc/prf/hkdf_prf_key_manager.h b/cc/prf/hkdf_prf_key_manager.h index fe860a3..0140e4b 100644 --- a/cc/prf/hkdf_prf_key_manager.h +++ b/cc/prf/hkdf_prf_key_manager.h
@@ -17,6 +17,7 @@ #ifndef TINK_PRF_HKDF_PRF_KEY_MANAGER_H_ #define TINK_PRF_HKDF_PRF_KEY_MANAGER_H_ +#include <memory> #include <string> #include <utility>
diff --git a/cc/prf/hkdf_prf_key_manager_test.cc b/cc/prf/hkdf_prf_key_manager_test.cc index d985694..155cd5c 100644 --- a/cc/prf/hkdf_prf_key_manager_test.cc +++ b/cc/prf/hkdf_prf_key_manager_test.cc
@@ -16,6 +16,8 @@ #include "tink/prf/hkdf_prf_key_manager.h" +#include <memory> +#include <sstream> #include <string> #include <utility>
diff --git a/cc/prf/hmac_prf_key_manager.h b/cc/prf/hmac_prf_key_manager.h index 4074cfc..89113d0 100644 --- a/cc/prf/hmac_prf_key_manager.h +++ b/cc/prf/hmac_prf_key_manager.h
@@ -17,6 +17,7 @@ #define TINK_PRF_HMAC_PRF_KEY_MANAGER_H_ #include <algorithm> +#include <map> #include <memory> #include <string> #include <vector>
diff --git a/cc/prf/hmac_prf_key_manager_test.cc b/cc/prf/hmac_prf_key_manager_test.cc index d1453f0..e5ad56d 100644 --- a/cc/prf/hmac_prf_key_manager_test.cc +++ b/cc/prf/hmac_prf_key_manager_test.cc
@@ -16,6 +16,8 @@ #include "tink/prf/hmac_prf_key_manager.h" +#include <sstream> + #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/status/status.h" @@ -39,7 +41,6 @@ using ::google::crypto::tink::HashType; using ::google::crypto::tink::HmacPrfKey; using ::google::crypto::tink::HmacPrfKeyFormat; -using ::google::crypto::tink::KeyData; using ::testing::HasSubstr; using ::testing::Not; using ::testing::SizeIs;
diff --git a/cc/prf/prf_config_test.cc b/cc/prf/prf_config_test.cc index eb685de..f86df0b 100644 --- a/cc/prf/prf_config_test.cc +++ b/cc/prf/prf_config_test.cc
@@ -20,8 +20,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/status/status.h" -#include "tink/config.h" -#include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/keyset_handle.h" #include "tink/prf/hmac_prf_key_manager.h" #include "tink/prf/prf_key_templates.h" @@ -44,7 +43,7 @@ }; TEST_F(PrfConfigTest, RegisterWorks) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Not supported in FIPS-only mode"; } @@ -59,7 +58,7 @@ // FIPS-only mode tests TEST_F(PrfConfigTest, RegisterNonFipsTemplates) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-only mode"; } @@ -77,7 +76,7 @@ } TEST_F(PrfConfigTest, RegisterFipsValidTemplates) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-only mode"; }
diff --git a/cc/prf/prf_key_templates.cc b/cc/prf/prf_key_templates.cc index 92c0378..fb8003f 100644 --- a/cc/prf/prf_key_templates.cc +++ b/cc/prf/prf_key_templates.cc
@@ -15,6 +15,8 @@ //////////////////////////////////////////////////////////////////////////////// #include "tink/prf/prf_key_templates.h" +#include <memory> + #include "absl/memory/memory.h" #include "tink/prf/aes_cmac_prf_key_manager.h" #include "tink/prf/hkdf_prf_key_manager.h"
diff --git a/cc/prf/prf_set.h b/cc/prf/prf_set.h index 51c0636..ddf630b 100644 --- a/cc/prf/prf_set.h +++ b/cc/prf/prf_set.h
@@ -46,7 +46,7 @@ // for non-deterministic MAC algorithms. class Prf { public: - virtual ~Prf() {} + virtual ~Prf() = default; // Computes the PRF selected by the underlying key on input and // returns the first outputLength bytes. // When choosing this parameter keep the birthday paradox in mind. @@ -67,7 +67,7 @@ // the Keyset. class PrfSet { public: - virtual ~PrfSet() {} + virtual ~PrfSet() = default; // The primary ID of the keyset. virtual uint32_t GetPrimaryId() const = 0; // A map of the PRFs represented by the keys in this keyset.
diff --git a/cc/prf/prf_set_test.cc b/cc/prf/prf_set_test.cc index f76ed88..00e5038 100644 --- a/cc/prf/prf_set_test.cc +++ b/cc/prf/prf_set_test.cc
@@ -17,8 +17,10 @@ #include "tink/prf/prf_set.h" #include <map> +#include <memory> #include <string> #include <utility> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/prf/prf_set_wrapper.cc b/cc/prf/prf_set_wrapper.cc index f244a27..af81b56 100644 --- a/cc/prf/prf_set_wrapper.cc +++ b/cc/prf/prf_set_wrapper.cc
@@ -15,10 +15,20 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/prf/prf_set_wrapper.h" +#include <cstdint> +#include <map> +#include <memory> +#include <string> #include <utility> +#include <vector> #include "absl/memory/memory.h" #include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "tink/internal/monitoring_util.h" +#include "tink/internal/registry_impl.h" +#include "tink/monitoring/monitoring.h" +#include "tink/prf/prf_set.h" #include "tink/util/status.h" #include "proto/tink.pb.h" @@ -29,12 +39,59 @@ namespace { +constexpr absl::string_view kPrimitive = "prf"; +constexpr absl::string_view kComputeApi = "compute"; + +class MonitoredPrf : public Prf { + public: + explicit MonitoredPrf(uint32_t key_id, const Prf* prf, + MonitoringClient* monitoring_client) + : key_id_(key_id), prf_(prf), monitoring_client_(monitoring_client) {} + ~MonitoredPrf() override = default; + + MonitoredPrf(MonitoredPrf&& other) = default; + MonitoredPrf& operator=(MonitoredPrf&& other) = default; + + MonitoredPrf(const MonitoredPrf&) = delete; + MonitoredPrf& operator=(const MonitoredPrf&) = delete; + + util::StatusOr<std::string> Compute(absl::string_view input, + size_t output_length) const override { + util::StatusOr<std::string> result = prf_->Compute(input, output_length); + if (!result.ok()) { + if (monitoring_client_ != nullptr) { + monitoring_client_->LogFailure(); + } + return result.status(); + } + + if (monitoring_client_ != nullptr) { + monitoring_client_->Log(key_id_, input.size()); + } + return result.value(); + } + + private: + uint32_t key_id_; + const Prf* prf_; + MonitoringClient* monitoring_client_; +}; + class PrfSetPrimitiveWrapper : public PrfSet { public: - explicit PrfSetPrimitiveWrapper(std::unique_ptr<PrimitiveSet<Prf>> prf_set) - : prf_set_(std::move(prf_set)) { + explicit PrfSetPrimitiveWrapper( + std::unique_ptr<PrimitiveSet<Prf>> prf_set, + std::unique_ptr<MonitoringClient> monitoring_client = nullptr) + : prf_set_(std::move(prf_set)), + monitoring_client_(std::move(monitoring_client)) { + wrapped_prfs_.reserve(prf_set_->get_raw_primitives().value()->size()); for (const auto& prf : *prf_set_->get_raw_primitives().value()) { - prfs_.insert({prf->get_key_id(), &prf->get_primitive()}); + std::unique_ptr<Prf> wrapped_prf = std::make_unique<MonitoredPrf>( + prf->get_key_id(), &prf->get_primitive(), + monitoring_client_.get()); + + prfs_.insert({prf->get_key_id(), wrapped_prf.get()}); + wrapped_prfs_.push_back(std::move(wrapped_prf)); } } @@ -43,10 +100,12 @@ } const std::map<uint32_t, Prf*>& GetPrfs() const override { return prfs_; } - ~PrfSetPrimitiveWrapper() override {} + ~PrfSetPrimitiveWrapper() override = default; private: std::unique_ptr<PrimitiveSet<Prf>> prf_set_; + std::unique_ptr<MonitoringClient> monitoring_client_; + std::vector<std::unique_ptr<Prf>> wrapped_prfs_; std::map<uint32_t, Prf*> prfs_; }; @@ -74,7 +133,26 @@ std::unique_ptr<PrimitiveSet<Prf>> prf_set) const { util::Status status = Validate(prf_set.get()); if (!status.ok()) return status; - return {absl::make_unique<PrfSetPrimitiveWrapper>(std::move(prf_set))}; + + MonitoringClientFactory* const monitoring_factory = + internal::RegistryImpl::GlobalInstance().GetMonitoringClientFactory(); + // Monitoring is not enabled. Create a wrapper without monitoring clients. + if (monitoring_factory == nullptr) { + return {absl::make_unique<PrfSetPrimitiveWrapper>(std::move(prf_set))}; + } + util::StatusOr<MonitoringKeySetInfo> keyset_info = + internal::MonitoringKeySetInfoFromPrimitiveSet(*prf_set); + if (!keyset_info.ok()) { + return keyset_info.status(); + } + util::StatusOr<std::unique_ptr<MonitoringClient>> monitoring_client = + monitoring_factory->New( + MonitoringContext(kPrimitive, kComputeApi, *keyset_info)); + if (!monitoring_client.ok()) { + return monitoring_client.status(); + } + return {absl::make_unique<PrfSetPrimitiveWrapper>( + std::move(prf_set), *std::move(monitoring_client))}; } } // namespace tink
diff --git a/cc/prf/prf_set_wrapper_test.cc b/cc/prf/prf_set_wrapper_test.cc index 095c672..b100de1 100644 --- a/cc/prf/prf_set_wrapper_test.cc +++ b/cc/prf/prf_set_wrapper_test.cc
@@ -16,6 +16,7 @@ #include "tink/prf/prf_set_wrapper.h" #include <cstdint> +#include <map> #include <memory> #include <string> #include <utility> @@ -23,9 +24,13 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/memory/memory.h" +#include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "tink/monitoring/monitoring_client_mocks.h" #include "tink/prf/prf_set.h" #include "tink/primitive_set.h" +#include "tink/registry.h" +#include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" #include "proto/tink.pb.h" @@ -38,11 +43,24 @@ using ::crypto::tink::test::IsOkAndHolds; using ::google::crypto::tink::KeysetInfo; using ::google::crypto::tink::KeyStatusType; +using ::testing::_; +using ::testing::ByMove; using ::testing::Key; +using ::testing::NiceMock; using ::testing::Not; +using ::testing::Return; using ::testing::StrEq; +using ::testing::Test; using ::testing::UnorderedElementsAre; +KeysetInfo::KeyInfo MakeKey(uint32_t id) { + KeysetInfo::KeyInfo key; + key.set_output_prefix_type(google::crypto::tink::OutputPrefixType::RAW); + key.set_key_id(id); + key.set_status(KeyStatusType::ENABLED); + return key; +} + class FakePrf : public Prf { public: explicit FakePrf(const std::string& output) : output_(output) {} @@ -65,14 +83,6 @@ return prf_set_->AddPrimitive(std::move(prf), key_info); } - KeysetInfo::KeyInfo MakeKey(uint32_t id) { - KeysetInfo::KeyInfo key; - key.set_output_prefix_type(google::crypto::tink::OutputPrefixType::RAW); - key.set_key_id(id); - key.set_status(KeyStatusType::ENABLED); - return key; - } - std::unique_ptr<PrimitiveSet<Prf>>& PrfSet() { return prf_set_; } private: @@ -134,6 +144,104 @@ IsOkAndHolds(StrEq("different"))); } +// Tests for the monitoring behavior. +class PrfSetWrapperWithMonitoringTest : public Test { + protected: + // Reset the global registry. + void SetUp() override { + Registry::Reset(); + // Setup mocks for catching Monitoring calls. + auto monitoring_client_factory = + absl::make_unique<MockMonitoringClientFactory>(); + auto monitoring_client = + absl::make_unique<NiceMock<MockMonitoringClient>>(); + monitoring_client_ref_ = monitoring_client.get(); + // Monitoring tests expect that the client factory will create the + // corresponding MockMonitoringClients. + EXPECT_CALL(*monitoring_client_factory, New(_)) + .WillOnce( + Return(ByMove(util::StatusOr<std::unique_ptr<MonitoringClient>>( + std::move(monitoring_client))))); + + ASSERT_THAT(internal::RegistryImpl::GlobalInstance() + .RegisterMonitoringClientFactory( + std::move(monitoring_client_factory)), + IsOk()); + ASSERT_THAT( + internal::RegistryImpl::GlobalInstance().GetMonitoringClientFactory(), + Not(testing::IsNull())); + } + + // Cleanup the registry to avoid mock leaks. + ~PrfSetWrapperWithMonitoringTest() override { Registry::Reset(); } + + NiceMock<MockMonitoringClient>* monitoring_client_ref_; +}; + +class AlwaysFailingPrf : public Prf { + public: + AlwaysFailingPrf() = default; + + util::StatusOr<std::string> Compute(absl::string_view input, + size_t output_length) const override { + return util::Status(absl::StatusCode::kOutOfRange, "AlwaysFailingPrf"); + } +}; + +TEST_F(PrfSetWrapperWithMonitoringTest, WrapKeysetWithMonitoringFailure) { + const absl::flat_hash_map<std::string, std::string> annotations = { + {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}}; + auto primitive_set = absl::make_unique<PrimitiveSet<Prf>>(annotations); + util::StatusOr<PrimitiveSet<Prf>::Entry<Prf>*> entry = + primitive_set->AddPrimitive(absl::make_unique<AlwaysFailingPrf>(), + MakeKey(/*id=*/1)); + ASSERT_THAT(entry, IsOk()); + ASSERT_THAT(primitive_set->set_primary(entry.value()), IsOk()); + ASSERT_THAT(primitive_set + ->AddPrimitive(absl::make_unique<FakePrf>("output"), + MakeKey(/*id=*/1)) + .status(), + IsOk()); + util::StatusOr<std::unique_ptr<PrfSet>> prf_set = + PrfSetWrapper().Wrap(std::move(primitive_set)); + ASSERT_THAT(prf_set, IsOk()); + EXPECT_CALL(*monitoring_client_ref_, LogFailure()); + EXPECT_THAT((*prf_set)->ComputePrimary("input", /*output_length=*/16), + Not(IsOk())); +} + +TEST_F(PrfSetWrapperWithMonitoringTest, WrapKeysetWithMonitoringVerifySuccess) { + const absl::flat_hash_map<std::string, std::string> annotations = { + {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}}; + auto primitive_set = absl::make_unique<PrimitiveSet<Prf>>(annotations); + + util::StatusOr<PrimitiveSet<Prf>::Entry<Prf>*> entry = + primitive_set->AddPrimitive(absl::make_unique<FakePrf>("output"), + MakeKey(/*id=*/1)); + ASSERT_THAT(entry, IsOk()); + ASSERT_THAT(primitive_set->set_primary(entry.value()), IsOk()); + ASSERT_THAT(primitive_set + ->AddPrimitive(absl::make_unique<FakePrf>("output"), + MakeKey(/*id=*/1)) + .status(), + IsOk()); + + util::StatusOr<std::unique_ptr<PrfSet>> prf_set = + PrfSetWrapper().Wrap(std::move(primitive_set)); + ASSERT_THAT(prf_set, IsOk()); + std::map<uint32_t, Prf*> prf_map = (*prf_set)->GetPrfs(); + std::string input = "input"; + for (const auto& entry : prf_map) { + EXPECT_CALL(*monitoring_client_ref_, Log(entry.first, input.size())); + EXPECT_THAT((entry.second)->Compute(input, /*output_length=*/16).status(), + IsOk()); + } + input = "hello_world"; + EXPECT_CALL(*monitoring_client_ref_, + Log((*prf_set)->GetPrimaryId(), input.size())); + EXPECT_THAT((*prf_set)->ComputePrimary(input, /*output_length=*/16), IsOk()); +} + } // namespace } // namespace tink } // namespace crypto
diff --git a/cc/primitive_set.h b/cc/primitive_set.h index 76ae5ce..9f6439c 100644 --- a/cc/primitive_set.h +++ b/cc/primitive_set.h
@@ -17,10 +17,13 @@ #ifndef TINK_PRIMITIVE_SET_H_ #define TINK_PRIMITIVE_SET_H_ +#include <algorithm> +#include <memory> #include <string> -#include <unordered_map> +#include <utility> #include <vector> +#include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/memory/memory.h" #include "absl/status/status.h" @@ -113,48 +116,16 @@ }; typedef std::vector<std::unique_ptr<Entry<P>>> Primitives; + typedef absl::flat_hash_map<std::string, Primitives> + CiphertextPrefixToPrimitivesMap; - // Constructs an empty PrimitiveSet. - // Note: This is equivalent to PrimitiveSet<P>(/*annotations=*/{}). - PrimitiveSet<P>() = default; - // Constructs an empty PrimitiveSet with `annotations`. - explicit PrimitiveSet<P>( - const absl::flat_hash_map<std::string, std::string>& annotations) - : annotations_(annotations) {} + private: + // Helper methods for mutations, used by the Builder and the deprecated + // mutation methods on PrimitiveSet. - // Adds 'primitive' to this set for the specified 'key'. - crypto::tink::util::StatusOr<Entry<P>*> AddPrimitive( - std::unique_ptr<P> primitive, - const google::crypto::tink::KeysetInfo::KeyInfo& key_info) { - auto entry_or = Entry<P>::New(std::move(primitive), key_info); - if (!entry_or.ok()) return entry_or.status(); - - absl::MutexLock lock(&primitives_mutex_); - std::string identifier = entry_or.value()->get_identifier(); - primitives_[identifier].push_back(std::move(entry_or.value())); - return primitives_[identifier].back().get(); - } - - // Returns the entries with primitives identifed by 'identifier'. - crypto::tink::util::StatusOr<const Primitives*> get_primitives( - absl::string_view identifier) { - absl::MutexLock lock(&primitives_mutex_); - typename CiphertextPrefixToPrimitivesMap::iterator found = - primitives_.find(std::string(identifier)); - if (found == primitives_.end()) { - return ToStatusF(absl::StatusCode::kNotFound, - "No primitives found for identifier '%s'.", identifier); - } - return &(found->second); - } - - // Returns all primitives that use RAW prefix. - crypto::tink::util::StatusOr<const Primitives*> get_raw_primitives() { - return get_primitives(CryptoFormat::kRawPrefix); - } - - // Sets the given 'primary' as the primary primitive of this set. - crypto::tink::util::Status set_primary(Entry<P>* primary) { + static crypto::tink::util::Status SetPrimaryImpl( + Entry<P>** output, Entry<P>* primary, + const CiphertextPrefixToPrimitivesMap& primitives) { if (!primary) { return util::Status(absl::StatusCode::kInvalidArgument, "The primary primitive must be non-null."); @@ -163,23 +134,190 @@ return util::Status(absl::StatusCode::kInvalidArgument, "Primary has to be enabled."); } - auto entries_result = get_primitives(primary->get_identifier()); - if (!entries_result.ok()) { + + if (primitives.count(primary->get_identifier()) == 0) { return util::Status(absl::StatusCode::kInvalidArgument, "Primary cannot be set to an entry which is " "not held by this primitive set."); } - primary_ = primary; + *output = primary; return crypto::tink::util::OkStatus(); } - // Returns the entry with the primary primitive. - const Entry<P>* get_primary() const { return primary_; } + static crypto::tink::util::StatusOr<Entry<P>*> AddPrimitiveImpl( + std::unique_ptr<P> primitive, + const google::crypto::tink::KeysetInfo::KeyInfo& key_info, + CiphertextPrefixToPrimitivesMap& primitives, + std::vector<Entry<P>*>& primitives_in_keyset_order) { + auto entry_or = Entry<P>::New(std::move(primitive), key_info); + if (!entry_or.ok()) return entry_or.status(); - // Returns all entries currently in this primitive set. - const std::vector<Entry<P>*> get_all() const { - absl::MutexLock lock(&primitives_mutex_); + std::string identifier = entry_or.value()->get_identifier(); + primitives[identifier].push_back(std::move(entry_or.value())); + + Entry<P>* stored_entry = primitives[identifier].back().get(); + primitives_in_keyset_order.push_back(stored_entry); + return stored_entry; + } + + public: + // Builder is used to construct PrimitiveSet objects. Objects returned by + // the builder are immutable. Calling any of the non-const methods on them + // will fail. + class Builder { + public: + // Adds 'primitive' to this set for the specified 'key'. + Builder& AddPrimitive( + std::unique_ptr<P> primitive, + const google::crypto::tink::KeysetInfo::KeyInfo& key_info) & { + absl::MutexLock lock(&mutex_); + if (!status_.ok()) return *this; + status_ = AddPrimitiveImpl(std::move(primitive), key_info, primitives_, + primitives_in_keyset_order_) + .status(); + return *this; + } + + Builder&& AddPrimitive( + std::unique_ptr<P> primitive, + const google::crypto::tink::KeysetInfo::KeyInfo& key_info) && { + return std::move(AddPrimitive(std::move(primitive), key_info)); + } + + // Adds 'primitive' to this set for the specified 'key' and marks it + // primary. + Builder& AddPrimaryPrimitive( + std::unique_ptr<P> primitive, + const google::crypto::tink::KeysetInfo::KeyInfo& key_info) & { + absl::MutexLock lock(&mutex_); + if (!status_.ok()) return *this; + auto entry_result = + AddPrimitiveImpl(std::move(primitive), key_info, primitives_, + primitives_in_keyset_order_); + if (!entry_result.ok()) { + status_ = entry_result.status(); + return *this; + } + status_ = SetPrimaryImpl(&primary_, entry_result.value(), primitives_); + return *this; + } + + Builder&& AddPrimaryPrimitive( + std::unique_ptr<P> primitive, + const google::crypto::tink::KeysetInfo::KeyInfo& key_info) && { + return std::move(AddPrimaryPrimitive(std::move(primitive), key_info)); + } + + // Add the given annotations. Existing annotations will not be overwritten. + Builder& AddAnnotations( + absl::flat_hash_map<std::string, std::string> annotations) & { + absl::MutexLock lock(&mutex_); + annotations_.merge(std::move(annotations)); + return *this; + } + + Builder&& AddAnnotations( + absl::flat_hash_map<std::string, std::string> annotations) && { + return std::move(AddAnnotations(std::move(annotations))); + } + + crypto::tink::util::StatusOr<PrimitiveSet<P>> Build() && { + absl::MutexLock lock(&mutex_); + if (!status_.ok()) return status_; + return PrimitiveSet<P>(std::move(primitives_), primary_, + std::move(primitives_in_keyset_order_), + std::move(annotations_)); + } + + private: + // Owned by primitives_. + Entry<P>* primary_ ABSL_GUARDED_BY(mutex_) = nullptr; + CiphertextPrefixToPrimitivesMap primitives_ ABSL_GUARDED_BY(mutex_); + // Entries in the original keyset key order, all owned by primitives_. + std::vector<Entry<P>*> primitives_in_keyset_order_ ABSL_GUARDED_BY(mutex_); + absl::flat_hash_map<std::string, std::string> annotations_ + ABSL_GUARDED_BY(mutex_); + absl::Mutex mutex_; + crypto::tink::util::Status status_ ABSL_GUARDED_BY(mutex_); + }; + + // PrimitiveSet is movable, but not copyable + PrimitiveSet(PrimitiveSet&&) = default; + PrimitiveSet<P>& operator=(PrimitiveSet&&) = default; + PrimitiveSet(const PrimitiveSet&) = delete; + PrimitiveSet<P>& operator=(const PrimitiveSet&) = delete; + + // Constructs an empty PrimitiveSet. + // Note: This is equivalent to PrimitiveSet<P>(/*annotations=*/{}). + ABSL_DEPRECATED( + "Constructing PrimitiveSet using constructors is deprecated. Use " + "PrimitiveSet<>::Builder instead.") + PrimitiveSet<P>() = default; + // Constructs an empty PrimitiveSet with `annotations`. + ABSL_DEPRECATED( + "Constructing PrimitiveSet using constructors is deprecated. Use " + "PrimitiveSet<>::Builder instead.") + explicit PrimitiveSet<P>( + const absl::flat_hash_map<std::string, std::string>& annotations) + : annotations_(annotations) {} + + // Adds 'primitive' to this set for the specified 'key'. + ABSL_DEPRECATED( + "Mutating PrimitiveSets after construction is deprecated. Use " + "PrimitiveSet<>::Builder instead.") + crypto::tink::util::StatusOr<Entry<P>*> AddPrimitive( + std::unique_ptr<P> primitive, + const google::crypto::tink::KeysetInfo::KeyInfo& key_info) { + if (!is_mutable()) { + return util::Status(absl::StatusCode::kFailedPrecondition, + "PrimitiveSet is not mutable."); + } + + absl::MutexLock lock(primitives_mutex_.get()); + return AddPrimitiveImpl(std::move(primitive), key_info, primitives_, + primitives_in_keyset_order_); + } + + // Returns the entries with primitives identified by 'identifier'. + crypto::tink::util::StatusOr<const Primitives*> get_primitives( + absl::string_view identifier) const { + absl::MutexLockMaybe lock(primitives_mutex_.get()); + auto found = primitives_.find(std::string(identifier)); + if (found == primitives_.end()) { + return ToStatusF(absl::StatusCode::kNotFound, + "No primitives found for identifier '%s'.", identifier); + } + return &(found->second); + } + + // Returns all primitives that use RAW prefix. + crypto::tink::util::StatusOr<const Primitives*> get_raw_primitives() const { + return get_primitives(CryptoFormat::kRawPrefix); + } + + // Sets the given 'primary' as the primary primitive of this set. + ABSL_DEPRECATED( + "Mutating PrimitiveSets after construction is deprecated. Use " + "PrimitiveSet<>::Builder instead.") + crypto::tink::util::Status set_primary(Entry<P>* primary) { + if (!is_mutable()) { + return util::Status(absl::StatusCode::kFailedPrecondition, + "PrimitiveSet is not mutable."); + } + absl::MutexLock lock(primitives_mutex_.get()); + return SetPrimaryImpl(&primary_, primary, primitives_); + } + + // Returns the entry with the primary primitive. + const Entry<P>* get_primary() const { + absl::MutexLockMaybe lock(primitives_mutex_.get()); + return primary_; + } + + // Returns all entries. + std::vector<Entry<P>*> get_all() const { + absl::MutexLockMaybe lock(primitives_mutex_.get()); std::vector<Entry<P>*> result; for (const auto& prefix_and_vector : primitives_) { for (const auto& primitive : prefix_and_vector.second) { @@ -189,21 +327,43 @@ return result; } + // Returns all entries in the original keyset key order. + std::vector<Entry<P>*> get_all_in_keyset_order() const { + absl::MutexLockMaybe lock(primitives_mutex_.get()); + return primitives_in_keyset_order_; + } + const absl::flat_hash_map<std::string, std::string>& get_annotations() const { return annotations_; } + bool is_mutable() const { return primitives_mutex_ != nullptr; } + private: - typedef std::unordered_map<std::string, Primitives> - CiphertextPrefixToPrimitivesMap; - // The Entry<P> object is owned by primitives_ - Entry<P>* primary_ = nullptr; - mutable absl::Mutex primitives_mutex_; + // Constructs an empty PrimitiveSet. + // Note: This is equivalent to PrimitiveSet<P>(/*annotations=*/{}). + PrimitiveSet(CiphertextPrefixToPrimitivesMap primitives, Entry<P>* primary, + std::vector<Entry<P>*> primitives_in_keyset_order, + absl::flat_hash_map<std::string, std::string> annotations) + : primary_(primary), + primitives_mutex_(nullptr), + primitives_(std::move(primitives)), + primitives_in_keyset_order_(std::move(primitives_in_keyset_order)), + annotations_(std::move(annotations)) {} + + // Owned by primitives_. + Entry<P>* primary_ ABSL_GUARDED_BY(primitives_mutex_) = nullptr; + // If primitives_mutex_ is a nullptr, PrimitiveSet is immutable and lock-free. + // If not nullptr, primitives_mutex_ guards all read and write access. + mutable std::unique_ptr<absl::Mutex> primitives_mutex_ = + absl::make_unique<absl::Mutex>(); CiphertextPrefixToPrimitivesMap primitives_ ABSL_GUARDED_BY(primitives_mutex_); + // Entries in the original keyset key order, all owned by primitives_. + std::vector<Entry<P>*> primitives_in_keyset_order_ + ABSL_GUARDED_BY(primitives_mutex_); - // Annotations for the set of primitives. - const absl::flat_hash_map<std::string, std::string> annotations_; + absl::flat_hash_map<std::string, std::string> annotations_; }; } // namespace tink
diff --git a/cc/primitive_wrapper.h b/cc/primitive_wrapper.h index beadd8b..1ba6f22 100644 --- a/cc/primitive_wrapper.h +++ b/cc/primitive_wrapper.h
@@ -31,10 +31,15 @@ // // PrimitiveWrappers need to be written for every new primitive. They can be // registered in the registry to be fully integrated in Tink. -template <typename InputPrimitive, typename Primitive> +template <typename InputPrimitiveParam, typename PrimitiveParam> class PrimitiveWrapper { public: - virtual ~PrimitiveWrapper() {} + virtual ~PrimitiveWrapper() = default; + + // Useful when writing templated code. + using InputPrimitive = InputPrimitiveParam; + using Primitive = PrimitiveParam; + virtual crypto::tink::util::StatusOr<std::unique_ptr<Primitive>> Wrap( std::unique_ptr<PrimitiveSet<InputPrimitive>> primitive_set) const = 0; };
diff --git a/cc/proto/aes_cmac.proto b/cc/proto/aes_cmac.proto index b214834..541ff58 100644 --- a/cc/proto/aes_cmac.proto +++ b/cc/proto/aes_cmac.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_cmac_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_cmac_go_proto"; message AesCmacParams { uint32 tag_size = 1;
diff --git a/cc/proto/aes_cmac_prf.proto b/cc/proto/aes_cmac_prf.proto index 58e5f67..b2efc6d 100644 --- a/cc/proto/aes_cmac_prf.proto +++ b/cc/proto/aes_cmac_prf.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_cmac_prf_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_cmac_prf_go_proto"; // key_type: type.googleapis.com/google.crypto.tink.AesCmacPrfKey message AesCmacPrfKey {
diff --git a/cc/proto/aes_ctr.proto b/cc/proto/aes_ctr.proto index ecdb256..721699c 100644 --- a/cc/proto/aes_ctr.proto +++ b/cc/proto/aes_ctr.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_ctr_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_ctr_go_proto"; message AesCtrParams { uint32 iv_size = 1;
diff --git a/cc/proto/aes_ctr_hmac_aead.proto b/cc/proto/aes_ctr_hmac_aead.proto index dcf541d..91ccb9b 100644 --- a/cc/proto/aes_ctr_hmac_aead.proto +++ b/cc/proto/aes_ctr_hmac_aead.proto
@@ -23,7 +23,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_ctr_hmac_aead_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_ctr_hmac_aead_go_proto"; message AesCtrHmacAeadKeyFormat { AesCtrKeyFormat aes_ctr_key_format = 1;
diff --git a/cc/proto/aes_ctr_hmac_streaming.proto b/cc/proto/aes_ctr_hmac_streaming.proto index 064b630..776e9bd 100644 --- a/cc/proto/aes_ctr_hmac_streaming.proto +++ b/cc/proto/aes_ctr_hmac_streaming.proto
@@ -23,7 +23,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_ctr_hmac_streaming_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_ctr_hmac_streaming_go_proto"; message AesCtrHmacStreamingParams { uint32 ciphertext_segment_size = 1;
diff --git a/cc/proto/aes_eax.proto b/cc/proto/aes_eax.proto index c673306..c1bf500 100644 --- a/cc/proto/aes_eax.proto +++ b/cc/proto/aes_eax.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_eax_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_eax_go_proto"; // only allowing tag size in bytes = 16 message AesEaxParams {
diff --git a/cc/proto/aes_gcm.proto b/cc/proto/aes_gcm.proto index fba7a89..2551aa4 100644 --- a/cc/proto/aes_gcm.proto +++ b/cc/proto/aes_gcm.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_gcm_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_gcm_go_proto"; option objc_class_prefix = "TINKPB"; message AesGcmKeyFormat {
diff --git a/cc/proto/aes_gcm_hkdf_streaming.proto b/cc/proto/aes_gcm_hkdf_streaming.proto index 61fb479..5ec7ca4 100644 --- a/cc/proto/aes_gcm_hkdf_streaming.proto +++ b/cc/proto/aes_gcm_hkdf_streaming.proto
@@ -24,7 +24,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_gcm_hkdf_streaming_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_gcm_hkdf_streaming_go_proto"; message AesGcmHkdfStreamingParams { uint32 ciphertext_segment_size = 1;
diff --git a/cc/proto/aes_gcm_siv.proto b/cc/proto/aes_gcm_siv.proto index df9fada..220d79f 100644 --- a/cc/proto/aes_gcm_siv.proto +++ b/cc/proto/aes_gcm_siv.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_gcm_siv_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_gcm_siv_go_proto"; // The only allowed IV size is 12 bytes and tag size is 16 bytes. // Thus, accept no params.
diff --git a/cc/proto/aes_siv.proto b/cc/proto/aes_siv.proto index 0023027..ccb8d3c 100644 --- a/cc/proto/aes_siv.proto +++ b/cc/proto/aes_siv.proto
@@ -20,7 +20,15 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/aes_siv_go_proto"; +option go_package = "github.com/google/tink/go/proto/aes_siv_go_proto"; + +// Tink implements RFC 5297 (https://www.rfc-editor.org/rfc/rfc5297) for +// AES-SIV, putting the SIV/Tag at the beginning of the ciphertext. +// +// While the RFC 5297 supports a list of associated datas, Tink only supports +// exactly one associated data, which corresponds to a list with one element in +// RFC 5297. An empty associated data is a list with one empty element, and not +// an empty list. message AesSivKeyFormat { // Only valid value is: 64.
diff --git a/cc/proto/cached_dek_aead.proto b/cc/proto/cached_dek_aead.proto index 10bcde5..9b1a33f 100644 --- a/cc/proto/cached_dek_aead.proto +++ b/cc/proto/cached_dek_aead.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/cached_dek_aead_go_proto"; +option go_package = "github.com/google/tink/go/proto/cached_dek_aead_go_proto"; message CachedDekAeadKeyFormat { // Required.
diff --git a/cc/proto/cached_dek_envelope.proto b/cc/proto/cached_dek_envelope.proto index 1b096ad..8739b83 100644 --- a/cc/proto/cached_dek_envelope.proto +++ b/cc/proto/cached_dek_envelope.proto
@@ -22,7 +22,7 @@ option java_multiple_files = true; option java_package = "com.google.crypto.tink.proto"; -option go_package = "github.com/google/tink/proto/cached_dek_envelope_go_proto"; +option go_package = "github.com/google/tink/go/proto/cached_dek_envelope_go_proto"; message CachedDekEnvelopeAeadKeyFormat { // Required.
diff --git a/cc/proto/chacha20_poly1305.proto b/cc/proto/chacha20_poly1305.proto index 2cd6ead..ef8ab6e 100644 --- a/cc/proto/chacha20_poly1305.proto +++ b/cc/proto/chacha20_poly1305.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/chacha20_poly1305_go_proto"; +option go_package = "github.com/google/tink/go/proto/chacha20_poly1305_go_proto"; message ChaCha20Poly1305KeyFormat {}
diff --git a/cc/proto/common.proto b/cc/proto/common.proto index eaff8d3..4546064 100644 --- a/cc/proto/common.proto +++ b/cc/proto/common.proto
@@ -21,7 +21,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/common_go_proto"; +option go_package = "github.com/google/tink/go/proto/common_go_proto"; enum EllipticCurveType { UNKNOWN_CURVE = 0;
diff --git a/cc/proto/config.proto b/cc/proto/config.proto index ebbd742..cff6506 100644 --- a/cc/proto/config.proto +++ b/cc/proto/config.proto
@@ -21,7 +21,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/config_go_proto"; +option go_package = "github.com/google/tink/go/proto/config_go_proto"; // An entry that describes a key type to be used with Tink library, // specifying the corresponding primitive, key manager, and deprecation status.
diff --git a/cc/proto/ecdsa.proto b/cc/proto/ecdsa.proto index 6ba3970..2ce461f 100644 --- a/cc/proto/ecdsa.proto +++ b/cc/proto/ecdsa.proto
@@ -23,7 +23,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/ecdsa_go_proto"; +option go_package = "github.com/google/tink/go/proto/ecdsa_go_proto"; enum EcdsaSignatureEncoding { UNKNOWN_ENCODING = 0; @@ -80,4 +80,5 @@ message EcdsaKeyFormat { // Required. EcdsaParams params = 2; + uint32 version = 3; }
diff --git a/cc/proto/ecies_aead_hkdf.proto b/cc/proto/ecies_aead_hkdf.proto index 9470991..0c06ee3 100644 --- a/cc/proto/ecies_aead_hkdf.proto +++ b/cc/proto/ecies_aead_hkdf.proto
@@ -24,7 +24,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/ecies_aead_hkdf_go_proto"; +option go_package = "github.com/google/tink/go/proto/ecies_aead_hkdf_go_proto"; // Protos for keys for ECIES with HKDF and AEAD encryption. //
diff --git a/cc/proto/ed25519.proto b/cc/proto/ed25519.proto index 669f33a..613c59f 100644 --- a/cc/proto/ed25519.proto +++ b/cc/proto/ed25519.proto
@@ -23,10 +23,10 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/ed25519_go_proto"; +option go_package = "github.com/google/tink/go/proto/ed25519_go_proto"; message Ed25519KeyFormat { - uint32 version = 1; + uint32 version = 1; } // key_type: type.googleapis.com/google.crypto.tink.Ed25519PublicKey
diff --git a/cc/proto/empty.proto b/cc/proto/empty.proto index 33831a9..beeba07 100644 --- a/cc/proto/empty.proto +++ b/cc/proto/empty.proto
@@ -20,6 +20,6 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/empty_go_proto"; +option go_package = "github.com/google/tink/go/proto/empty_go_proto"; message Empty {}
diff --git a/cc/proto/hkdf_prf.proto b/cc/proto/hkdf_prf.proto index 3d3cbe9..38e69c5 100644 --- a/cc/proto/hkdf_prf.proto +++ b/cc/proto/hkdf_prf.proto
@@ -22,12 +22,16 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/hkdf_prf_proto"; +option go_package = "github.com/google/tink/go/proto/hkdf_prf_proto"; message HkdfPrfParams { HashType hash = 1; - // Salt, optional in RFC 5869. Using "" is equivalent to zeros of length up to - // the block length of the HMac. + // Optional. + // + // An unspecified or zero-length value is equivalent to a sequence of zeros + // (0x00) with a length equal to the output size of hash. + // + // See https://rfc-editor.org/rfc/rfc5869. bytes salt = 2; }
diff --git a/cc/proto/hmac.proto b/cc/proto/hmac.proto index 2733e51..bdd4e8a 100644 --- a/cc/proto/hmac.proto +++ b/cc/proto/hmac.proto
@@ -22,7 +22,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/hmac_go_proto"; +option go_package = "github.com/google/tink/go/proto/hmac_go_proto"; message HmacParams { HashType hash = 1; // HashType is an enum.
diff --git a/cc/proto/hmac_prf.proto b/cc/proto/hmac_prf.proto index 7b3c52d..87ef97d 100644 --- a/cc/proto/hmac_prf.proto +++ b/cc/proto/hmac_prf.proto
@@ -22,7 +22,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/hmac_prf_go_proto"; +option go_package = "github.com/google/tink/go/proto/hmac_prf_go_proto"; message HmacPrfParams { HashType hash = 1; // HashType is an enum.
diff --git a/cc/proto/hpke.proto b/cc/proto/hpke.proto index 847864a..f794e77 100644 --- a/cc/proto/hpke.proto +++ b/cc/proto/hpke.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/hpke_proto"; +option go_package = "github.com/google/tink/go/proto/hpke_proto"; enum HpkeKem { KEM_UNKNOWN = 0;
diff --git a/cc/proto/jwt_ecdsa.proto b/cc/proto/jwt_ecdsa.proto index 4c80fe1..ce78b04 100644 --- a/cc/proto/jwt_ecdsa.proto +++ b/cc/proto/jwt_ecdsa.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/jwt_ecdsa_go_proto"; +option go_package = "github.com/google/tink/go/proto/jwt_ecdsa_go_proto"; // See https://datatracker.ietf.org/doc/html/rfc7518#section-3.4 enum JwtEcdsaAlgorithm {
diff --git a/cc/proto/jwt_hmac.proto b/cc/proto/jwt_hmac.proto index e54a51d..a499638 100644 --- a/cc/proto/jwt_hmac.proto +++ b/cc/proto/jwt_hmac.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/jwt_hmac_go_proto"; +option go_package = "github.com/google/tink/go/proto/jwt_hmac_go_proto"; // See https://datatracker.ietf.org/doc/html/rfc7518#section-3.2 enum JwtHmacAlgorithm {
diff --git a/cc/proto/jwt_rsa_ssa_pkcs1.proto b/cc/proto/jwt_rsa_ssa_pkcs1.proto index adf31c8..54a9731 100644 --- a/cc/proto/jwt_rsa_ssa_pkcs1.proto +++ b/cc/proto/jwt_rsa_ssa_pkcs1.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/rsa_ssa_pkcs1_go_proto"; +option go_package = "github.com/google/tink/go/proto/rsa_ssa_pkcs1_go_proto"; // See https://datatracker.ietf.org/doc/html/rfc7518#section-3.3 enum JwtRsaSsaPkcs1Algorithm {
diff --git a/cc/proto/jwt_rsa_ssa_pss.proto b/cc/proto/jwt_rsa_ssa_pss.proto index 4312645..eb2d454 100644 --- a/cc/proto/jwt_rsa_ssa_pss.proto +++ b/cc/proto/jwt_rsa_ssa_pss.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/jwt_rsa_ssa_pss_go_proto"; +option go_package = "github.com/google/tink/go/proto/jwt_rsa_ssa_pss_go_proto"; // See https://datatracker.ietf.org/doc/html/rfc7518#section-3.5 enum JwtRsaSsaPssAlgorithm {
diff --git a/cc/proto/kms_aead.proto b/cc/proto/kms_aead.proto index e818788..16de8ee 100644 --- a/cc/proto/kms_aead.proto +++ b/cc/proto/kms_aead.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/kms_aead_go_proto"; +option go_package = "github.com/google/tink/go/proto/kms_aead_go_proto"; message KmsAeadKeyFormat { // Required.
diff --git a/cc/proto/kms_envelope.proto b/cc/proto/kms_envelope.proto index fa806e6..8b0fd83 100644 --- a/cc/proto/kms_envelope.proto +++ b/cc/proto/kms_envelope.proto
@@ -22,7 +22,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/kms_envelope_go_proto"; +option go_package = "github.com/google/tink/go/proto/kms_envelope_go_proto"; message KmsEnvelopeAeadKeyFormat { // Required.
diff --git a/cc/proto/prf_based_deriver.proto b/cc/proto/prf_based_deriver.proto index 06dd334..e58a2cd 100644 --- a/cc/proto/prf_based_deriver.proto +++ b/cc/proto/prf_based_deriver.proto
@@ -22,7 +22,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/prf_based_deriver_go_proto"; +option go_package = "github.com/google/tink/go/proto/prf_based_deriver_go_proto"; message PrfBasedDeriverParams { KeyTemplate derived_key_template = 1;
diff --git a/cc/proto/rsa_ssa_pkcs1.proto b/cc/proto/rsa_ssa_pkcs1.proto index 961189d..9797ee0 100644 --- a/cc/proto/rsa_ssa_pkcs1.proto +++ b/cc/proto/rsa_ssa_pkcs1.proto
@@ -24,7 +24,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/rsa_ssa_pkcs1_go_proto"; +option go_package = "github.com/google/tink/go/proto/rsa_ssa_pkcs1_go_proto"; message RsaSsaPkcs1Params { // Hash function used in computing hash of the signing message
diff --git a/cc/proto/rsa_ssa_pss.proto b/cc/proto/rsa_ssa_pss.proto index 8e7903f..1150057 100644 --- a/cc/proto/rsa_ssa_pss.proto +++ b/cc/proto/rsa_ssa_pss.proto
@@ -25,7 +25,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/rsa_ssa_pss_go_proto"; +option go_package = "github.com/google/tink/go/proto/rsa_ssa_pss_go_proto"; message RsaSsaPssParams { // Hash function used in computing hash of the signing message
diff --git a/cc/proto/tink.proto b/cc/proto/tink.proto index 1787581..8b3d100 100644 --- a/cc/proto/tink.proto +++ b/cc/proto/tink.proto
@@ -21,7 +21,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/tink_go_proto"; +option go_package = "github.com/google/tink/go/proto/tink_go_proto"; option objc_class_prefix = "TINKPB"; // Each instantiation of a Tink primitive is identified by type_url,
diff --git a/cc/proto/xchacha20_poly1305.proto b/cc/proto/xchacha20_poly1305.proto index cc52624..a2613f1 100644 --- a/cc/proto/xchacha20_poly1305.proto +++ b/cc/proto/xchacha20_poly1305.proto
@@ -20,7 +20,7 @@ option java_package = "com.google.crypto.tink.proto"; option java_multiple_files = true; -option go_package = "github.com/google/tink/proto/xchacha20_poly1305_go_proto"; +option go_package = "github.com/google/tink/go/proto/xchacha20_poly1305_go_proto"; message XChaCha20Poly1305KeyFormat { uint32 version = 1;
diff --git a/cc/proto_keyset_format.cc b/cc/proto_keyset_format.cc new file mode 100644 index 0000000..e666ed9 --- /dev/null +++ b/cc/proto_keyset_format.cc
@@ -0,0 +1,101 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/proto_keyset_format.h" + +#include <ios> +#include <iostream> +#include <memory> +#include <ostream> +#include <sstream> +#include <string> +#include <utility> + +#include "tink/binary_keyset_reader.h" +#include "tink/binary_keyset_writer.h" +#include "tink/cleartext_keyset_handle.h" +#include "tink/util/secret_data.h" + +namespace crypto { +namespace tink { + +crypto::tink::util::StatusOr<KeysetHandle> ParseKeysetFromProtoKeysetFormat( + absl::string_view serialized_keyset, SecretKeyAccessToken token) { + crypto::tink::util::StatusOr<std::unique_ptr<crypto::tink::KeysetReader>> + keyset_reader = BinaryKeysetReader::New(serialized_keyset); + if (!keyset_reader.ok()) { + return keyset_reader.status(); + } + crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> result = + CleartextKeysetHandle::Read(std::move(*keyset_reader)); + if (!result.ok()) { + return result.status(); + } + return std::move(**result); +} + +crypto::tink::util::StatusOr<util::SecretData> +SerializeKeysetToProtoKeysetFormat(const KeysetHandle& keyset_handle, + SecretKeyAccessToken token) { + std::stringbuf string_buf(std::ios_base::out); + crypto::tink::util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> + keyset_writer = BinaryKeysetWriter::New( + std::make_unique<std::ostream>(&string_buf)); + if (!keyset_writer.ok()) { + return keyset_writer.status(); + } + crypto::tink::util::Status status = + CleartextKeysetHandle::Write(keyset_writer->get(), keyset_handle); + if (!status.ok()) { + return status; + } + // TODO(tholenst): directly write into a secret data. + return util::SecretDataFromStringView(string_buf.str()); +} + +crypto::tink::util::StatusOr<KeysetHandle> +ParseKeysetWithoutSecretFromProtoKeysetFormat( + absl::string_view serialized_keyset) { + std::string keyset_copy = std::string(serialized_keyset); + crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> result = + KeysetHandle::ReadNoSecret(keyset_copy); + if (!result.ok()) { + return result.status(); + } + return std::move(**result); +} + +crypto::tink::util::StatusOr<std::string> +SerializeKeysetWithoutSecretToProtoKeysetFormat( + const KeysetHandle& keyset_handle) { + std::stringbuf string_buf(std::ios_base::out); + crypto::tink::util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> + keyset_writer = BinaryKeysetWriter::New( + std::make_unique<std::ostream>(&string_buf)); + if (!keyset_writer.ok()) { + return keyset_writer.status(); + } + crypto::tink::util::Status status = + keyset_handle.WriteNoSecret(keyset_writer->get()); + if (!status.ok()) { + return status; + } + return string_buf.str(); +} + +} // namespace tink +} // namespace crypto +
diff --git a/cc/proto_keyset_format.h b/cc/proto_keyset_format.h new file mode 100644 index 0000000..cab89eb --- /dev/null +++ b/cc/proto_keyset_format.h
@@ -0,0 +1,56 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_PROTO_KEYSET_FORMAT_H_ +#define TINK_PROTO_KEYSET_FORMAT_H_ + +#include <string> + +#include "absl/strings/string_view.h" +#include "tink/keyset_handle.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/secret_data.h" + +namespace crypto { +namespace tink { + +// Serializes a keyset into a binary string in "ProtoKeysetFormat". +// This function can serialize both keyset with or without secret key material. +crypto::tink::util::StatusOr<util::SecretData> +SerializeKeysetToProtoKeysetFormat(const KeysetHandle& keyset_handle, + SecretKeyAccessToken token); + +// Parses a keyset from a binary string in "ProtoKeysetFormat". +// This function can parse both keyset with or without secret key material. +crypto::tink::util::StatusOr<KeysetHandle> ParseKeysetFromProtoKeysetFormat( + absl::string_view serialized_keyset, SecretKeyAccessToken token); + +// Serializes a keyset into a binary string in "ProtoKeysetFormat". +// This function will fail if the keyset contains secret key material. +crypto::tink::util::StatusOr<std::string> +SerializeKeysetWithoutSecretToProtoKeysetFormat( + const KeysetHandle& keyset_handle); + +// Parses a keyset from a binary string in "ProtoKeysetFormat". +// This function will fail if the keyset contains secret key material. +crypto::tink::util::StatusOr<KeysetHandle> +ParseKeysetWithoutSecretFromProtoKeysetFormat( + absl::string_view serialized_keyset); + + +} // namespace tink +} // namespace crypto +#endif // TINK_PROTO_KEYSET_FORMAT_H_
diff --git a/cc/proto_keyset_format_test.cc b/cc/proto_keyset_format_test.cc new file mode 100644 index 0000000..5ac23e6 --- /dev/null +++ b/cc/proto_keyset_format_test.cc
@@ -0,0 +1,279 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/proto_keyset_format.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/escaping.h" +#include "tink/config/tink_config.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/legacy_proto_parameters.h" +#include "tink/internal/proto_parameters_serialization.h" +#include "tink/keyset_handle_builder.h" +#include "tink/mac.h" +#include "tink/mac/mac_key_templates.h" +#include "tink/signature/signature_key_templates.h" +#include "tink/util/secret_data.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { + +namespace { + +using ::crypto::tink::internal::LegacyProtoParameters; +using ::crypto::tink::internal::ProtoParametersSerialization; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::util::SecretData; +using ::crypto::tink::util::SecretDataAsStringView; +using ::testing::Eq; +using ::testing::Not; + +class SerializeKeysetToProtoKeysetFormatTest : public ::testing::Test { + protected: + void SetUp() override { + auto status = TinkConfig::Register(); + ASSERT_THAT(status, IsOk()); + } +}; + +util::StatusOr<LegacyProtoParameters> CmacParameters() { + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create(MacKeyTemplates::AesCmac()); + if (!serialization.ok()) return serialization.status(); + + return LegacyProtoParameters(*serialization); +} + +util::StatusOr<LegacyProtoParameters> EcdsaParameters() { + util::StatusOr<ProtoParametersSerialization> serialization = + ProtoParametersSerialization::Create(SignatureKeyTemplates::EcdsaP256()); + if (!serialization.ok()) return serialization.status(); + + return LegacyProtoParameters(*serialization); +} + +TEST_F(SerializeKeysetToProtoKeysetFormatTest, SerializeAndParseSingleKey) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CmacParameters(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/123)) + .Build(); + ASSERT_THAT(handle, IsOk()); + + crypto::tink::util::StatusOr<SecretData> serialization = + SerializeKeysetToProtoKeysetFormat(*handle, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<KeysetHandle> parsed_handle = ParseKeysetFromProtoKeysetFormat( + SecretDataAsStringView(*serialization), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_handle, IsOk()); + ASSERT_THAT(handle->size(), Eq(1)); + ASSERT_THAT(parsed_handle->size(), Eq(1)); + + EXPECT_TRUE(*(*handle)[0].GetKey() == *(*parsed_handle)[0].GetKey()); + EXPECT_TRUE((*handle)[0].GetId() == (*parsed_handle)[0].GetId()); + EXPECT_TRUE((*handle)[0].GetStatus() == (*parsed_handle)[0].GetStatus()); +} + +TEST_F(SerializeKeysetToProtoKeysetFormatTest, SerializeAndParseMultipleKeys) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CmacParameters(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/false, + /*id=*/123)) + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/125)) + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kDisabled, /*is_primary=*/true, + /*id=*/127)) + .Build(); + ASSERT_THAT(handle, IsOk()); + + crypto::tink::util::StatusOr<SecretData> serialization = + SerializeKeysetToProtoKeysetFormat(*handle, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<KeysetHandle> parsed_handle = ParseKeysetFromProtoKeysetFormat( + SecretDataAsStringView(*serialization), InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_handle, IsOk()); + ASSERT_THAT(handle->size(), Eq(3)); + ASSERT_THAT(parsed_handle->size(), Eq(3)); + + EXPECT_TRUE(*(*handle)[0].GetKey() == *(*parsed_handle)[0].GetKey()); + EXPECT_TRUE((*handle)[0].GetId() == (*parsed_handle)[0].GetId()); + EXPECT_TRUE((*handle)[0].GetStatus() == (*parsed_handle)[0].GetStatus()); + + EXPECT_TRUE(*(*handle)[1].GetKey() == *(*parsed_handle)[1].GetKey()); + EXPECT_TRUE((*handle)[1].GetId() == (*parsed_handle)[1].GetId()); + EXPECT_TRUE((*handle)[1].GetStatus() == (*parsed_handle)[1].GetStatus()); + + EXPECT_TRUE(*(*handle)[2].GetKey() == *(*parsed_handle)[2].GetKey()); + EXPECT_TRUE((*handle)[2].GetId() == (*parsed_handle)[2].GetId()); + EXPECT_TRUE((*handle)[2].GetStatus() == (*parsed_handle)[2].GetStatus()); +} + +TEST_F(SerializeKeysetToProtoKeysetFormatTest, SerializeNoAccessFails) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CmacParameters(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/123)) + .Build(); + ASSERT_THAT(handle, IsOk()); + + crypto::tink::util::StatusOr<std::string> serialization = + SerializeKeysetWithoutSecretToProtoKeysetFormat(*handle); + ASSERT_THAT(serialization, Not(IsOk())); +} + +TEST_F(SerializeKeysetToProtoKeysetFormatTest, ParseNoAccessFails) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + CmacParameters(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/123)) + .Build(); + ASSERT_THAT(handle, IsOk()); + + crypto::tink::util::StatusOr<SecretData> serialization = + SerializeKeysetToProtoKeysetFormat(*handle, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<KeysetHandle> parsed_handle = + ParseKeysetWithoutSecretFromProtoKeysetFormat( + SecretDataAsStringView(*serialization)); + ASSERT_THAT(parsed_handle, Not(IsOk())); +} + +TEST_F(SerializeKeysetToProtoKeysetFormatTest, TestVector) { + std::string serialized_keyset = absl::HexStringToBytes( + "0895e59bcc0612680a5c0a2e747970652e676f6f676c65617069732e636f6d2f676f6f67" + "6c652e63727970746f2e74696e6b2e486d61634b657912281a20cca20f02278003b3513f" + "5d01759ac1302f7d883f2f4a40025532ee1b11f9e587120410100803180110011895e59b" + "cc062001"); + crypto::tink::util::StatusOr<KeysetHandle> keyset_handle = + ParseKeysetFromProtoKeysetFormat(serialized_keyset, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(keyset_handle.status(), IsOk()); + crypto::tink::util::StatusOr<std::unique_ptr<Mac>> mac = + (*keyset_handle).GetPrimitive<Mac>(); + ASSERT_THAT(mac.status(), IsOk()); + ASSERT_THAT( + (*mac)->VerifyMac( + absl::HexStringToBytes("016986f2956092d259136923c6f4323557714ec499"), + "data"), + IsOk()); +} + +TEST_F(SerializeKeysetToProtoKeysetFormatTest, SerializeAndParsePublicKey) { + util::StatusOr<internal::LegacyProtoParameters> parameters = + EcdsaParameters(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<KeysetHandle> handle = + KeysetHandleBuilder() + .AddEntry(KeysetHandleBuilder::Entry::CreateFromCopyableParams( + *parameters, KeyStatus::kEnabled, /*is_primary=*/true, + /*id=*/123)) + .Build(); + ASSERT_THAT(handle, IsOk()); + util::StatusOr<std::unique_ptr<KeysetHandle>> public_handle = + handle->GetPublicKeysetHandle(); + ASSERT_THAT(public_handle, IsOk()); + + + crypto::tink::util::StatusOr<SecretData> serialization1 = + SerializeKeysetToProtoKeysetFormat(**public_handle, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization1, IsOk()); + crypto::tink::util::StatusOr<std::string> serialization2 = + SerializeKeysetWithoutSecretToProtoKeysetFormat(**public_handle); + ASSERT_THAT(serialization2, IsOk()); + + util::StatusOr<KeysetHandle> parsed_handle1 = + ParseKeysetFromProtoKeysetFormat(SecretDataAsStringView(*serialization1), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_handle1, IsOk()); + util::StatusOr<KeysetHandle> parsed_handle2 = + ParseKeysetWithoutSecretFromProtoKeysetFormat( + SecretDataAsStringView(*serialization1)); + ASSERT_THAT(parsed_handle2, IsOk()); + util::StatusOr<KeysetHandle> parsed_handle3 = + ParseKeysetFromProtoKeysetFormat(*serialization2, + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(parsed_handle3, IsOk()); + util::StatusOr<KeysetHandle> parsed_handle4 = + ParseKeysetWithoutSecretFromProtoKeysetFormat(*serialization2); + ASSERT_THAT(parsed_handle4, IsOk()); + + ASSERT_THAT((*public_handle)->size(), Eq(1)); + ASSERT_THAT(parsed_handle1->size(), Eq(1)); + ASSERT_THAT(parsed_handle2->size(), Eq(1)); + ASSERT_THAT(parsed_handle3->size(), Eq(1)); + ASSERT_THAT(parsed_handle4->size(), Eq(1)); + + // TODO(b/277791403): Replace with KeysetHandle::Entry equality checks. + EXPECT_TRUE(*(**public_handle)[0].GetKey() == *(*parsed_handle1)[0].GetKey()); + EXPECT_TRUE(*(**public_handle)[0].GetKey() == *(*parsed_handle2)[0].GetKey()); + EXPECT_TRUE(*(**public_handle)[0].GetKey() == *(*parsed_handle3)[0].GetKey()); + EXPECT_TRUE(*(**public_handle)[0].GetKey() == *(*parsed_handle4)[0].GetKey()); + + EXPECT_TRUE((**public_handle)[0].GetId() == (*parsed_handle1)[0].GetId()); + EXPECT_TRUE((**public_handle)[0].GetId() == (*parsed_handle2)[0].GetId()); + EXPECT_TRUE((**public_handle)[0].GetId() == (*parsed_handle3)[0].GetId()); + EXPECT_TRUE((**public_handle)[0].GetId() == (*parsed_handle4)[0].GetId()); + + EXPECT_TRUE((**public_handle)[0].GetStatus() == + (*parsed_handle1)[0].GetStatus()); + EXPECT_TRUE((**public_handle)[0].GetStatus() == + (*parsed_handle2)[0].GetStatus()); + EXPECT_TRUE((**public_handle)[0].GetStatus() == + (*parsed_handle3)[0].GetStatus()); + EXPECT_TRUE((**public_handle)[0].GetStatus() == + (*parsed_handle4)[0].GetStatus()); +} + + +} // namespace + +} // namespace tink +} // namespace crypto
diff --git a/cc/public_key_sign.h b/cc/public_key_sign.h index 4fdb4b1..7c4b110 100644 --- a/cc/public_key_sign.h +++ b/cc/public_key_sign.h
@@ -39,7 +39,7 @@ virtual crypto::tink::util::StatusOr<std::string> Sign( absl::string_view data) const = 0; - virtual ~PublicKeySign() {} + virtual ~PublicKeySign() = default; }; } // namespace tink
diff --git a/cc/public_key_verify.h b/cc/public_key_verify.h index 7883caf..0119d1d 100644 --- a/cc/public_key_verify.h +++ b/cc/public_key_verify.h
@@ -38,7 +38,7 @@ absl::string_view signature, absl::string_view data) const = 0; - virtual ~PublicKeyVerify() {} + virtual ~PublicKeyVerify() = default; }; } // namespace tink
diff --git a/cc/random_access_stream.h b/cc/random_access_stream.h index b40445a..c5a4dec 100644 --- a/cc/random_access_stream.h +++ b/cc/random_access_stream.h
@@ -28,8 +28,8 @@ // like regular files. class RandomAccessStream { public: - RandomAccessStream() {} - virtual ~RandomAccessStream() {} + RandomAccessStream() = default; + virtual ~RandomAccessStream() = default; // Reads up to 'count' bytes starting at 'position' and writes them // to 'dest_buffer'. 'position' must be within the size of the stream,
diff --git a/cc/registry.h b/cc/registry.h index 29e38d2..5fb7933 100644 --- a/cc/registry.h +++ b/cc/registry.h
@@ -50,43 +50,6 @@ // and KeyManagers. class Registry { public: - // Returns a catalogue with the given name (if any found). - // Keeps the ownership of the catalogue. - template <class P> - ABSL_DEPRECATED("Catalogues are not supported anymore.") - static crypto::tink::util::StatusOr<const Catalogue<P>*> get_catalogue( - absl::string_view catalogue_name) { - return internal::RegistryImpl::GlobalInstance().get_catalogue<P>( - catalogue_name); - } - - // Adds the given 'catalogue' under the specified 'catalogue_name', - // to enable custom configuration of key types and key managers. - // - // Adding a custom catalogue should be a one-time operation, - // and fails if the given 'catalogue' tries to override - // an existing, different catalogue for the specified name. - template <class ConcreteCatalogue> - ABSL_DEPRECATED("Catalogues are not supported anymore.") - static crypto::tink::util::Status - AddCatalogue(absl::string_view catalogue_name, - std::unique_ptr<ConcreteCatalogue> catalogue) { - return internal::RegistryImpl::GlobalInstance().AddCatalogue( - catalogue_name, catalogue.release()); - } - - // AddCatalogue has the same functionality as the overload which uses a - // unique_ptr and which should be preferred. - // - // Takes ownership of 'catalogue', which must be non-nullptr (in case of - // failure, 'catalogue' is deleted). - template <class P> - ABSL_DEPRECATED("Use AddCatalogue with a unique_ptr input instead.") - static crypto::tink::util::Status - AddCatalogue(absl::string_view catalogue_name, Catalogue<P>* catalogue) { - return AddCatalogue(catalogue_name, absl::WrapUnique(catalogue)); - } - // Registers the given 'manager' for the key type 'manager->get_key_type()'. template <class ConcreteKeyManager> static crypto::tink::util::Status RegisterKeyManager( @@ -95,23 +58,6 @@ manager.release(), new_key_allowed); } - // Same functionality as the overload which takes a unique pointer, for - // new_key_allowed = true. - template <class P> - ABSL_DEPRECATED( - "Use RegisterKeyManager with a unique_ptr manager and new_key_allowed = " - "true instead.") - static crypto::tink::util::Status RegisterKeyManager(KeyManager<P>* manager) { - return RegisterKeyManager(absl::WrapUnique(manager), true); - } - - template <class P> - ABSL_DEPRECATED("Use RegisterKeyManager with a unique_ptr manager instead.") - static crypto::tink::util::Status RegisterKeyManager(KeyManager<P>* manager, - bool new_key_allowed) { - return RegisterKeyManager(absl::WrapUnique(manager), new_key_allowed); - } - template <class KTManager> static crypto::tink::util::Status RegisterKeyTypeManager( std::unique_ptr<KTManager> manager, bool new_key_allowed) { @@ -161,15 +107,6 @@ const google::crypto::tink::KeyData& key_data) { return internal::RegistryImpl::GlobalInstance().GetPrimitive<P>(key_data); } - // Convenience method for creating a new primitive for the key given - // in 'key'. It looks up a KeyManager identified by type_url, - // and calls manager's GetPrimitive(key)-method. - template <class P> - static crypto::tink::util::StatusOr<std::unique_ptr<P>> GetPrimitive( - absl::string_view type_url, const portable_proto::MessageLite& key) { - return internal::RegistryImpl::GlobalInstance().GetPrimitive<P>(type_url, - key); - } // Generates a new KeyData for the specified 'key_template'. // It looks up a KeyManager identified by key_template.type_url,
diff --git a/cc/restricted_data.h b/cc/restricted_data.h new file mode 100644 index 0000000..02902b2 --- /dev/null +++ b/cc/restricted_data.h
@@ -0,0 +1,74 @@ +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_RESTRICTED_DATA_H_ +#define TINK_RESTRICTED_DATA_H_ + +#include "tink/secret_key_access_token.h" +#include "tink/util/secret_data.h" + +namespace crypto { +namespace tink { + +// Stores secret (sensitive) data that is safely destroyed in the event of +// core dumps (similar to `util::SecretData`) and access restricted via +// `SecurityKeyAccessToken`. This class is particularly useful for +// encapsulating cryptographic key material. +// +// Example: +// RestrictedData restricted_data(/*num_random_bytes=*/32); +// absl::string_view raw_secret = +// restricted_data.GetSecret(InsecureSecretKeyAccess::Get()); +class RestrictedData { + public: + // Copyable and movable. + RestrictedData(const RestrictedData& other) = default; + RestrictedData& operator=(const RestrictedData& other) = default; + RestrictedData(RestrictedData&& other) = default; + RestrictedData& operator=(RestrictedData&& other) = default; + + // Creates a new RestrictedData object that wraps `secret`. Note that creating + // a `token` requires access to `InsecureSecretKeyAccess::Get()`. + explicit RestrictedData(absl::string_view secret, SecretKeyAccessToken token) + : secret_(util::SecretDataFromStringView(secret)) {} + + // Creates a new RestrictedData object that wraps a secret containing + // `num_random_bytes`. The program will terminate if `num_random_bytes` is a + // negative value. + explicit RestrictedData(int64_t num_random_bytes); + + // Returns the secret for this RestrictedData object. Note that creating a + // `token` requires access to `InsecureSecretKeyAccess::Get()`. + absl::string_view GetSecret(SecretKeyAccessToken token) const { + return util::SecretDataAsStringView(secret_); + } + + int64_t size() const { return secret_.size(); } + + // Constant-time comparison operators. + bool operator==(const RestrictedData& other) const; + bool operator!=(const RestrictedData& other) const { + return !(*this == other); + } + + private: + util::SecretData secret_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_RESTRICTED_DATA_H_
diff --git a/cc/secret_key_access.h b/cc/secret_key_access.h index d51e144..52655ad 100644 --- a/cc/secret_key_access.h +++ b/cc/secret_key_access.h
@@ -14,8 +14,8 @@ // //////////////////////////////////////////////////////////////////////////////// -#ifndef THIRD_PARTY_TINK_SECRET_KEY_ACCESS_H_ -#define THIRD_PARTY_TINK_SECRET_KEY_ACCESS_H_ +#ifndef TINK_SECRET_KEY_ACCESS_H_ +#define TINK_SECRET_KEY_ACCESS_H_ #include "tink/key_access.h" @@ -30,4 +30,4 @@ } // namespace tink } // namespace crypto -#endif // THIRD_PARTY_TINK_SECRET_KEY_ACCESS_H_ +#endif // TINK_SECRET_KEY_ACCESS_H_
diff --git a/cc/signature/BUILD.bazel b/cc/signature/BUILD.bazel index 2e5af69..b882901 100644 --- a/cc/signature/BUILD.bazel +++ b/cc/signature/BUILD.bazel
@@ -418,7 +418,6 @@ ":ecdsa_verify_key_manager", ":public_key_verify_factory", ":signature_config", - "//:config", "//:crypto_format", "//:keyset_handle", "//:public_key_verify", @@ -462,7 +461,6 @@ ":ecdsa_sign_key_manager", ":public_key_sign_factory", ":signature_config", - "//:config", "//:crypto_format", "//:keyset_handle", "//:public_key_sign", @@ -680,12 +678,11 @@ ":rsa_ssa_pss_verify_key_manager", ":signature_config", ":signature_key_templates", - "//:config", "//:keyset_handle", "//:public_key_sign", "//:public_key_verify", "//:registry", - "//config:tink_fips", + "//internal:fips_utils", "//util:status", "//util:test_matchers", "//util:test_util",
diff --git a/cc/signature/CMakeLists.txt b/cc/signature/CMakeLists.txt index 7cbee77..e8db1e7 100644 --- a/cc/signature/CMakeLists.txt +++ b/cc/signature/CMakeLists.txt
@@ -1,5 +1,7 @@ tink_module(signature) +add_subdirectory(internal) + tink_cc_library( NAME public_key_verify_wrapper SRCS @@ -320,7 +322,6 @@ signature_config.cc signature_config.h DEPS - tink::signature::ecdsa_sign_key_manager tink::signature::ecdsa_verify_key_manager tink::signature::ed25519_sign_key_manager tink::signature::ed25519_verify_key_manager @@ -336,6 +337,7 @@ tink::config::config_util tink::config::tink_fips tink::util::status + tink::signature::ecdsa_sign_key_manager tink::proto::config_cc_proto ) @@ -398,7 +400,6 @@ tink::signature::public_key_verify_factory tink::signature::signature_config gmock - tink::core::config tink::core::crypto_format tink::core::keyset_handle tink::core::public_key_verify @@ -440,7 +441,6 @@ tink::signature::public_key_sign_factory tink::signature::signature_config gmock - tink::core::config tink::core::crypto_format tink::core::keyset_handle tink::core::public_key_sign @@ -651,12 +651,11 @@ absl::memory absl::status crypto - tink::core::config tink::core::keyset_handle tink::core::public_key_sign tink::core::public_key_verify tink::core::registry - tink::config::tink_fips + tink::internal::fips_utils tink::util::status tink::util::test_matchers tink::util::test_util
diff --git a/cc/signature/ecdsa_sign_key_manager.cc b/cc/signature/ecdsa_sign_key_manager.cc index bea5312..0165feb 100644 --- a/cc/signature/ecdsa_sign_key_manager.cc +++ b/cc/signature/ecdsa_sign_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/signature/ecdsa_sign_key_manager.h" +#include <memory> #include <string> #include <utility> @@ -76,6 +77,12 @@ "Deriving EC keys is not allowed in FIPS mode."); } + util::Status status = + ValidateVersion(ecdsa_key_format.version(), get_version()); + if (!status.ok()) { + return status; + } + // Extract enough random bytes from the input_stream to match the security // level of the EC. Note that the input_stream here must come from a PRF // and will not use more bytes than required by the security level of the EC.
diff --git a/cc/signature/ecdsa_sign_key_manager.h b/cc/signature/ecdsa_sign_key_manager.h index 8f5e195..e618e79 100644 --- a/cc/signature/ecdsa_sign_key_manager.h +++ b/cc/signature/ecdsa_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_SIGNATURE_ECDSA_SIGN_KEY_MANAGER_H_ #define TINK_SIGNATURE_ECDSA_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/signature/ecdsa_sign_key_manager_test.cc b/cc/signature/ecdsa_sign_key_manager_test.cc index 2fe8a97..859925d 100644 --- a/cc/signature/ecdsa_sign_key_manager_test.cc +++ b/cc/signature/ecdsa_sign_key_manager_test.cc
@@ -316,6 +316,25 @@ test::StatusIs(absl::StatusCode::kInvalidArgument)); } +TEST(EcdsaSignKeyManagerTest, DeriveKeyWithInvalidKeyTemplateVersionFails) { + if (!internal::IsBoringSsl()) { + GTEST_SKIP() + << "Key derivation from an input stream is not supported with OpenSSL"; + } + EcdsaKeyFormat format; + format.set_version(1); + EcdsaParams* params = format.mutable_params(); + params->set_hash_type(HashType::SHA256); + params->set_curve(EllipticCurveType::NIST_P256); + params->set_encoding(EcdsaSignatureEncoding::DER); + + util::IstreamInputStream input_stream{ + absl::make_unique<std::stringstream>("tooshort")}; + + ASSERT_THAT(EcdsaSignKeyManager().DeriveKey(format, &input_stream).status(), + test::StatusIs(absl::StatusCode::kInvalidArgument)); +} + TEST(EcdsaSignKeyManagerTest, DeriveKeyInvalidCurve) { if (!internal::IsBoringSsl()) { GTEST_SKIP()
diff --git a/cc/signature/ecdsa_verify_key_manager.cc b/cc/signature/ecdsa_verify_key_manager.cc index f72c05c..7660fb1 100644 --- a/cc/signature/ecdsa_verify_key_manager.cc +++ b/cc/signature/ecdsa_verify_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/signature/ecdsa_verify_key_manager.h" +#include <memory> #include <utility> #include "absl/status/status.h"
diff --git a/cc/signature/ecdsa_verify_key_manager.h b/cc/signature/ecdsa_verify_key_manager.h index 5445c20..075a691 100644 --- a/cc/signature/ecdsa_verify_key_manager.h +++ b/cc/signature/ecdsa_verify_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_SIGNATURE_ECDSA_VERIFY_KEY_MANAGER_H_ #define TINK_SIGNATURE_ECDSA_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/signature/ed25519_sign_key_manager.cc b/cc/signature/ed25519_sign_key_manager.cc index 661e0e7..962b138 100644 --- a/cc/signature/ed25519_sign_key_manager.cc +++ b/cc/signature/ed25519_sign_key_manager.cc
@@ -16,6 +16,8 @@ #include "tink/signature/ed25519_sign_key_manager.h" +#include <memory> + #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h"
diff --git a/cc/signature/ed25519_sign_key_manager.h b/cc/signature/ed25519_sign_key_manager.h index a02b2ad..12ff8bf 100644 --- a/cc/signature/ed25519_sign_key_manager.h +++ b/cc/signature/ed25519_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_SIGNATURE_ED25519_SIGN_KEY_MANAGER_H_ #define TINK_SIGNATURE_ED25519_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/signature/ed25519_sign_key_manager_test.cc b/cc/signature/ed25519_sign_key_manager_test.cc index 65250de..060042d 100644 --- a/cc/signature/ed25519_sign_key_manager_test.cc +++ b/cc/signature/ed25519_sign_key_manager_test.cc
@@ -16,6 +16,7 @@ #include "tink/signature/ed25519_sign_key_manager.h" +#include <sstream> #include <string> #include "gmock/gmock.h"
diff --git a/cc/signature/ed25519_verify_key_manager.cc b/cc/signature/ed25519_verify_key_manager.cc index a4ba197..92f52ab 100644 --- a/cc/signature/ed25519_verify_key_manager.cc +++ b/cc/signature/ed25519_verify_key_manager.cc
@@ -16,6 +16,8 @@ #include "tink/signature/ed25519_verify_key_manager.h" +#include <memory> + #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "tink/public_key_verify.h"
diff --git a/cc/signature/ed25519_verify_key_manager.h b/cc/signature/ed25519_verify_key_manager.h index 2d0640f..25b1b2d 100644 --- a/cc/signature/ed25519_verify_key_manager.h +++ b/cc/signature/ed25519_verify_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_SIGNATURE_ED25519_VERIFY_KEY_MANAGER_H_ #define TINK_SIGNATURE_ED25519_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/signature/internal/BUILD.bazel b/cc/signature/internal/BUILD.bazel index 5b01f6e..3d679d8 100644 --- a/cc/signature/internal/BUILD.bazel +++ b/cc/signature/internal/BUILD.bazel
@@ -1 +1,49 @@ +package(default_visibility = ["//:__subpackages__"]) + licenses(["notice"]) + +cc_library( + name = "ecdsa_raw_sign_boringssl", + srcs = ["ecdsa_raw_sign_boringssl.cc"], + hdrs = ["ecdsa_raw_sign_boringssl.h"], + include_prefix = "tink/signature/internal", + deps = [ + "//:public_key_sign", + "//internal:bn_util", + "//internal:ec_util", + "//internal:err_util", + "//internal:fips_utils", + "//internal:md_util", + "//internal:ssl_unique_ptr", + "//internal:util", + "//subtle:common_enums", + "//subtle:subtle_util_boringssl", + "//util:errors", + "//util:statusor", + "@boringssl//:crypto", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "ecdsa_raw_sign_boringssl_test", + size = "small", + srcs = ["ecdsa_raw_sign_boringssl_test.cc"], + tags = ["fips"], + deps = [ + ":ecdsa_raw_sign_boringssl", + "//:public_key_sign", + "//:public_key_verify", + "//internal:ec_util", + "//internal:fips_utils", + "//subtle:common_enums", + "//subtle:ecdsa_verify_boringssl", + "//subtle:subtle_util_boringssl", + "//util:status", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/cc/signature/internal/CMakeLists.txt b/cc/signature/internal/CMakeLists.txt index e69de29..0b898c1 100644 --- a/cc/signature/internal/CMakeLists.txt +++ b/cc/signature/internal/CMakeLists.txt
@@ -0,0 +1,44 @@ +tink_module(signature::internal) + +tink_cc_library( + NAME ecdsa_raw_sign_boringssl + SRCS + ecdsa_raw_sign_boringssl.cc + ecdsa_raw_sign_boringssl.h + DEPS + absl::status + absl::strings + crypto + tink::core::public_key_sign + tink::internal::bn_util + tink::internal::ec_util + tink::internal::err_util + tink::internal::fips_utils + tink::internal::md_util + tink::internal::ssl_unique_ptr + tink::internal::util + tink::subtle::common_enums + tink::subtle::subtle_util_boringssl + tink::util::errors + tink::util::statusor +) + +tink_cc_test( + NAME ecdsa_raw_sign_boringssl_test + SRCS + ecdsa_raw_sign_boringssl_test.cc + DEPS + tink::signature::internal::ecdsa_raw_sign_boringssl + gmock + absl::status + tink::core::public_key_sign + tink::core::public_key_verify + tink::internal::ec_util + tink::internal::fips_utils + tink::subtle::common_enums + tink::subtle::ecdsa_verify_boringssl + tink::subtle::subtle_util_boringssl + tink::util::status + tink::util::statusor + tink::util::test_matchers +)
diff --git a/cc/signature/internal/ecdsa_raw_sign_boringssl.cc b/cc/signature/internal/ecdsa_raw_sign_boringssl.cc new file mode 100644 index 0000000..366c966 --- /dev/null +++ b/cc/signature/internal/ecdsa_raw_sign_boringssl.cc
@@ -0,0 +1,159 @@ +// Copyright 2017 Google Inc. +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tink/signature/internal/ecdsa_raw_sign_boringssl.h" + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "openssl/bn.h" +#include "openssl/ec.h" +#include "openssl/ecdsa.h" +#include "openssl/evp.h" +#include "tink/internal/bn_util.h" +#include "tink/internal/ec_util.h" +#include "tink/internal/err_util.h" +#include "tink/internal/md_util.h" +#include "tink/internal/ssl_unique_ptr.h" +#include "tink/internal/util.h" +#include "tink/subtle/common_enums.h" +#include "tink/subtle/subtle_util_boringssl.h" +#include "tink/util/errors.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +// Transforms ECDSA DER signature encoding to IEEE_P1363 encoding. +// +// The IEEE_P1363 signature's format is r || s, where r and s are zero-padded +// and have the same size in bytes as the order of the curve. For example, for +// NIST P-256 curve, r and s are zero-padded to 32 bytes. +// +// The DER signature is encoded using ASN.1 +// (https://tools.ietf.org/html/rfc5480#appendix-A): ECDSA-Sig-Value :: = +// SEQUENCE { r INTEGER, s INTEGER }. In particular, the encoding is: 0x30 || +// totalLength || 0x02 || r's length || r || 0x02 || s's length || s. +crypto::tink::util::StatusOr<std::string> DerToIeee(absl::string_view der, + const EC_KEY* key) { + size_t field_size_in_bytes = + (EC_GROUP_get_degree(EC_KEY_get0_group(key)) + 7) / 8; + + const uint8_t* der_ptr = reinterpret_cast<const uint8_t*>(der.data()); + // Note: d2i_ECDSA_SIG is deprecated in BoringSSL, but it isn't in OpenSSL. + internal::SslUniquePtr<ECDSA_SIG> ecdsa( + d2i_ECDSA_SIG(nullptr, &der_ptr, der.size())); + if (ecdsa == nullptr || + der_ptr != reinterpret_cast<const uint8_t*>(der.data() + der.size())) { + return util::Status(absl::StatusCode::kInternal, "d2i_ECDSA_SIG failed"); + } + + const BIGNUM* r_bn; + const BIGNUM* s_bn; + ECDSA_SIG_get0(ecdsa.get(), &r_bn, &s_bn); + util::StatusOr<std::string> r = + internal::BignumToString(r_bn, field_size_in_bytes); + if (!r.ok()) { + return r.status(); + } + util::StatusOr<std::string> s = + internal::BignumToString(s_bn, field_size_in_bytes); + if (!s.ok()) { + return s.status(); + } + return absl::StrCat(*r, *s); +} + +} // namespace + +// static +util::StatusOr<std::unique_ptr<EcdsaRawSignBoringSsl>> +EcdsaRawSignBoringSsl::New(const subtle::SubtleUtilBoringSSL::EcKey& ec_key, + subtle::EcdsaSignatureEncoding encoding) { + auto status = internal::CheckFipsCompatibility<EcdsaRawSignBoringSsl>(); + if (!status.ok()) return status; + + // Check curve. + util::StatusOr<internal::SslUniquePtr<EC_GROUP>> group = + internal::EcGroupFromCurveType(ec_key.curve); + if (!group.ok()) { + return group.status(); + } + internal::SslUniquePtr<EC_KEY> key(EC_KEY_new()); + EC_KEY_set_group(key.get(), group->get()); + + // Check key. + util::StatusOr<internal::SslUniquePtr<EC_POINT>> pub_key = + internal::GetEcPoint(ec_key.curve, ec_key.pub_x, ec_key.pub_y); + if (!pub_key.ok()) { + return pub_key.status(); + } + + if (!EC_KEY_set_public_key(key.get(), pub_key->get())) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Invalid public key: ", internal::GetSslErrors())); + } + + internal::SslUniquePtr<BIGNUM> priv_key( + BN_bin2bn(ec_key.priv.data(), ec_key.priv.size(), nullptr)); + if (!EC_KEY_set_private_key(key.get(), priv_key.get())) { + return util::Status( + absl::StatusCode::kInvalidArgument, + absl::StrCat("Invalid private key: ", internal::GetSslErrors())); + } + + return { + absl::WrapUnique(new EcdsaRawSignBoringSsl(std::move(key), encoding))}; +} + +util::StatusOr<std::string> EcdsaRawSignBoringSsl::Sign( + absl::string_view data) const { + // BoringSSL expects a non-null pointer for data, + // regardless of whether the size is 0. + data = internal::EnsureStringNonNull(data); + + // Compute the raw signature. + std::vector<uint8_t> buffer(ECDSA_size(key_.get())); + unsigned int sig_length; + if (1 != ECDSA_sign(0 /* unused */, + reinterpret_cast<const uint8_t*>(data.data()), + data.size(), buffer.data(), &sig_length, key_.get())) { + return util::Status(absl::StatusCode::kInternal, "Signing failed."); + } + + if (encoding_ == subtle::EcdsaSignatureEncoding::IEEE_P1363) { + auto status_or_sig = DerToIeee( + absl::string_view(reinterpret_cast<char*>(buffer.data()), sig_length), + key_.get()); + if (!status_or_sig.ok()) { + return status_or_sig.status(); + } + return status_or_sig.value(); + } + + return std::string(reinterpret_cast<char*>(buffer.data()), sig_length); +} + +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/signature/internal/ecdsa_raw_sign_boringssl.h b/cc/signature/internal/ecdsa_raw_sign_boringssl.h new file mode 100644 index 0000000..7c917c4 --- /dev/null +++ b/cc/signature/internal/ecdsa_raw_sign_boringssl.h
@@ -0,0 +1,65 @@ +// Copyright 2017 Google Inc. +// +// 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_SIGNATURE_INTERNAL_ECDSA_RAW_SIGN_BORINGSSL_H_ +#define TINK_SIGNATURE_INTERNAL_ECDSA_RAW_SIGN_BORINGSSL_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "absl/strings/string_view.h" +#include "openssl/ec.h" +#include "openssl/evp.h" +#include "tink/internal/fips_utils.h" +#include "tink/internal/ssl_unique_ptr.h" +#include "tink/public_key_sign.h" +#include "tink/subtle/common_enums.h" +#include "tink/subtle/subtle_util_boringssl.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { +namespace internal { + +// ECDSA raw signing using Boring SSL, generating signatures in DER-encoding. +class EcdsaRawSignBoringSsl : public PublicKeySign { + public: + static crypto::tink::util::StatusOr<std::unique_ptr<EcdsaRawSignBoringSsl>> + New(const crypto::tink::internal::EcKey& ec_key, + subtle::EcdsaSignatureEncoding encoding); + + // Computes the signature for 'data'. + crypto::tink::util::StatusOr<std::string> Sign( + absl::string_view data) const override; + + static constexpr crypto::tink::internal::FipsCompatibility kFipsStatus = + crypto::tink::internal::FipsCompatibility::kRequiresBoringCrypto; + + private: + EcdsaRawSignBoringSsl(internal::SslUniquePtr<EC_KEY> key, + subtle::EcdsaSignatureEncoding encoding) + : key_(std::move(key)), encoding_(encoding) {} + + internal::SslUniquePtr<EC_KEY> key_; + subtle::EcdsaSignatureEncoding encoding_; +}; + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_SIGNATURE_INTERNAL_ECDSA_RAW_SIGN_BORINGSSL_H_
diff --git a/cc/signature/internal/ecdsa_raw_sign_boringssl_test.cc b/cc/signature/internal/ecdsa_raw_sign_boringssl_test.cc new file mode 100644 index 0000000..aaf3221 --- /dev/null +++ b/cc/signature/internal/ecdsa_raw_sign_boringssl_test.cc
@@ -0,0 +1,296 @@ +// Copyright 2017 Google Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/signature/internal/ecdsa_raw_sign_boringssl.h" + +#include <memory> +#include <string> +#include <utility> + +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "tink/internal/ec_util.h" +#include "tink/internal/fips_utils.h" +#include "tink/public_key_sign.h" +#include "tink/public_key_verify.h" +#include "tink/subtle/common_enums.h" +#include "tink/subtle/ecdsa_verify_boringssl.h" +#include "tink/subtle/subtle_util_boringssl.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::testing::Eq; +using ::testing::Not; +using ::testing::SizeIs; + +util::StatusOr<std::string> ComputeDigest(subtle::HashType hash_type, + absl::string_view data) { + util::StatusOr<const EVP_MD*> hash = internal::EvpHashFromHashType(hash_type); + if (!hash.ok()) return hash.status(); + + unsigned int digest_size; + uint8_t digest[EVP_MAX_MD_SIZE]; + if (1 != EVP_Digest(data.data(), data.size(), digest, &digest_size, *hash, + nullptr)) { + return util::Status(absl::StatusCode::kInternal, + "Could not compute digest."); + } + + return std::string(reinterpret_cast<const char*>(digest), digest_size); +} + +TEST(EcdsaRawSignBoringSslTest, VerifySignature) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() + << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; + } + subtle::EcdsaSignatureEncoding encodings[2] = { + subtle::EcdsaSignatureEncoding::DER, + subtle::EcdsaSignatureEncoding::IEEE_P1363}; + for (subtle::EcdsaSignatureEncoding encoding : encodings) { + util::StatusOr<EcKey> ec_key = subtle::SubtleUtilBoringSSL::GetNewEcKey( + subtle::EllipticCurveType::NIST_P256); + ASSERT_THAT(ec_key, IsOk()); + + util::StatusOr<std::unique_ptr<EcdsaRawSignBoringSsl>> signer = + EcdsaRawSignBoringSsl::New(*ec_key, encoding); + ASSERT_THAT(signer, IsOk()); + + util::StatusOr<std::unique_ptr<subtle::EcdsaVerifyBoringSsl>> verifier = + subtle::EcdsaVerifyBoringSsl::New(*ec_key, subtle::HashType::SHA256, + encoding); + ASSERT_THAT(verifier, IsOk()); + + std::string message = "some data to be signed"; + util::StatusOr<std::string> message_digest = + ComputeDigest(subtle::HashType::SHA256, message); + ASSERT_THAT(message_digest, IsOk()); + util::StatusOr<std::string> signature = (*signer)->Sign(*message_digest); + ASSERT_THAT(signature, IsOkAndHolds(Not(Eq(message)))); + EXPECT_THAT((*verifier)->Verify(*signature, message), IsOk()); + } +} + +TEST(EcdsaRawSignBoringSslTest, VerifySignatureWithEmptyMessage) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() + << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; + } + subtle::EcdsaSignatureEncoding encodings[2] = { + subtle::EcdsaSignatureEncoding::DER, + subtle::EcdsaSignatureEncoding::IEEE_P1363}; + for (subtle::EcdsaSignatureEncoding encoding : encodings) { + util::StatusOr<EcKey> ec_key = subtle::SubtleUtilBoringSSL::GetNewEcKey( + subtle::EllipticCurveType::NIST_P256); + ASSERT_THAT(ec_key, IsOk()); + + util::StatusOr<std::unique_ptr<EcdsaRawSignBoringSsl>> signer = + EcdsaRawSignBoringSsl::New(*ec_key, encoding); + ASSERT_THAT(signer, IsOk()); + + util::StatusOr<std::unique_ptr<subtle::EcdsaVerifyBoringSsl>> verifier = + subtle::EcdsaVerifyBoringSsl::New(*ec_key, subtle::HashType::SHA256, + encoding); + ASSERT_THAT(verifier, IsOk()); + + // Message is a null string_view. + const absl::string_view empty_message; + util::StatusOr<std::string> empty_message_digest = + ComputeDigest(subtle::HashType::SHA256, empty_message); + ASSERT_THAT(empty_message_digest, IsOk()); + util::StatusOr<std::string> empty_msg_signature = + (*signer)->Sign(*empty_message_digest); + ASSERT_THAT(empty_msg_signature, IsOkAndHolds(Not(Eq(empty_message)))); + EXPECT_THAT((*verifier)->Verify(*empty_msg_signature, empty_message), + IsOk()); + } +} + +TEST(EcdsaRawSignBoringSslTest, VerifyFailsWithInvalidMessageOrSignature) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() + << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; + } + subtle::EcdsaSignatureEncoding encodings[2] = { + subtle::EcdsaSignatureEncoding::DER, + subtle::EcdsaSignatureEncoding::IEEE_P1363}; + for (subtle::EcdsaSignatureEncoding encoding : encodings) { + util::StatusOr<EcKey> ec_key = subtle::SubtleUtilBoringSSL::GetNewEcKey( + subtle::EllipticCurveType::NIST_P256); + ASSERT_THAT(ec_key, IsOk()); + + util::StatusOr<std::unique_ptr<EcdsaRawSignBoringSsl>> signer = + EcdsaRawSignBoringSsl::New(*ec_key, encoding); + ASSERT_THAT(signer, IsOk()); + + util::StatusOr<std::unique_ptr<subtle::EcdsaVerifyBoringSsl>> verifier = + subtle::EcdsaVerifyBoringSsl::New(*ec_key, subtle::HashType::SHA256, + encoding); + ASSERT_THAT(verifier, IsOk()); + + std::string message = "some data to be signed"; + util::StatusOr<std::string> message_digest = + ComputeDigest(subtle::HashType::SHA256, message); + ASSERT_THAT(message_digest, IsOk()); + util::StatusOr<std::string> signature = (*signer)->Sign(*message_digest); + ASSERT_THAT(signature, IsOkAndHolds(Not(Eq(message)))); + EXPECT_THAT((*verifier)->Verify(*signature, message), IsOk()); + + EXPECT_THAT((*verifier)->Verify("some bad signature", message), + Not(IsOk())); + EXPECT_THAT((*verifier)->Verify(*signature, "some bad message"), + Not(IsOk())); + } +} + +TEST(EcdsaRawSignBoringSslTest, VerifyFailsWhenEncodingDoesNotMatch) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() + << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; + } + subtle::EcdsaSignatureEncoding encodings[2] = { + subtle::EcdsaSignatureEncoding::DER, + subtle::EcdsaSignatureEncoding::IEEE_P1363}; + for (subtle::EcdsaSignatureEncoding encoding : encodings) { + util::StatusOr<EcKey> ec_key = subtle::SubtleUtilBoringSSL::GetNewEcKey( + subtle::EllipticCurveType::NIST_P256); + ASSERT_THAT(ec_key, IsOk()); + + util::StatusOr<std::unique_ptr<EcdsaRawSignBoringSsl>> signer = + EcdsaRawSignBoringSsl::New(*ec_key, encoding); + ASSERT_THAT(signer, IsOk()); + + util::StatusOr<std::unique_ptr<subtle::EcdsaVerifyBoringSsl>> verifier = + subtle::EcdsaVerifyBoringSsl::New( + *ec_key, subtle::HashType::SHA256, + encoding == subtle::EcdsaSignatureEncoding::DER + ? subtle::EcdsaSignatureEncoding::IEEE_P1363 + : subtle::EcdsaSignatureEncoding::DER); + ASSERT_THAT(verifier, IsOk()); + + std::string message = "some data to be signed"; + util::StatusOr<std::string> message_digest = + ComputeDigest(subtle::HashType::SHA256, message); + ASSERT_THAT(message_digest, IsOk()); + util::StatusOr<std::string> signature = (*signer)->Sign(*message_digest); + ASSERT_THAT(signature, IsOkAndHolds(Not(Eq(message)))); + EXPECT_THAT((*verifier)->Verify(*signature, message), Not(IsOk())); + } +} + +TEST(EcdsaRawSignBoringSslTest, + SignatureSizesAreCorrectWhenUsingIeeeP136Encoding) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() + << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; + } + subtle::EllipticCurveType curves[3] = {subtle::EllipticCurveType::NIST_P256, + subtle::EllipticCurveType::NIST_P384, + subtle::EllipticCurveType::NIST_P521}; + for (subtle::EllipticCurveType curve : curves) { + util::StatusOr<EcKey> ec_key = + subtle::SubtleUtilBoringSSL::GetNewEcKey(curve); + ASSERT_THAT(ec_key, IsOk()); + + util::StatusOr<std::unique_ptr<EcdsaRawSignBoringSsl>> signer = + EcdsaRawSignBoringSsl::New(*ec_key, + subtle::EcdsaSignatureEncoding::IEEE_P1363); + ASSERT_THAT(signer, IsOk()); + + util::StatusOr<std::unique_ptr<subtle::EcdsaVerifyBoringSsl>> verifier = + subtle::EcdsaVerifyBoringSsl::New( + *ec_key, subtle::HashType::SHA256, + subtle::EcdsaSignatureEncoding::IEEE_P1363); + ASSERT_THAT(verifier, IsOk()); + + std::string message = "some data to be signed"; + util::StatusOr<std::string> message_digest = + ComputeDigest(subtle::HashType::SHA256, message); + ASSERT_THAT(message_digest, IsOk()); + util::StatusOr<std::string> signature = (*signer)->Sign(*message_digest); + ASSERT_THAT(signature, IsOkAndHolds(Not(Eq(message)))); + EXPECT_THAT((*verifier)->Verify(*signature, message), IsOk()); + + // Check signature size. + util::StatusOr<int32_t> field_size_in_bytes = + internal::EcFieldSizeInBytes(curve); + ASSERT_THAT(field_size_in_bytes, IsOk()); + EXPECT_THAT(*signature, SizeIs(2 * (*field_size_in_bytes))); + } +} + +TEST(EcdsaRawSignBoringSslTest, CreateFailsWithBadPublicKey) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() + << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; + } + util::StatusOr<EcKey> ec_key = subtle::SubtleUtilBoringSSL::GetNewEcKey( + subtle::EllipticCurveType::NIST_P256); + ASSERT_THAT(ec_key, IsOk()); + + ec_key->pub_x += "corrupted public key x coordinate"; + EXPECT_THAT( + EcdsaRawSignBoringSsl::New(*ec_key, subtle::EcdsaSignatureEncoding::DER), + Not(IsOk())); +} + +// TODO(bleichen): add Wycheproof tests. + +// FIPS-only mode test +TEST(EcdsaRawSignBoringSslTest, FipsFailWithoutBoringCrypto) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { + GTEST_SKIP() + << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; + } + + util::StatusOr<EcKey> p256_key = subtle::SubtleUtilBoringSSL::GetNewEcKey( + subtle::EllipticCurveType::NIST_P256); + ASSERT_THAT(p256_key, IsOk()); + EXPECT_THAT( + EcdsaRawSignBoringSsl::New(*p256_key, subtle::EcdsaSignatureEncoding::DER) + .status(), + StatusIs(absl::StatusCode::kInternal)); + + util::StatusOr<EcKey> p384_key = subtle::SubtleUtilBoringSSL::GetNewEcKey( + subtle::EllipticCurveType::NIST_P384); + ASSERT_THAT(p384_key, IsOk()); + EXPECT_THAT( + EcdsaRawSignBoringSsl::New(*p384_key, subtle::EcdsaSignatureEncoding::DER) + .status(), + StatusIs(absl::StatusCode::kInternal)); + + util::StatusOr<EcKey> p521_key = *subtle::SubtleUtilBoringSSL::GetNewEcKey( + subtle::EllipticCurveType::NIST_P521); + ASSERT_THAT(p521_key, IsOk()); + EXPECT_THAT( + EcdsaRawSignBoringSsl::New(*p521_key, subtle::EcdsaSignatureEncoding::DER) + .status(), + StatusIs(absl::StatusCode::kInternal)); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto
diff --git a/cc/signature/public_key_sign_factory.cc b/cc/signature/public_key_sign_factory.cc index e0fe718..f44be10 100644 --- a/cc/signature/public_key_sign_factory.cc +++ b/cc/signature/public_key_sign_factory.cc
@@ -16,6 +16,8 @@ #include "tink/signature/public_key_sign_factory.h" +#include <memory> + #include "tink/key_manager.h" #include "tink/keyset_handle.h" #include "tink/public_key_sign.h"
diff --git a/cc/signature/public_key_sign_factory.h b/cc/signature/public_key_sign_factory.h index 35e6e78..30c4b82 100644 --- a/cc/signature/public_key_sign_factory.h +++ b/cc/signature/public_key_sign_factory.h
@@ -17,6 +17,8 @@ #ifndef TINK_SIGNATURE_PUBLIC_KEY_SIGN_FACTORY_H_ #define TINK_SIGNATURE_PUBLIC_KEY_SIGN_FACTORY_H_ +#include <memory> + #include "absl/base/macros.h" #include "tink/key_manager.h" #include "tink/keyset_handle.h"
diff --git a/cc/signature/public_key_sign_factory_test.cc b/cc/signature/public_key_sign_factory_test.cc index 804002a..e5b2eb4 100644 --- a/cc/signature/public_key_sign_factory_test.cc +++ b/cc/signature/public_key_sign_factory_test.cc
@@ -20,7 +20,6 @@ #include <utility> #include "gtest/gtest.h" -#include "tink/config.h" #include "tink/crypto_format.h" #include "tink/keyset_handle.h" #include "tink/public_key_sign.h" @@ -33,7 +32,6 @@ #include "proto/ecdsa.pb.h" #include "proto/tink.pb.h" -using crypto::tink::TestKeysetHandle; using crypto::tink::test::AddTinkKey; using google::crypto::tink::EcdsaPrivateKey; using google::crypto::tink::EcdsaSignatureEncoding;
diff --git a/cc/signature/public_key_sign_wrapper.cc b/cc/signature/public_key_sign_wrapper.cc index 8743396..aeab1cf 100644 --- a/cc/signature/public_key_sign_wrapper.cc +++ b/cc/signature/public_key_sign_wrapper.cc
@@ -16,6 +16,7 @@ #include "tink/signature/public_key_sign_wrapper.h" +#include <memory> #include <string> #include <utility> @@ -63,7 +64,7 @@ crypto::tink::util::StatusOr<std::string> Sign( absl::string_view data) const override; - ~PublicKeySignSetWrapper() override {} + ~PublicKeySignSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<PublicKeySign>> public_key_sign_set_;
diff --git a/cc/signature/public_key_sign_wrapper.h b/cc/signature/public_key_sign_wrapper.h index ec58c48..f258dad 100644 --- a/cc/signature/public_key_sign_wrapper.h +++ b/cc/signature/public_key_sign_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_SIGNATURE_PUBLIC_KEY_SIGN_WRAPPER_H_ #define TINK_SIGNATURE_PUBLIC_KEY_SIGN_WRAPPER_H_ +#include <memory> + #include "absl/strings/string_view.h" #include "tink/primitive_set.h" #include "tink/primitive_wrapper.h"
diff --git a/cc/signature/public_key_sign_wrapper_test.cc b/cc/signature/public_key_sign_wrapper_test.cc index 4059d61..db7f099 100644 --- a/cc/signature/public_key_sign_wrapper_test.cc +++ b/cc/signature/public_key_sign_wrapper_test.cc
@@ -16,6 +16,7 @@ #include "tink/signature/public_key_sign_wrapper.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/signature/public_key_verify_factory.cc b/cc/signature/public_key_verify_factory.cc index dffa6d5..8747e44 100644 --- a/cc/signature/public_key_verify_factory.cc +++ b/cc/signature/public_key_verify_factory.cc
@@ -16,6 +16,8 @@ #include "tink/signature/public_key_verify_factory.h" +#include <memory> + #include "tink/key_manager.h" #include "tink/keyset_handle.h" #include "tink/public_key_verify.h"
diff --git a/cc/signature/public_key_verify_factory.h b/cc/signature/public_key_verify_factory.h index e039a04..af21525 100644 --- a/cc/signature/public_key_verify_factory.h +++ b/cc/signature/public_key_verify_factory.h
@@ -17,6 +17,8 @@ #ifndef TINK_SIGNATURE_PUBLIC_KEY_VERIFY_FACTORY_H_ #define TINK_SIGNATURE_PUBLIC_KEY_VERIFY_FACTORY_H_ +#include <memory> + #include "absl/base/macros.h" #include "tink/key_manager.h" #include "tink/keyset_handle.h"
diff --git a/cc/signature/public_key_verify_factory_test.cc b/cc/signature/public_key_verify_factory_test.cc index 94564c6..f302e43 100644 --- a/cc/signature/public_key_verify_factory_test.cc +++ b/cc/signature/public_key_verify_factory_test.cc
@@ -20,7 +20,6 @@ #include <utility> #include "gtest/gtest.h" -#include "tink/config.h" #include "tink/crypto_format.h" #include "tink/keyset_handle.h" #include "tink/public_key_verify.h" @@ -33,7 +32,6 @@ #include "proto/ecdsa.pb.h" #include "proto/tink.pb.h" -using crypto::tink::TestKeysetHandle; using crypto::tink::test::AddTinkKey; using google::crypto::tink::EcdsaPublicKey; using google::crypto::tink::EcdsaSignatureEncoding;
diff --git a/cc/signature/public_key_verify_wrapper.cc b/cc/signature/public_key_verify_wrapper.cc index 840aadd..784c0d3 100644 --- a/cc/signature/public_key_verify_wrapper.cc +++ b/cc/signature/public_key_verify_wrapper.cc
@@ -16,18 +16,19 @@ #include "tink/signature/public_key_verify_wrapper.h" +#include <memory> #include <string> #include <utility> #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "tink/crypto_format.h" -#include "tink/internal/util.h" -#include "tink/primitive_set.h" -#include "tink/public_key_verify.h" #include "tink/internal/monitoring_util.h" #include "tink/internal/registry_impl.h" +#include "tink/internal/util.h" #include "tink/monitoring/monitoring.h" +#include "tink/primitive_set.h" +#include "tink/public_key_verify.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "proto/tink.pb.h" @@ -65,7 +66,7 @@ crypto::tink::util::Status Verify(absl::string_view signature, absl::string_view data) const override; - ~PublicKeyVerifySetWrapper() override {} + ~PublicKeyVerifySetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<PublicKeyVerify>> public_key_verify_set_;
diff --git a/cc/signature/public_key_verify_wrapper.h b/cc/signature/public_key_verify_wrapper.h index e36d923..3563ec5 100644 --- a/cc/signature/public_key_verify_wrapper.h +++ b/cc/signature/public_key_verify_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_SIGNATURE_PUBLIC_KEY_VERIFY_WRAPPER_H_ #define TINK_SIGNATURE_PUBLIC_KEY_VERIFY_WRAPPER_H_ +#include <memory> + #include "absl/strings/string_view.h" #include "tink/primitive_set.h" #include "tink/primitive_wrapper.h"
diff --git a/cc/signature/public_key_verify_wrapper_test.cc b/cc/signature/public_key_verify_wrapper_test.cc index 7a85945..90f4558 100644 --- a/cc/signature/public_key_verify_wrapper_test.cc +++ b/cc/signature/public_key_verify_wrapper_test.cc
@@ -115,12 +115,12 @@ keyset_info.key_info(0)); ASSERT_TRUE(entry_result.ok()); - pk_verify.reset(new DummyPublicKeyVerify(signature_name_1)); + pk_verify = std::make_unique<DummyPublicKeyVerify>(signature_name_1); entry_result = pk_verify_set->AddPrimitive(std::move(pk_verify), keyset_info.key_info(1)); ASSERT_TRUE(entry_result.ok()); - pk_verify.reset(new DummyPublicKeyVerify(signature_name_2)); + pk_verify = std::make_unique<DummyPublicKeyVerify>(signature_name_2); entry_result = pk_verify_set->AddPrimitive(std::move(pk_verify), keyset_info.key_info(2)); ASSERT_TRUE(entry_result.ok());
diff --git a/cc/signature/rsa_ssa_pkcs1_sign_key_manager.cc b/cc/signature/rsa_ssa_pkcs1_sign_key_manager.cc index cd4c1fa..43c2753 100644 --- a/cc/signature/rsa_ssa_pkcs1_sign_key_manager.cc +++ b/cc/signature/rsa_ssa_pkcs1_sign_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/signature/rsa_ssa_pkcs1_sign_key_manager.h" +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/signature/rsa_ssa_pkcs1_sign_key_manager.h b/cc/signature/rsa_ssa_pkcs1_sign_key_manager.h index ea42ca7..2448b76 100644 --- a/cc/signature/rsa_ssa_pkcs1_sign_key_manager.h +++ b/cc/signature/rsa_ssa_pkcs1_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_SIGNATURE_RSA_SSA_PKCS1_SIGN_KEY_MANAGER_H_ #define TINK_SIGNATURE_RSA_SSA_PKCS1_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/signature/rsa_ssa_pkcs1_verify_key_manager.cc b/cc/signature/rsa_ssa_pkcs1_verify_key_manager.cc index 06f4964..8a62115 100644 --- a/cc/signature/rsa_ssa_pkcs1_verify_key_manager.cc +++ b/cc/signature/rsa_ssa_pkcs1_verify_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/signature/rsa_ssa_pkcs1_verify_key_manager.h" +#include <memory> #include <utility> #include "absl/strings/str_cat.h"
diff --git a/cc/signature/rsa_ssa_pkcs1_verify_key_manager.h b/cc/signature/rsa_ssa_pkcs1_verify_key_manager.h index 8519021..7c1d0cd 100644 --- a/cc/signature/rsa_ssa_pkcs1_verify_key_manager.h +++ b/cc/signature/rsa_ssa_pkcs1_verify_key_manager.h
@@ -18,6 +18,7 @@ #define TINK_SIGNATURE_RSA_SSA_PKCS1_VERIFY_KEY_MANAGER_H_ #include <algorithm> +#include <memory> #include <string> #include <vector>
diff --git a/cc/signature/rsa_ssa_pss_sign_key_manager.cc b/cc/signature/rsa_ssa_pss_sign_key_manager.cc index 8d6bd25..18058ce 100644 --- a/cc/signature/rsa_ssa_pss_sign_key_manager.cc +++ b/cc/signature/rsa_ssa_pss_sign_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/signature/rsa_ssa_pss_sign_key_manager.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/signature/rsa_ssa_pss_sign_key_manager.h b/cc/signature/rsa_ssa_pss_sign_key_manager.h index 14618de..61ad38b 100644 --- a/cc/signature/rsa_ssa_pss_sign_key_manager.h +++ b/cc/signature/rsa_ssa_pss_sign_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_SIGNATURE_RSA_SSA_PSS_SIGN_KEY_MANAGER_H_ #define TINK_SIGNATURE_RSA_SSA_PSS_SIGN_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/signature/rsa_ssa_pss_verify_key_manager.cc b/cc/signature/rsa_ssa_pss_verify_key_manager.cc index 7c7aa8c..abe4806 100644 --- a/cc/signature/rsa_ssa_pss_verify_key_manager.cc +++ b/cc/signature/rsa_ssa_pss_verify_key_manager.cc
@@ -16,6 +16,7 @@ #include "tink/signature/rsa_ssa_pss_verify_key_manager.h" +#include <memory> #include <utility> #include "absl/status/status.h"
diff --git a/cc/signature/rsa_ssa_pss_verify_key_manager.h b/cc/signature/rsa_ssa_pss_verify_key_manager.h index 3fc234a..5642dcd 100644 --- a/cc/signature/rsa_ssa_pss_verify_key_manager.h +++ b/cc/signature/rsa_ssa_pss_verify_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_SIGNATURE_RSA_SSA_PSS_VERIFY_KEY_MANAGER_H_ #define TINK_SIGNATURE_RSA_SSA_PSS_VERIFY_KEY_MANAGER_H_ +#include <memory> #include <string> #include "absl/memory/memory.h"
diff --git a/cc/signature/signature_config.cc b/cc/signature/signature_config.cc index ec7f920..206b3f1 100644 --- a/cc/signature/signature_config.cc +++ b/cc/signature/signature_config.cc
@@ -20,7 +20,6 @@ #include "tink/config/config_util.h" #include "tink/config/tink_fips.h" #include "tink/registry.h" -#include "tink/signature/ecdsa_sign_key_manager.h" #include "tink/signature/ecdsa_verify_key_manager.h" #include "tink/signature/ed25519_sign_key_manager.h" #include "tink/signature/ed25519_verify_key_manager.h" @@ -31,20 +30,13 @@ #include "tink/signature/rsa_ssa_pss_sign_key_manager.h" #include "tink/signature/rsa_ssa_pss_verify_key_manager.h" #include "tink/util/status.h" +#include "tink/signature/ecdsa_sign_key_manager.h" #include "proto/config.pb.h" -using google::crypto::tink::RegistryConfig; - namespace crypto { namespace tink { // static -const google::crypto::tink::RegistryConfig& SignatureConfig::Latest() { - static const RegistryConfig* config = new RegistryConfig(); - return *config; -} - -// static util::Status SignatureConfig::Register() { // Register primitive wrappers. auto status = Registry::RegisterPrimitiveWrapper(
diff --git a/cc/signature/signature_config.h b/cc/signature/signature_config.h index ca9dab4..d1c333a 100644 --- a/cc/signature/signature_config.h +++ b/cc/signature/signature_config.h
@@ -37,16 +37,6 @@ // class SignatureConfig { public: - static constexpr char kPublicKeySignCatalogueName[] = "TinkPublicKeySign"; - static constexpr char kPublicKeyVerifyCatalogueName[] = "TinkPublicKeyVerify"; - static constexpr char kPublicKeySignPrimitiveName[] = "PublicKeySign"; - static constexpr char kPublicKeyVerifyPrimitiveName[] = "PublicKeyVerify"; - - // Returns config with implementations of PublicKeySign and PublicKeyVerify - // supported in the current Tink release. - ABSL_DEPRECATED("This is not supported anymore.") - static const google::crypto::tink::RegistryConfig& Latest(); - // Registers PublicKeySign and PublicKeyVerify primitive wrappers, and key // managers for all implementations of PublicKeySign and PublicKeyVerify from // the current Tink release.
diff --git a/cc/signature/signature_config_test.cc b/cc/signature/signature_config_test.cc index dbeb022..d0f9b02 100644 --- a/cc/signature/signature_config_test.cc +++ b/cc/signature/signature_config_test.cc
@@ -25,8 +25,7 @@ #include "absl/memory/memory.h" #include "absl/status/status.h" #include "openssl/crypto.h" -#include "tink/config.h" -#include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/keyset_handle.h" #include "tink/public_key_sign.h" #include "tink/public_key_verify.h" @@ -54,7 +53,7 @@ }; TEST_F(SignatureConfigTest, testBasic) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -81,7 +80,7 @@ // Tests that the PublicKeySignWrapper has been properly registered and we // can wrap primitives. TEST_F(SignatureConfigTest, PublicKeySignWrapperRegistered) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -117,7 +116,7 @@ // Tests that the PublicKeyVerifyWrapper has been properly registered and we // can wrap primitives. TEST_F(SignatureConfigTest, PublicKeyVerifyWrapperRegistered) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Not supported if FIPS-mode is used and BoringCrypto is " "not available"; } @@ -148,7 +147,7 @@ // FIPS-only mode tests TEST_F(SignatureConfigTest, RegisterNonFipsTemplates) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-only mode with BoringCrypto."; } @@ -173,7 +172,7 @@ } TEST_F(SignatureConfigTest, RegisterFipsValidTemplates) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Only supported in FIPS-only mode with BoringCrypto."; }
diff --git a/cc/signature/signature_key_templates.cc b/cc/signature/signature_key_templates.cc index a95c3bf..e2e33c9 100644 --- a/cc/signature/signature_key_templates.cc +++ b/cc/signature/signature_key_templates.cc
@@ -16,6 +16,8 @@ #include "tink/signature/signature_key_templates.h" +#include <memory> + #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "openssl/bn.h"
diff --git a/cc/signature/signature_pem_keyset_reader.cc b/cc/signature/signature_pem_keyset_reader.cc index 0802e0b..62421be 100644 --- a/cc/signature/signature_pem_keyset_reader.cc +++ b/cc/signature/signature_pem_keyset_reader.cc
@@ -51,7 +51,6 @@ using ::google::crypto::tink::EcdsaParams; using ::google::crypto::tink::EcdsaPublicKey; -using ::google::crypto::tink::EcdsaSignatureEncoding; using ::google::crypto::tink::EllipticCurveType; using ::google::crypto::tink::EncryptedKeyset; using ::google::crypto::tink::HashType;
diff --git a/cc/signature/signature_pem_keyset_reader.h b/cc/signature/signature_pem_keyset_reader.h index bc26ac7..d0f47df 100644 --- a/cc/signature/signature_pem_keyset_reader.h +++ b/cc/signature/signature_pem_keyset_reader.h
@@ -17,6 +17,7 @@ #ifndef TINK_SIGNATURE_SIGNATURE_PEM_KEYSET_READER_H_ #define TINK_SIGNATURE_SIGNATURE_PEM_KEYSET_READER_H_ +#include <memory> #include <string> #include <utility> #include <vector>
diff --git a/cc/signature/signature_pem_keyset_reader_test.cc b/cc/signature/signature_pem_keyset_reader_test.cc index da8ec90..ff4092b 100644 --- a/cc/signature/signature_pem_keyset_reader_test.cc +++ b/cc/signature/signature_pem_keyset_reader_test.cc
@@ -227,6 +227,18 @@ return private_key_proto; } +PemKey CreatePemKey(absl::string_view serialized_key, + crypto::tink::PemKeyType key_type, + crypto::tink::PemAlgorithm algorithm, + size_t key_size_in_bits, + google::crypto::tink::HashType hash_type) { + PemKey pem_key = { + /*serialized_key=*/std::string(serialized_key), + /*parameters=*/{key_type, algorithm, key_size_in_bits, hash_type}, + }; + return pem_key; +} + // Verify check on PEM array size not zero before creating a reader. TEST(SignaturePemKeysetReaderTest, BuildEmptyPemArray) { auto builder = SignaturePemKeysetReaderBuilder( @@ -240,11 +252,9 @@ TEST(SignaturePemKeysetReaderTest, ReadEncryptedUnsupported) { auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kRsaPublicKey2048), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 2048, - .hash_type = HashType::SHA384}}); + builder.Add(CreatePemKey(kRsaPublicKey2048, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/2048, + HashType::SHA384)); auto keyset_reader_or = builder.Build(); ASSERT_THAT(keyset_reader_or, IsOk()); @@ -260,16 +270,12 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kRsaPublicKey2048), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 2048, - .hash_type = HashType::SHA384}}); - builder.Add({.serialized_key = std::string(kRsaPublicKey2048), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 2048, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kRsaPublicKey2048, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/2048, + HashType::SHA384)); + builder.Add(CreatePemKey(kRsaPublicKey2048, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/2048, + HashType::SHA256)); auto keyset_reader_or = builder.Build(); ASSERT_THAT(keyset_reader_or, IsOk()); @@ -330,16 +336,12 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_SIGN); - builder.Add({.serialized_key = std::string(kRsaPrivateKey2048), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 2048, - .hash_type = HashType::SHA256}}); - builder.Add({.serialized_key = std::string(kRsaPrivateKey2048), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 2048, - .hash_type = HashType::SHA384}}); + builder.Add(CreatePemKey(kRsaPrivateKey2048, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/2048, + HashType::SHA256)); + builder.Add(CreatePemKey(kRsaPrivateKey2048, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/2048, + HashType::SHA384)); auto keyset_reader_or = builder.Build(); ASSERT_THAT(keyset_reader_or, IsOk()); @@ -399,11 +401,9 @@ TEST(SignaturePemKeysetReaderTest, ReadRsaPrivateKeyKeyTypeMismatch) { auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_SIGN); - builder.Add({.serialized_key = std::string(kRsaPublicKey2048), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 2048, - .hash_type = HashType::SHA384}}); + builder.Add(CreatePemKey(kRsaPublicKey2048, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/2048, + HashType::SHA384)); auto keyset_reader_or = builder.Build(); ASSERT_THAT(keyset_reader_or, IsOk()); @@ -420,11 +420,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kRsaPrivateKey2048), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 2048, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kRsaPrivateKey2048, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/2048, + HashType::SHA256)); auto keyset_reader_or = builder.Build(); ASSERT_THAT(keyset_reader_or, IsOk()); @@ -440,11 +438,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kRsaPublicKey1024), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 1024, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kRsaPublicKey1024, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/1024, + HashType::SHA256)); auto keyset_reader_or = builder.Build(); ASSERT_THAT(keyset_reader_or, IsOk()); @@ -461,11 +457,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kRsaPublicKey2048), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 3072, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kRsaPublicKey2048, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/3072, + HashType::SHA256)); auto keyset_reader_or = builder.Build(); ASSERT_THAT(keyset_reader_or, IsOk()); @@ -481,11 +475,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kRsaPublicKey2048), - .parameters = {.key_type = PemKeyType::PEM_RSA, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 2048, - .hash_type = HashType::SHA1}}); + builder.Add(CreatePemKey(kRsaPublicKey2048, PemKeyType::PEM_RSA, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/2048, + HashType::SHA1)); auto keyset_reader_or = builder.Build(); ASSERT_THAT(keyset_reader_or, IsOk()); @@ -500,17 +492,13 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kEcdsaP256PublicKey), - .parameters = {.key_type = PemKeyType::PEM_EC, - .algorithm = PemAlgorithm::ECDSA_IEEE, - .key_size_in_bits = 256, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kEcdsaP256PublicKey, PemKeyType::PEM_EC, + PemAlgorithm::ECDSA_IEEE, /*key_size_in_bits=*/256, + HashType::SHA256)); - builder.Add({.serialized_key = std::string(kEcdsaP256PublicKey), - .parameters = {.key_type = PemKeyType::PEM_EC, - .algorithm = PemAlgorithm::ECDSA_DER, - .key_size_in_bits = 256, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kEcdsaP256PublicKey, PemKeyType::PEM_EC, + PemAlgorithm::ECDSA_DER, /*key_size_in_bits=*/256, + HashType::SHA256)); auto reader = builder.Build(); ASSERT_THAT(reader, IsOk()); @@ -565,11 +553,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kEcdsaP256PublicKey), - .parameters = {.key_type = PemKeyType::PEM_EC, - .algorithm = PemAlgorithm::ECDSA_IEEE, - .key_size_in_bits = 256, - .hash_type = HashType::SHA512}}); + builder.Add(CreatePemKey(kEcdsaP256PublicKey, PemKeyType::PEM_EC, + PemAlgorithm::ECDSA_IEEE, /*key_size_in_bits=*/256, + HashType::SHA512)); auto reader = builder.Build(); ASSERT_THAT(reader, IsOk()); @@ -582,11 +568,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kEcdsaP256PublicKey), - .parameters = {.key_type = PemKeyType::PEM_EC, - .algorithm = PemAlgorithm::ECDSA_IEEE, - .key_size_in_bits = 512, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kEcdsaP256PublicKey, PemKeyType::PEM_EC, + PemAlgorithm::ECDSA_IEEE, /*key_size_in_bits=*/512, + HashType::SHA256)); auto reader = builder.Build(); ASSERT_THAT(reader, IsOk()); @@ -599,11 +583,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kEcdsaP256PublicKey), - .parameters = {.key_type = PemKeyType::PEM_EC, - .algorithm = PemAlgorithm::RSASSA_PSS, - .key_size_in_bits = 256, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kEcdsaP256PublicKey, PemKeyType::PEM_EC, + PemAlgorithm::RSASSA_PSS, /*key_size_in_bits=*/256, + HashType::SHA256)); auto reader = builder.Build(); ASSERT_THAT(reader, IsOk()); @@ -616,11 +598,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kEd25519PublicKey), - .parameters = {.key_type = PemKeyType::PEM_EC, - .algorithm = PemAlgorithm::ECDSA_IEEE, - .key_size_in_bits = 256, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kEd25519PublicKey, PemKeyType::PEM_EC, + PemAlgorithm::ECDSA_IEEE, /*key_size_in_bits=*/256, + HashType::SHA256)); auto reader = builder.Build(); ASSERT_THAT(reader, IsOk()); @@ -633,11 +613,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kSecp256k1PublicKey), - .parameters = {.key_type = PemKeyType::PEM_EC, - .algorithm = PemAlgorithm::ECDSA_IEEE, - .key_size_in_bits = 256, - .hash_type = HashType::SHA256}}); + builder.Add(CreatePemKey(kSecp256k1PublicKey, PemKeyType::PEM_EC, + PemAlgorithm::ECDSA_IEEE, /*key_size_in_bits=*/256, + HashType::SHA256)); auto reader = builder.Build(); ASSERT_THAT(reader, IsOk()); @@ -664,11 +642,9 @@ auto builder = SignaturePemKeysetReaderBuilder( SignaturePemKeysetReaderBuilder::PemReaderType::PUBLIC_KEY_VERIFY); - builder.Add({.serialized_key = std::string(kEcdsaP384PublicKey), - .parameters = {.key_type = PemKeyType::PEM_EC, - .algorithm = PemAlgorithm::ECDSA_IEEE, - .key_size_in_bits = 384, - .hash_type = HashType::SHA384}}); + builder.Add(CreatePemKey(kEcdsaP384PublicKey, PemKeyType::PEM_EC, + PemAlgorithm::ECDSA_IEEE, /*key_size_in_bits=*/384, + HashType::SHA384)); auto reader = builder.Build(); ASSERT_THAT(reader, IsOk());
diff --git a/cc/streaming_aead.h b/cc/streaming_aead.h index 139bfdb..81b6333 100644 --- a/cc/streaming_aead.h +++ b/cc/streaming_aead.h
@@ -77,7 +77,7 @@ std::unique_ptr<crypto::tink::RandomAccessStream> ciphertext_source, absl::string_view associated_data) const = 0; - virtual ~StreamingAead() {} + virtual ~StreamingAead() = default; }; } // namespace tink
diff --git a/cc/streaming_mac.h b/cc/streaming_mac.h index 037b148..3ca4f69 100644 --- a/cc/streaming_mac.h +++ b/cc/streaming_mac.h
@@ -44,7 +44,7 @@ virtual util::StatusOr<std::unique_ptr<OutputStreamWithResult<util::Status>>> NewVerifyMacOutputStream(const std::string& mac_value) const = 0; - virtual ~StreamingMac() {} + virtual ~StreamingMac() = default; }; } // namespace tink
diff --git a/cc/streamingaead/BUILD.bazel b/cc/streamingaead/BUILD.bazel index a1d469b..8cf9667 100644 --- a/cc/streamingaead/BUILD.bazel +++ b/cc/streamingaead/BUILD.bazel
@@ -202,17 +202,24 @@ size = "small", srcs = ["streaming_aead_wrapper_test.cc"], deps = [ + ":aes_gcm_hkdf_streaming_key_manager", + ":streaming_aead_config", ":streaming_aead_wrapper", "//:input_stream", + "//:insecure_secret_key_access", "//:output_stream", "//:primitive_set", + "//:proto_keyset_format", "//:random_access_stream", "//:streaming_aead", + "//internal:test_random_access_stream", + "//proto:aes_gcm_hkdf_streaming_cc_proto", + "//proto:common_cc_proto", "//proto:tink_cc_proto", "//subtle:random", + "//subtle:streaming_aead_test_util", "//subtle:test_util", "//util:buffer", - "//util:file_random_access_stream", "//util:istream_input_stream", "//util:ostream_output_stream", "//util:status", @@ -308,7 +315,6 @@ ":aes_gcm_hkdf_streaming_key_manager", ":streaming_aead_config", ":streaming_aead_key_templates", - "//:config", "//:keyset_handle", "//:registry", "//:streaming_aead", @@ -376,10 +382,10 @@ "//:primitive_set", "//:random_access_stream", "//:streaming_aead", + "//internal:test_random_access_stream", "//proto:tink_cc_proto", "//subtle:random", "//subtle:test_util", - "//util:file_random_access_stream", "//util:ostream_output_stream", "//util:status", "//util:test_matchers", @@ -418,10 +424,8 @@ deps = [ ":shared_random_access_stream", "//:random_access_stream", - "//util:buffer", - "//util:file_random_access_stream", - "//util:status", - "//util:test_util", + "//internal:test_random_access_stream", + "//subtle:random", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main",
diff --git a/cc/streamingaead/CMakeLists.txt b/cc/streamingaead/CMakeLists.txt index d4d817e..889e017 100644 --- a/cc/streamingaead/CMakeLists.txt +++ b/cc/streamingaead/CMakeLists.txt
@@ -188,25 +188,32 @@ SRCS streaming_aead_wrapper_test.cc DEPS + tink::streamingaead::aes_gcm_hkdf_streaming_key_manager + tink::streamingaead::streaming_aead_config tink::streamingaead::streaming_aead_wrapper gmock absl::memory absl::status absl::strings tink::core::input_stream + tink::core::insecure_secret_key_access tink::core::output_stream tink::core::primitive_set + tink::core::proto_keyset_format tink::core::random_access_stream tink::core::streaming_aead + tink::internal::test_random_access_stream tink::subtle::random + tink::subtle::streaming_aead_test_util tink::subtle::test_util tink::util::buffer - tink::util::file_random_access_stream tink::util::istream_input_stream tink::util::ostream_output_stream tink::util::status tink::util::test_matchers tink::util::test_util + tink::proto::aes_gcm_hkdf_streaming_cc_proto + tink::proto::common_cc_proto tink::proto::tink_cc_proto ) @@ -292,7 +299,6 @@ gmock absl::memory absl::status - tink::core::config tink::core::keyset_handle tink::core::registry tink::core::streaming_aead @@ -358,9 +364,9 @@ tink::core::primitive_set tink::core::random_access_stream tink::core::streaming_aead + tink::internal::test_random_access_stream tink::subtle::random tink::subtle::test_util - tink::util::file_random_access_stream tink::util::ostream_output_stream tink::util::status tink::util::test_matchers @@ -397,8 +403,6 @@ absl::memory absl::strings tink::core::random_access_stream - tink::util::buffer - tink::util::file_random_access_stream - tink::util::status - tink::util::test_util + tink::internal::test_random_access_stream + tink::subtle::random )
diff --git a/cc/streamingaead/aes_ctr_hmac_streaming_key_manager.cc b/cc/streamingaead/aes_ctr_hmac_streaming_key_manager.cc index a5e5eee..020e4ba 100644 --- a/cc/streamingaead/aes_ctr_hmac_streaming_key_manager.cc +++ b/cc/streamingaead/aes_ctr_hmac_streaming_key_manager.cc
@@ -71,6 +71,10 @@ return Status(absl::StatusCode::kInvalidArgument, "ciphertext_segment_size too small"); } + if (params.ciphertext_segment_size() > 0x7fffffff) { + return Status(absl::StatusCode::kInvalidArgument, + "ciphertext_segment_size too big"); + } return ValidateAesKeySize(params.derived_key_size()); }
diff --git a/cc/streamingaead/aes_ctr_hmac_streaming_key_manager.h b/cc/streamingaead/aes_ctr_hmac_streaming_key_manager.h index 859dd18..1c0ea91 100644 --- a/cc/streamingaead/aes_ctr_hmac_streaming_key_manager.h +++ b/cc/streamingaead/aes_ctr_hmac_streaming_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_STREAMINGAEAD_AES_CTR_HMAC_STREAMING_KEY_MANAGER_H_ #define TINK_STREAMINGAEAD_AES_CTR_HMAC_STREAMING_KEY_MANAGER_H_ +#include <memory> #include <string> #include <utility> @@ -97,7 +98,7 @@ const google::crypto::tink::AesCtrHmacStreamingKeyFormat& key_format, InputStream* input_stream) const override; - ~AesCtrHmacStreamingKeyManager() override {} + ~AesCtrHmacStreamingKeyManager() override = default; private: const std::string key_type_ = absl::StrCat(
diff --git a/cc/streamingaead/aes_ctr_hmac_streaming_key_manager_test.cc b/cc/streamingaead/aes_ctr_hmac_streaming_key_manager_test.cc index f962c3f..231ea46 100644 --- a/cc/streamingaead/aes_ctr_hmac_streaming_key_manager_test.cc +++ b/cc/streamingaead/aes_ctr_hmac_streaming_key_manager_test.cc
@@ -48,7 +48,6 @@ using ::google::crypto::tink::AesCtrHmacStreamingKey; using ::google::crypto::tink::AesCtrHmacStreamingKeyFormat; using ::google::crypto::tink::HashType; -using ::google::crypto::tink::KeyData; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::Not; @@ -226,6 +225,20 @@ HasSubstr("ciphertext_segment_size"))); } +TEST(AesCtrHmacStreamingKeyManagerTest, ValidateKeyFormatTooLargeSegment) { + AesCtrHmacStreamingKeyFormat key_format; + key_format.set_key_size(32); + key_format.mutable_params()->set_derived_key_size(32); + key_format.mutable_params()->set_hkdf_hash_type(HashType::SHA256); + key_format.mutable_params()->set_ciphertext_segment_size(2147483648); + key_format.mutable_params()->mutable_hmac_params()-> + set_hash(HashType::SHA256); + key_format.mutable_params()->mutable_hmac_params()->set_tag_size(32); + EXPECT_THAT(AesCtrHmacStreamingKeyManager().ValidateKeyFormat(key_format), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("ciphertext_segment_size too big"))); +} + TEST(AesCtrHmacStreamingKeyManagerTest, CreateKey) { AesCtrHmacStreamingKeyFormat key_format; key_format.set_key_size(32);
diff --git a/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager.h b/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager.h index 418e1a0..03f5a79 100644 --- a/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager.h +++ b/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager.h
@@ -16,6 +16,7 @@ #ifndef TINK_STREAMINGAEAD_AES_GCM_HKDF_STREAMING_KEY_MANAGER_H_ #define TINK_STREAMINGAEAD_AES_GCM_HKDF_STREAMING_KEY_MANAGER_H_ +#include <memory> #include <string> #include <utility> @@ -93,7 +94,7 @@ const google::crypto::tink::AesGcmHkdfStreamingKeyFormat& key_format, InputStream* input_stream) const override; - ~AesGcmHkdfStreamingKeyManager() override {} + ~AesGcmHkdfStreamingKeyManager() override = default; private: const std::string key_type_ = absl::StrCat(
diff --git a/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager_test.cc b/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager_test.cc index 183012c..ea2d10f 100644 --- a/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager_test.cc +++ b/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager_test.cc
@@ -51,7 +51,6 @@ using ::google::crypto::tink::AesGcmHkdfStreamingKey; using ::google::crypto::tink::AesGcmHkdfStreamingKeyFormat; using ::google::crypto::tink::HashType; -using ::google::crypto::tink::KeyData; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::Not;
diff --git a/cc/streamingaead/buffered_input_stream.cc b/cc/streamingaead/buffered_input_stream.cc index 5654a4f..acbba47 100644 --- a/cc/streamingaead/buffered_input_stream.cc +++ b/cc/streamingaead/buffered_input_stream.cc
@@ -18,6 +18,7 @@ #include <algorithm> #include <cstring> +#include <memory> #include <utility> #include <vector> @@ -135,9 +136,7 @@ return status_; } - -BufferedInputStream::~BufferedInputStream() { -} +BufferedInputStream::~BufferedInputStream() = default; int64_t BufferedInputStream::Position() const { if (direct_access_) return input_stream_->Position();
diff --git a/cc/streamingaead/buffered_input_stream_test.cc b/cc/streamingaead/buffered_input_stream_test.cc index ff80d8b..14792a3 100644 --- a/cc/streamingaead/buffered_input_stream_test.cc +++ b/cc/streamingaead/buffered_input_stream_test.cc
@@ -17,6 +17,7 @@ #include "tink/streamingaead/buffered_input_stream.h" #include <algorithm> +#include <memory> #include <sstream> #include <string> #include <utility>
diff --git a/cc/streamingaead/decrypting_input_stream.cc b/cc/streamingaead/decrypting_input_stream.cc index d09d998..c5e459d 100644 --- a/cc/streamingaead/decrypting_input_stream.cc +++ b/cc/streamingaead/decrypting_input_stream.cc
@@ -18,6 +18,7 @@ #include <algorithm> #include <cstring> +#include <memory> #include <string> #include <utility> #include <vector> @@ -42,6 +43,8 @@ using util::Status; using util::StatusOr; +using StreamingAeadEntry = PrimitiveSet<StreamingAead>::Entry<StreamingAead>; + // static StatusOr<std::unique_ptr<InputStream>> DecryptingInputStream::New( std::shared_ptr<PrimitiveSet<StreamingAead>> primitives, @@ -68,12 +71,10 @@ } // Matching has not been attempted yet, so try it now. attempted_matching_ = true; - auto raw_primitives_result = primitives_->get_raw_primitives(); - if (!raw_primitives_result.ok()) { - return Status(absl::StatusCode::kInternal, "No RAW primitives found"); - } - for (auto& primitive : *(raw_primitives_result.value())) { - StreamingAead& streaming_aead = primitive->get_primitive(); + std::vector<StreamingAeadEntry*> all_primitives = primitives_->get_all(); + + for (const StreamingAeadEntry* entry : all_primitives) { + StreamingAead& streaming_aead = entry->get_primitive(); auto shared_ct = absl::make_unique<SharedInputStream>( buffered_ct_source_.get()); auto decrypting_stream_result = streaming_aead.NewDecryptingStream(
diff --git a/cc/streamingaead/decrypting_input_stream.h b/cc/streamingaead/decrypting_input_stream.h index 61bb2e6..ae3072f 100644 --- a/cc/streamingaead/decrypting_input_stream.h +++ b/cc/streamingaead/decrypting_input_stream.h
@@ -48,7 +48,7 @@ std::unique_ptr<crypto::tink::InputStream> ciphertext_source, absl::string_view associated_data); - ~DecryptingInputStream() override {} + ~DecryptingInputStream() override = default; util::StatusOr<int> Next(const void** data) override; void BackUp(int count) override; int64_t Position() const override;
diff --git a/cc/streamingaead/decrypting_input_stream_test.cc b/cc/streamingaead/decrypting_input_stream_test.cc index 0918ac8..04368ce 100644 --- a/cc/streamingaead/decrypting_input_stream_test.cc +++ b/cc/streamingaead/decrypting_input_stream_test.cc
@@ -16,6 +16,7 @@ #include "tink/streamingaead/decrypting_input_stream.h" +#include <memory> #include <sstream> #include <string> #include <utility>
diff --git a/cc/streamingaead/decrypting_random_access_stream.cc b/cc/streamingaead/decrypting_random_access_stream.cc index df7e9a1..6a5e72e 100644 --- a/cc/streamingaead/decrypting_random_access_stream.cc +++ b/cc/streamingaead/decrypting_random_access_stream.cc
@@ -16,7 +16,9 @@ #include "tink/streamingaead/decrypting_random_access_stream.h" +#include <memory> #include <utility> +#include <vector> #include "absl/memory/memory.h" #include "absl/status/status.h" @@ -39,6 +41,8 @@ using util::Status; using util::StatusOr; +using StreamingAeadEntry = PrimitiveSet<StreamingAead>::Entry<StreamingAead>; + // static StatusOr<std::unique_ptr<RandomAccessStream>> DecryptingRandomAccessStream::New( std::shared_ptr<PrimitiveSet<StreamingAead>> primitives, @@ -97,12 +101,9 @@ "Did not find a decrypter matching the ciphertext stream."); } attempted_matching_ = true; - auto raw_primitives_result = primitives_->get_raw_primitives(); - if (!raw_primitives_result.ok()) { - return Status(absl::StatusCode::kInternal, "No RAW primitives found"); - } - for (auto& primitive : *(raw_primitives_result.value())) { - StreamingAead& streaming_aead = primitive->get_primitive(); + std::vector<StreamingAeadEntry*> all_primitives = primitives_->get_all(); + for (const StreamingAeadEntry* entry : all_primitives) { + StreamingAead& streaming_aead = entry->get_primitive(); auto shared_ct = absl::make_unique<SharedRandomAccessStream>( ciphertext_source_.get()); auto decrypting_stream_result =
diff --git a/cc/streamingaead/decrypting_random_access_stream.h b/cc/streamingaead/decrypting_random_access_stream.h index a78a719..ad409a1 100644 --- a/cc/streamingaead/decrypting_random_access_stream.h +++ b/cc/streamingaead/decrypting_random_access_stream.h
@@ -50,7 +50,7 @@ std::unique_ptr<crypto::tink::RandomAccessStream> ciphertext_source, absl::string_view associated_data); - ~DecryptingRandomAccessStream() override {} + ~DecryptingRandomAccessStream() override = default; crypto::tink::util::Status PRead(int64_t position, int count, crypto::tink::util::Buffer* dest_buffer) override; crypto::tink::util::StatusOr<int64_t> size() override;
diff --git a/cc/streamingaead/decrypting_random_access_stream_test.cc b/cc/streamingaead/decrypting_random_access_stream_test.cc index 496fbbf..232e2db 100644 --- a/cc/streamingaead/decrypting_random_access_stream_test.cc +++ b/cc/streamingaead/decrypting_random_access_stream_test.cc
@@ -16,23 +16,25 @@ #include "tink/streamingaead/decrypting_random_access_stream.h" +#include <memory> #include <sstream> #include <string> #include <utility> #include <vector> +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "tink/internal/test_random_access_stream.h" #include "tink/output_stream.h" #include "tink/primitive_set.h" #include "tink/random_access_stream.h" #include "tink/streaming_aead.h" #include "tink/subtle/random.h" #include "tink/subtle/test_util.h" -#include "tink/util/file_random_access_stream.h" #include "tink/util/ostream_output_stream.h" #include "tink/util/status.h" #include "tink/util/test_matchers.h" @@ -53,16 +55,6 @@ using subtle::test::WriteToStream; using testing::HasSubstr; -// Creates a RandomAccessStream with the specified contents. -std::unique_ptr<RandomAccessStream> GetRandomAccessStream( - absl::string_view contents) { - static int index = 1; - std::string filename = absl::StrCat("stream_data_file_", index, ".txt"); - index++; - int input_fd = test::GetTestFileDescriptor(filename, contents); - return {absl::make_unique<util::FileRandomAccessStream>(input_fd)}; -} - // Creates an RandomAccessStream that contains ciphertext resulting // from encryption of 'pt' with 'aad' as associated data, using 'saead'. std::unique_ptr<RandomAccessStream> GetCiphertextSource( @@ -81,27 +73,7 @@ EXPECT_THAT(WriteToStream(enc_stream_result.value().get(), pt), IsOk()); // Return the ciphertext as RandomAccessStream. - return GetRandomAccessStream(ct_buf->str()); -} - -// Reads the entire 'ra_stream', until no more bytes can be read, -// and puts the read bytes into 'contents'. -// Returns the status of the last ra_stream->PRead()-operation. -util::Status ReadAll(RandomAccessStream* ra_stream, std::string* contents) { - int chunk_size = 42; - contents->clear(); - auto buffer = std::move(util::Buffer::New(chunk_size).value()); - int64_t position = 0; - auto status = ra_stream->PRead(position, chunk_size, buffer.get()); - while (status.ok()) { - contents->append(buffer->get_mem_block(), buffer->size()); - position = contents->size(); - status = ra_stream->PRead(position, chunk_size, buffer.get()); - } - if (status.code() == absl::StatusCode::kOutOfRange) { // EOF - EXPECT_EQ(0, buffer->size()); - } - return status; + return std::make_unique<internal::TestRandomAccessStream>(ct_buf->str()); } // A container for specification of instances of DummyStreamingAead @@ -172,7 +144,8 @@ EXPECT_THAT(dec_stream_result, IsOk()); auto dec_stream = std::move(dec_stream_result.value()); std::string decrypted; - auto status = ReadAll(dec_stream.get(), &decrypted); + auto status = internal::ReadAllFromRandomAccessStream(dec_stream.get(), + decrypted); EXPECT_THAT(status, StatusIs(absl::StatusCode::kOutOfRange, HasSubstr("EOF"))); EXPECT_EQ(pt_size, dec_stream->size().value()); @@ -224,8 +197,11 @@ ", position = ", position, ", chunk_size = ", chunk_size)); auto buffer = std::move(util::Buffer::New(chunk_size).value()); - auto status = dec_stream->PRead(position, chunk_size, buffer.get()); - EXPECT_THAT(status, IsOk()); + util::Status status = + dec_stream->PRead(position, chunk_size, buffer.get()); + EXPECT_THAT(status, + testing::AnyOf( + IsOk(), StatusIs(absl::StatusCode::kOutOfRange))); EXPECT_EQ(std::min(chunk_size, pt_size - position), buffer->size()); EXPECT_EQ(0, std::memcmp(plaintext.data() + position, buffer->get_mem_block(), buffer->size())); @@ -320,7 +296,8 @@ saead_set, std::move(ct), "wrong aad"); EXPECT_THAT(dec_stream_result, IsOk()); std::string decrypted; - auto status = ReadAll(dec_stream_result.value().get(), &decrypted); + auto status = internal::ReadAllFromRandomAccessStream( + dec_stream_result.value().get(), decrypted); EXPECT_THAT(status, StatusIs(absl::StatusCode::kInvalidArgument)); } } @@ -344,20 +321,22 @@ SCOPED_TRACE(absl::StrCat("pt_size = ", pt_size, ", aad = '", aad, "'")); // Try decrypting a wrong ciphertext. - auto wrong_ct = - GetRandomAccessStream(subtle::Random::GetRandomBytes(pt_size)); + auto wrong_ct = std::make_unique<internal::TestRandomAccessStream>( + subtle::Random::GetRandomBytes(pt_size)); auto dec_stream_result = DecryptingRandomAccessStream::New( saead_set, std::move(wrong_ct), aad); EXPECT_THAT(dec_stream_result, IsOk()); std::string decrypted; - auto status = ReadAll(dec_stream_result.value().get(), &decrypted); + auto status = internal::ReadAllFromRandomAccessStream( + dec_stream_result.value().get(), decrypted); EXPECT_THAT(status, StatusIs(absl::StatusCode::kInvalidArgument)); } } } TEST(DecryptingRandomAccessStreamTest, NullPrimitiveSet) { - auto ct_stream = GetRandomAccessStream("some ciphertext contents"); + auto ct_stream = std::make_unique<internal::TestRandomAccessStream>( + "some ciphertext contents"); auto dec_stream_result = DecryptingRandomAccessStream::New( nullptr, std::move(ct_stream), "some aad"); EXPECT_THAT(dec_stream_result.status(),
diff --git a/cc/streamingaead/shared_input_stream.h b/cc/streamingaead/shared_input_stream.h index 32c9755..597d4a8 100644 --- a/cc/streamingaead/shared_input_stream.h +++ b/cc/streamingaead/shared_input_stream.h
@@ -35,7 +35,7 @@ crypto::tink::InputStream* input_stream) : input_stream_(input_stream) {} - ~SharedInputStream() override {} + ~SharedInputStream() override = default; crypto::tink::util::StatusOr<int> Next(const void** data) override { return input_stream_->Next(data);
diff --git a/cc/streamingaead/shared_input_stream_test.cc b/cc/streamingaead/shared_input_stream_test.cc index 27a0f19..aef6638 100644 --- a/cc/streamingaead/shared_input_stream_test.cc +++ b/cc/streamingaead/shared_input_stream_test.cc
@@ -17,6 +17,7 @@ #include "tink/streamingaead/shared_input_stream.h" #include <algorithm> +#include <memory> #include <sstream> #include <string> #include <utility>
diff --git a/cc/streamingaead/shared_random_access_stream.h b/cc/streamingaead/shared_random_access_stream.h index df84d0f..701be1a 100644 --- a/cc/streamingaead/shared_random_access_stream.h +++ b/cc/streamingaead/shared_random_access_stream.h
@@ -38,7 +38,7 @@ crypto::tink::RandomAccessStream* random_access_stream) : random_access_stream_(random_access_stream) {} - ~SharedRandomAccessStream() override {} + ~SharedRandomAccessStream() override = default; crypto::tink::util::Status PRead( int64_t position, int count,
diff --git a/cc/streamingaead/shared_random_access_stream_test.cc b/cc/streamingaead/shared_random_access_stream_test.cc index f0b3269..ddbb5b3 100644 --- a/cc/streamingaead/shared_random_access_stream_test.cc +++ b/cc/streamingaead/shared_random_access_stream_test.cc
@@ -22,53 +22,28 @@ #include "gtest/gtest.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" +#include "tink/internal/test_random_access_stream.h" #include "tink/random_access_stream.h" -#include "tink/util/buffer.h" -#include "tink/util/file_random_access_stream.h" -#include "tink/util/status.h" -#include "tink/util/test_util.h" +#include "tink/subtle/random.h" namespace crypto { namespace tink { namespace streamingaead { namespace { -// Reads the entire 'ra_stream' in chunks of size 'chunk_size', -// until no more bytes can be read, and puts the read bytes into 'contents'. -// Returns the status of the last ra_stream->Next()-operation. -util::Status ReadAll(RandomAccessStream* ra_stream, int chunk_size, - std::string* contents) { - contents->clear(); - auto buffer = std::move(util::Buffer::New(chunk_size).value()); - int64_t position = 0; - auto status = ra_stream->PRead(position, chunk_size, buffer.get()); - while (status.ok()) { - contents->append(buffer->get_mem_block(), buffer->size()); - position = contents->size(); - status = ra_stream->PRead(position, chunk_size, buffer.get()); - } - if (status.code() == absl::StatusCode::kOutOfRange) { // EOF - EXPECT_EQ(0, buffer->size()); - } - return status; -} - TEST(SharedRandomAccessStreamTest, ReadingStreams) { for (auto stream_size : {0, 10, 100, 1000, 10000, 1000000}) { SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size)); - std::string file_contents; - std::string filename = absl::StrCat(stream_size, "_reading_test.bin"); - int input_fd = test::GetTestFileDescriptor( - filename, stream_size, &file_contents); - EXPECT_EQ(stream_size, file_contents.size()); - auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(input_fd); + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + auto ra_stream = + absl::make_unique<internal::TestRandomAccessStream>(stream_content); SharedRandomAccessStream shared_stream(ra_stream.get()); std::string stream_contents; - auto status = ReadAll(&shared_stream, 1 + (stream_size / 10), - &stream_contents); + auto status = internal::ReadAllFromRandomAccessStream( + &shared_stream, stream_contents, /*chunk_size=*/1 + (stream_size / 10)); EXPECT_EQ(absl::StatusCode::kOutOfRange, status.code()); EXPECT_EQ("EOF", status.message()); - EXPECT_EQ(file_contents, stream_contents); + EXPECT_EQ(stream_content, stream_contents); EXPECT_EQ(stream_size, shared_stream.size().value()); } }
diff --git a/cc/streamingaead/streaming_aead_config.cc b/cc/streamingaead/streaming_aead_config.cc index 69ad21e..0ce14fd 100644 --- a/cc/streamingaead/streaming_aead_config.cc +++ b/cc/streamingaead/streaming_aead_config.cc
@@ -25,18 +25,10 @@ #include "tink/streamingaead/streaming_aead_wrapper.h" #include "tink/util/status.h" -using google::crypto::tink::RegistryConfig; - namespace crypto { namespace tink { // static -const RegistryConfig& StreamingAeadConfig::Latest() { - static const RegistryConfig* config = new RegistryConfig(); - return *config; -} - -// static util::Status StreamingAeadConfig::Register() { // Register primitive wrapper. auto status = Registry::RegisterPrimitiveWrapper(
diff --git a/cc/streamingaead/streaming_aead_config.h b/cc/streamingaead/streaming_aead_config.h index 179647d..0ed5ea5 100644 --- a/cc/streamingaead/streaming_aead_config.h +++ b/cc/streamingaead/streaming_aead_config.h
@@ -35,14 +35,6 @@ // class StreamingAeadConfig { public: - static constexpr char kCatalogueName[] = "TinkStreamingAead"; - static constexpr char kPrimitiveName[] = "StreamingAead"; - - // Returns config of StreamingAead implementations supported - // in the current Tink release. - ABSL_DEPRECATED("This is not supported anymore.") - static const google::crypto::tink::RegistryConfig& Latest(); - // Registers StreamingAead primitive wrapper and key managers for all // StreamingAead key types from the current Tink release. static crypto::tink::util::Status Register();
diff --git a/cc/streamingaead/streaming_aead_config_test.cc b/cc/streamingaead/streaming_aead_config_test.cc index d50018f..c3401e5 100644 --- a/cc/streamingaead/streaming_aead_config_test.cc +++ b/cc/streamingaead/streaming_aead_config_test.cc
@@ -24,7 +24,6 @@ #include "gtest/gtest.h" #include "absl/memory/memory.h" #include "absl/status/status.h" -#include "tink/config.h" #include "tink/config/tink_fips.h" #include "tink/keyset_handle.h" #include "tink/registry.h"
diff --git a/cc/streamingaead/streaming_aead_key_templates.cc b/cc/streamingaead/streaming_aead_key_templates.cc index 7605392..ca67d6b 100644 --- a/cc/streamingaead/streaming_aead_key_templates.cc +++ b/cc/streamingaead/streaming_aead_key_templates.cc
@@ -49,7 +49,8 @@ return key_template; } -KeyTemplate* NewAesCtrHmacStreamingKeyTemplate(int ikm_size_in_bytes) { +KeyTemplate* NewAesCtrHmacStreamingKeyTemplate(int ikm_size_in_bytes, + int segment_size_in_bytes) { KeyTemplate* key_template = new KeyTemplate; key_template->set_type_url( "type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey"); @@ -57,7 +58,7 @@ AesCtrHmacStreamingKeyFormat key_format; key_format.set_key_size(ikm_size_in_bytes); auto params = key_format.mutable_params(); - params->set_ciphertext_segment_size(4096); + params->set_ciphertext_segment_size(segment_size_in_bytes); params->set_derived_key_size(ikm_size_in_bytes); params->set_hkdf_hash_type(HashType::SHA256); auto hmac_params = params->mutable_hmac_params(); @@ -92,15 +93,29 @@ // static const KeyTemplate& StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment4KB() { - static const KeyTemplate* key_template = - NewAesCtrHmacStreamingKeyTemplate(/* ikm_size_in_bytes= */ 16); + static const KeyTemplate* key_template = NewAesCtrHmacStreamingKeyTemplate( + /* ikm_size_in_bytes= */ 16, /* segment_size_in_bytes= */ 4096); + return *key_template; +} + +// static +const KeyTemplate& StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment1MB() { + static const KeyTemplate* key_template = NewAesCtrHmacStreamingKeyTemplate( + /* ikm_size_in_bytes= */ 16, /* segment_size_in_bytes= */ 1048576); return *key_template; } // static const KeyTemplate& StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment4KB() { - static const KeyTemplate* key_template = - NewAesCtrHmacStreamingKeyTemplate(/* ikm_size_in_bytes= */ 32); + static const KeyTemplate* key_template = NewAesCtrHmacStreamingKeyTemplate( + /* ikm_size_in_bytes= */ 32, /* segment_size_in_bytes= */ 4096); + return *key_template; +} + +// static +const KeyTemplate& StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment1MB() { + static const KeyTemplate* key_template = NewAesCtrHmacStreamingKeyTemplate( + /* ikm_size_in_bytes= */ 32, /* segment_size_in_bytes= */ 1048576); return *key_template; }
diff --git a/cc/streamingaead/streaming_aead_key_templates.h b/cc/streamingaead/streaming_aead_key_templates.h index 5725fdb..bc7aded 100644 --- a/cc/streamingaead/streaming_aead_key_templates.h +++ b/cc/streamingaead/streaming_aead_key_templates.h
@@ -76,6 +76,18 @@ // Returns a KeyTemplate that generates new instances of // AesCtrHmacStreamingKey with the following parameters: + // - main key (ikm) size: 16 bytes + // - HKDF algorithm: HMAC-SHA256 + // - size of derived AES-CTR keys: 16 bytes + // - tag algorithm: HMAC-SHA256 + // - tag size: 32 bytes + // - ciphertext segment size: 1048576 bytes (1 MB) + // - OutputPrefixType: RAW + static const google::crypto::tink::KeyTemplate& + Aes128CtrHmacSha256Segment1MB(); + + // Returns a KeyTemplate that generates new instances of + // AesCtrHmacStreamingKey with the following parameters: // - main key (ikm) size: 32 bytes // - HKDF algorithm: HMAC-SHA256 // - size of derived AES-CTR keys: 32 bytes @@ -85,6 +97,18 @@ // - OutputPrefixType: RAW static const google::crypto::tink::KeyTemplate& Aes256CtrHmacSha256Segment4KB(); + + // Returns a KeyTemplate that generates new instances of + // AesCtrHmacStreamingKey with the following parameters: + // - main key (ikm) size: 32 bytes + // - HKDF algorithm: HMAC-SHA256 + // - size of derived AES-CTR keys: 32 bytes + // - tag algorithm: HMAC-SHA256 + // - tag size: 32 bytes + // - ciphertext segment size: 1048576 bytes (1 MB) + // - OutputPrefixType: RAW + static const google::crypto::tink::KeyTemplate& + Aes256CtrHmacSha256Segment1MB(); }; } // namespace tink
diff --git a/cc/streamingaead/streaming_aead_key_templates_test.cc b/cc/streamingaead/streaming_aead_key_templates_test.cc index 31098b8..e0c9f91 100644 --- a/cc/streamingaead/streaming_aead_key_templates_test.cc +++ b/cc/streamingaead/streaming_aead_key_templates_test.cc
@@ -203,6 +203,49 @@ EXPECT_THAT(key_format.params().hmac_params().tag_size(), Eq(32)); } +TEST(Aes128CtrHmacSha256Segment1MBTest, TypeUrl) { + EXPECT_THAT( + StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment1MB().type_url(), + Eq("type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey")); + EXPECT_THAT( + StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment1MB().type_url(), + Eq(AesCtrHmacStreamingKeyManager().get_key_type())); +} + +TEST(Aes128CtrHmacSha256Segment1MBTest, OutputPrefixType) { + EXPECT_THAT(StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment1MB() + .output_prefix_type(), + Eq(OutputPrefixType::RAW)); +} + +TEST(Aes128CtrHmacSha256Segment1MBTest, SameReference) { + // Check that reference to the same object is returned. + EXPECT_THAT(StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment1MB(), + Ref(StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment1MB())); +} + +TEST(Aes128CtrHmacSha256Segment1MBTest, WorksWithKeyTypeManager) { + const KeyTemplate& key_template = + StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment1MB(); + AesCtrHmacStreamingKeyFormat key_format; + EXPECT_TRUE(key_format.ParseFromString(key_template.value())); + EXPECT_THAT(AesCtrHmacStreamingKeyManager().ValidateKeyFormat(key_format), + IsOk()); +} + +TEST(Aes128CtrHmacSha256Segment1MBTest, CheckValues) { + const KeyTemplate& key_template = + StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment1MB(); + AesCtrHmacStreamingKeyFormat key_format; + EXPECT_TRUE(key_format.ParseFromString(key_template.value())); + EXPECT_THAT(key_format.key_size(), Eq(16)); + EXPECT_THAT(key_format.params().ciphertext_segment_size(), Eq(1048576)); + EXPECT_THAT(key_format.params().derived_key_size(), Eq(16)); + EXPECT_THAT(key_format.params().hkdf_hash_type(), Eq(HashType::SHA256)); + EXPECT_THAT(key_format.params().hmac_params().hash(), Eq(HashType::SHA256)); + EXPECT_THAT(key_format.params().hmac_params().tag_size(), Eq(32)); +} + TEST(Aes256CtrHmacSha256Segment4KBTest, TypeUrl) { EXPECT_THAT( StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment4KB().type_url(), @@ -246,6 +289,49 @@ EXPECT_THAT(key_format.params().hmac_params().tag_size(), Eq(32)); } +TEST(Aes256CtrHmacSha256Segment1MBTest, TypeUrl) { + EXPECT_THAT( + StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment1MB().type_url(), + Eq("type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey")); + EXPECT_THAT( + StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment1MB().type_url(), + Eq(AesCtrHmacStreamingKeyManager().get_key_type())); +} + +TEST(Aes256CtrHmacSha256Segment1MBTest, OutputPrefixType) { + EXPECT_THAT(StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment1MB() + .output_prefix_type(), + Eq(OutputPrefixType::RAW)); +} + +TEST(Aes256CtrHmacSha256Segment1MBTest, SameReference) { + // Check that reference to the same object is returned. + EXPECT_THAT(StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment1MB(), + Ref(StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment1MB())); +} + +TEST(Aes256CtrHmacSha256Segment1MBTest, WorksWithKeyTypeManager) { + const KeyTemplate& key_template = + StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment1MB(); + AesCtrHmacStreamingKeyFormat key_format; + EXPECT_TRUE(key_format.ParseFromString(key_template.value())); + EXPECT_THAT(AesCtrHmacStreamingKeyManager().ValidateKeyFormat(key_format), + IsOk()); +} + +TEST(Aes256CtrHmacSha256Segment1MBTest, CheckValues) { + const KeyTemplate& key_template = + StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment1MB(); + AesCtrHmacStreamingKeyFormat key_format; + EXPECT_TRUE(key_format.ParseFromString(key_template.value())); + EXPECT_THAT(key_format.key_size(), Eq(32)); + EXPECT_THAT(key_format.params().ciphertext_segment_size(), Eq(1048576)); + EXPECT_THAT(key_format.params().derived_key_size(), Eq(32)); + EXPECT_THAT(key_format.params().hkdf_hash_type(), Eq(HashType::SHA256)); + EXPECT_THAT(key_format.params().hmac_params().hash(), Eq(HashType::SHA256)); + EXPECT_THAT(key_format.params().hmac_params().tag_size(), Eq(32)); +} + } // namespace } // namespace tink } // namespace crypto
diff --git a/cc/streamingaead/streaming_aead_wrapper.cc b/cc/streamingaead/streaming_aead_wrapper.cc index 6f59f45..ae49413 100644 --- a/cc/streamingaead/streaming_aead_wrapper.cc +++ b/cc/streamingaead/streaming_aead_wrapper.cc
@@ -16,7 +16,9 @@ #include "tink/streamingaead/streaming_aead_wrapper.h" +#include <memory> #include <utility> +#include <vector> #include "absl/status/status.h" #include "tink/crypto_format.h" @@ -47,12 +49,6 @@ return Status(absl::StatusCode::kInvalidArgument, "primitive set has no primary"); } - auto raw_primitives_result = primitives->get_raw_primitives(); - if (!raw_primitives_result.ok()) { - return Status(absl::StatusCode::kInvalidArgument, - "primitive set has no raw primitives"); - } - // TODO(b/129044084) return util::OkStatus(); } @@ -78,7 +74,7 @@ std::unique_ptr<crypto::tink::RandomAccessStream> ciphertext_source, absl::string_view associated_data) const override; - ~StreamingAeadSetWrapper() override {} + ~StreamingAeadSetWrapper() override = default; private: // We use a shared_ptr here to ensure that primitives_ stays alive
diff --git a/cc/streamingaead/streaming_aead_wrapper.h b/cc/streamingaead/streaming_aead_wrapper.h index 578be85..2084bb3 100644 --- a/cc/streamingaead/streaming_aead_wrapper.h +++ b/cc/streamingaead/streaming_aead_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_STREAMINGAEAD_STREAMING_AEAD_WRAPPER_H_ #define TINK_STREAMINGAEAD_STREAMING_AEAD_WRAPPER_H_ +#include <memory> + #include "absl/strings/string_view.h" #include "tink/primitive_set.h" #include "tink/primitive_wrapper.h"
diff --git a/cc/streamingaead/streaming_aead_wrapper_test.cc b/cc/streamingaead/streaming_aead_wrapper_test.cc index e262c92..aa6aa72 100644 --- a/cc/streamingaead/streaming_aead_wrapper_test.cc +++ b/cc/streamingaead/streaming_aead_wrapper_test.cc
@@ -16,9 +16,11 @@ #include "tink/streamingaead/streaming_aead_wrapper.h" +#include <memory> #include <sstream> #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/memory/memory.h" @@ -26,64 +28,41 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "tink/input_stream.h" +#include "tink/insecure_secret_key_access.h" +#include "tink/internal/test_random_access_stream.h" #include "tink/output_stream.h" #include "tink/primitive_set.h" +#include "tink/proto_keyset_format.h" #include "tink/random_access_stream.h" #include "tink/streaming_aead.h" +#include "tink/streamingaead/aes_gcm_hkdf_streaming_key_manager.h" +#include "tink/streamingaead/streaming_aead_config.h" #include "tink/subtle/random.h" +#include "tink/subtle/streaming_aead_test_util.h" #include "tink/subtle/test_util.h" #include "tink/util/buffer.h" -#include "tink/util/file_random_access_stream.h" #include "tink/util/istream_input_stream.h" #include "tink/util/ostream_output_stream.h" #include "tink/util/status.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" +#include "proto/aes_gcm_hkdf_streaming.pb.h" +#include "proto/common.pb.h" #include "proto/tink.pb.h" namespace crypto { namespace tink { namespace { -using crypto::tink::test::DummyStreamingAead; -using crypto::tink::test::IsOk; -using crypto::tink::test::StatusIs; -using google::crypto::tink::KeysetInfo; -using google::crypto::tink::KeyStatusType; -using google::crypto::tink::OutputPrefixType; -using subtle::test::ReadFromStream; -using subtle::test::WriteToStream; -using testing::HasSubstr; - -// Creates a RandomAccessStream with the specified contents. -std::unique_ptr<RandomAccessStream> GetRandomAccessStream( - absl::string_view contents) { - static int index = 1; - std::string filename = absl::StrCat("stream_data_file_", index, ".txt"); - index++; - int input_fd = test::GetTestFileDescriptor(filename, contents); - return {absl::make_unique<util::FileRandomAccessStream>(input_fd)}; -} - -// Reads the entire 'ra_stream', until no more bytes can be read, -// and puts the read bytes into 'contents'. -// Returns the status of the last ra_stream->PRead()-operation. -util::Status ReadAll(RandomAccessStream* ra_stream, std::string* contents) { - int chunk_size = 42; - contents->clear(); - auto buffer = std::move(util::Buffer::New(chunk_size).value()); - int64_t position = 0; - auto status = ra_stream->PRead(position, chunk_size, buffer.get()); - while (status.ok()) { - contents->append(buffer->get_mem_block(), buffer->size()); - position = contents->size(); - status = ra_stream->PRead(position, chunk_size, buffer.get()); - } - if (status.code() == absl::StatusCode::kOutOfRange) { // EOF - EXPECT_EQ(0, buffer->size()); - } - return status; -} +using ::crypto::tink::test::DummyStreamingAead; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::KeysetInfo; +using ::google::crypto::tink::KeyStatusType; +using ::google::crypto::tink::OutputPrefixType; +using ::crypto::tink::subtle::test::ReadFromStream; +using ::crypto::tink::subtle::test::WriteToStream; +using ::testing::HasSubstr; // A container for specification of instances of DummyStreamingAead // to be created for testing. @@ -232,12 +211,14 @@ EXPECT_EQ(absl::StrCat(saead_name_2, aad, plaintext), ct_buf->str()); // Decrypt the ciphertext. - auto ct_source = GetRandomAccessStream(ct_buf->str()); + auto ct_source = + std::make_unique<internal::TestRandomAccessStream>(ct_buf->str()); auto dec_stream_result = saead->NewDecryptingRandomAccessStream(std::move(ct_source), aad); EXPECT_THAT(dec_stream_result, IsOk()); std::string decrypted; - status = ReadAll(dec_stream_result.value().get(), &decrypted); + status = internal::ReadAllFromRandomAccessStream( + dec_stream_result.value().get(), decrypted); EXPECT_THAT(status, StatusIs(absl::StatusCode::kOutOfRange, HasSubstr("EOF"))); EXPECT_EQ(plaintext, decrypted); @@ -303,24 +284,63 @@ EXPECT_EQ(plaintext, decrypted); } -TEST(StreamingAeadSetWrapperTest, MissingRawPrimitives) { - uint32_t key_id_0 = 1234543; - uint32_t key_id_1 = 726329; - uint32_t key_id_2 = 7213743; - std::string saead_name_0 = "streaming_aead0"; - std::string saead_name_1 = "streaming_aead1"; - std::string saead_name_2 = "streaming_aead2"; +TEST(StreamingAeadSetWrapperTest, EncryptWithTink) { + ASSERT_THAT(StreamingAeadConfig::Register(), IsOk()); - auto saead_set = GetTestStreamingAeadSet( - {{key_id_0, saead_name_0, OutputPrefixType::TINK}, - {key_id_1, saead_name_1, OutputPrefixType::LEGACY}, - {key_id_2, saead_name_2, OutputPrefixType::TINK}}); + google::crypto::tink::AesGcmHkdfStreamingKey key; + key.set_key_value("0123456789012345"); + google::crypto::tink::AesGcmHkdfStreamingParams& params = + *key.mutable_params(); + params.set_hkdf_hash_type(google::crypto::tink::HashType::SHA1); + params.set_derived_key_size(16); + params.set_ciphertext_segment_size(1024); - // Wrap saead_set and test the resulting StreamingAead. - StreamingAeadWrapper wrapper; - auto wrap_result = wrapper.Wrap(std::move(saead_set)); - EXPECT_THAT(wrap_result.status(), StatusIs(absl::StatusCode::kInvalidArgument, - HasSubstr("no raw primitives"))); + std::string serialized_key_1 = key.SerializeAsString(); + + key.set_key_value("0123456789abcdef"); + std::string serialized_key_2 = key.SerializeAsString(); + + google::crypto::tink::Keyset keyset; + { + google::crypto::tink::Keyset::Key& keyset_key = *keyset.add_key(); + google::crypto::tink::KeyData& key_data = *keyset_key.mutable_key_data(); + key_data.set_type_url(AesGcmHkdfStreamingKeyManager().get_key_type()); + key_data.set_value(serialized_key_1); + key_data.set_key_material_type(google::crypto::tink::KeyData::SYMMETRIC); + keyset_key.set_key_id(1); + keyset_key.set_output_prefix_type( + google::crypto::tink::OutputPrefixType::TINK); + keyset_key.set_status(google::crypto::tink::KeyStatusType::ENABLED); + + keyset.set_primary_key_id(1); + } + { + google::crypto::tink::Keyset::Key& keyset_key = *keyset.add_key(); + google::crypto::tink::KeyData& key_data = *keyset_key.mutable_key_data(); + key_data.set_type_url(AesGcmHkdfStreamingKeyManager().get_key_type()); + key_data.set_value(serialized_key_2); + key_data.set_key_material_type(google::crypto::tink::KeyData::SYMMETRIC); + keyset_key.set_key_id(2); + keyset_key.set_output_prefix_type( + google::crypto::tink::OutputPrefixType::RAW); + keyset_key.set_status(google::crypto::tink::KeyStatusType::ENABLED); + } + + crypto::tink::util::StatusOr<KeysetHandle> handle = + ParseKeysetFromProtoKeysetFormat(keyset.SerializeAsString(), + InsecureSecretKeyAccess::Get()); + ASSERT_THAT(handle.status(), IsOk()); + + crypto::tink::util::StatusOr<std::unique_ptr<StreamingAead>> streaming_aead = + handle->GetPrimitive<StreamingAead>(); + + ASSERT_THAT(streaming_aead.status(), IsOk()); + + EXPECT_THAT(EncryptThenDecrypt(streaming_aead.value().get(), + streaming_aead.value().get(), + subtle::Random::GetRandomBytes(10000), + "some associated data", 0), + IsOk()); } } // namespace
diff --git a/cc/subtle/BUILD.bazel b/cc/subtle/BUILD.bazel index 4998753..caa9741 100644 --- a/cc/subtle/BUILD.bazel +++ b/cc/subtle/BUILD.bazel
@@ -228,14 +228,10 @@ ":common_enums", ":subtle_util_boringssl", "//:public_key_sign", - "//internal:bn_util", - "//internal:ec_util", - "//internal:err_util", "//internal:fips_utils", "//internal:md_util", - "//internal:ssl_unique_ptr", "//internal:util", - "//util:errors", + "//signature/internal:ecdsa_raw_sign_boringssl", "//util:statusor", "@boringssl//:crypto", "@com_google_absl//absl/status", @@ -797,12 +793,11 @@ ":test_util", "//:random_access_stream", "//:streaming_aead", + "//internal:test_random_access_stream", "//util:buffer", - "//util:file_random_access_stream", "//util:istream_input_stream", "//util:ostream_output_stream", "//util:status", - "//util:test_util", "@com_google_absl//absl/strings", ], ) @@ -1004,7 +999,7 @@ ":common_enums", ":hmac_boringssl", "//:mac", - "//config:tink_fips", + "//internal:fips_utils", "//util:secret_data", "//util:status", "//util:statusor", @@ -1019,14 +1014,13 @@ name = "aes_gcm_boringssl_test", size = "small", srcs = ["aes_gcm_boringssl_test.cc"], - data = ["@wycheproof//testvectors:aes_gcm"], + data = ["//testvectors:aes_gcm"], tags = ["fips"], deps = [ ":aes_gcm_boringssl", "//aead/internal:wycheproof_aead", - "//config:tink_fips", + "//internal:fips_utils", "//util:secret_data", - "//util:status", "//util:statusor", "//util:test_matchers", "@com_google_absl//absl/status", @@ -1122,7 +1116,7 @@ name = "aes_eax_boringssl_test", size = "small", srcs = ["aes_eax_boringssl_test.cc"], - data = ["@wycheproof//testvectors:aes_eax"], + data = ["//testvectors:aes_eax"], tags = ["fips"], deps = [ ":aes_eax_boringssl", @@ -1142,7 +1136,6 @@ cc_test( name = "encrypt_then_authenticate_test", - size = "small", srcs = ["encrypt_then_authenticate_test.cc"], deps = [ ":aes_ctr_boringssl", @@ -1167,7 +1160,7 @@ deps = [ ":aes_ctr_boringssl", ":random", - "//config:tink_fips", + "//internal:fips_utils", "//util:secret_data", "//util:status", "//util:statusor", @@ -1182,7 +1175,7 @@ name = "aes_siv_boringssl_test", size = "small", srcs = ["aes_siv_boringssl_test.cc"], - data = ["@wycheproof//testvectors:aes_siv_cmac"], + data = ["//testvectors:aes_siv_cmac"], tags = ["fips"], deps = [ ":aes_siv_boringssl", @@ -1210,8 +1203,8 @@ ":subtle_util_boringssl", "//:public_key_sign", "//:public_key_verify", - "//config:tink_fips", "//internal:ec_util", + "//internal:fips_utils", "//util:status", "//util:statusor", "//util:test_matchers", @@ -1225,8 +1218,8 @@ size = "small", srcs = ["ecdsa_verify_boringssl_test.cc"], data = [ - "@wycheproof//testvectors:ecdsa", - "@wycheproof//testvectors:ecdsa_webcrypto", + "//testvectors:ecdsa", + "//testvectors:ecdsa_webcrypto", ], tags = ["fips"], deps = [ @@ -1237,7 +1230,7 @@ ":wycheproof_util", "//:public_key_sign", "//:public_key_verify", - "//config:tink_fips", + "//internal:fips_utils", "//util:status", "//util:statusor", "//util:test_matchers", @@ -1277,7 +1270,7 @@ name = "ed25519_verify_boringssl_test", size = "small", srcs = ["ed25519_verify_boringssl_test.cc"], - data = ["@wycheproof//testvectors:eddsa"], + data = ["//testvectors:eddsa"], tags = ["fips"], deps = [ ":ed25519_verify_boringssl", @@ -1300,7 +1293,7 @@ name = "rsa_ssa_pss_verify_boringssl_test", size = "small", srcs = ["rsa_ssa_pss_verify_boringssl_test.cc"], - data = ["@wycheproof//testvectors:rsa_pss"], + data = ["//testvectors:rsa_pss"], tags = ["fips"], deps = [ ":common_enums", @@ -1308,8 +1301,8 @@ ":wycheproof_util", "//:public_key_sign", "//:public_key_verify", - "//config:tink_fips", "//internal:err_util", + "//internal:fips_utils", "//internal:rsa_util", "//internal:ssl_unique_ptr", "//util:status", @@ -1330,7 +1323,7 @@ deps = [ ":rsa_ssa_pss_sign_boringssl", ":rsa_ssa_pss_verify_boringssl", - "//config:tink_fips", + "//internal:fips_utils", "//internal:rsa_util", "//internal:ssl_unique_ptr", "//util:test_matchers", @@ -1345,7 +1338,7 @@ name = "rsa_ssa_pkcs1_verify_boringssl_test", size = "small", srcs = ["rsa_ssa_pkcs1_verify_boringssl_test.cc"], - data = ["@wycheproof//testvectors:rsa_signature"], + data = ["//testvectors:rsa_signature"], tags = ["fips"], deps = [ ":common_enums", @@ -1353,8 +1346,8 @@ ":wycheproof_util", "//:public_key_sign", "//:public_key_verify", - "//config:tink_fips", "//internal:err_util", + "//internal:fips_utils", "//internal:rsa_util", "//internal:ssl_unique_ptr", "//util:status", @@ -1375,7 +1368,7 @@ deps = [ ":rsa_ssa_pkcs1_sign_boringssl", ":rsa_ssa_pkcs1_verify_boringssl", - "//config:tink_fips", + "//internal:fips_utils", "//internal:rsa_util", "//internal:ssl_unique_ptr", "//util:test_matchers", @@ -1390,7 +1383,7 @@ name = "aes_gcm_siv_boringssl_test", size = "small", srcs = ["aes_gcm_siv_boringssl_test.cc"], - data = ["@wycheproof//testvectors:aes_gcm_siv"], + data = ["//testvectors:aes_gcm_siv"], tags = ["fips"], deps = [ ":aes_gcm_siv_boringssl", @@ -1446,7 +1439,7 @@ name = "xchacha20_poly1305_boringssl_test", size = "small", srcs = ["xchacha20_poly1305_boringssl_test.cc"], - data = ["@wycheproof//testvectors:chacha20_poly1305"], + data = ["//testvectors:chacha20_poly1305"], tags = ["fips"], deps = [ ":subtle_util", @@ -1545,11 +1538,10 @@ "//:output_stream", "//:random_access_stream", "//:streaming_aead", - "//util:file_random_access_stream", + "//internal:test_random_access_stream", "//util:ostream_output_stream", "//util:status", "//util:test_matchers", - "//util:test_util", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", @@ -1578,7 +1570,7 @@ name = "stateful_hmac_boringssl_test", size = "small", srcs = ["stateful_hmac_boringssl_test.cc"], - data = ["@wycheproof//testvectors:hmac"], + data = ["//testvectors:hmac"], deps = [ ":common_enums", ":stateful_hmac_boringssl", @@ -1598,7 +1590,7 @@ name = "stateful_cmac_boringssl_test", size = "small", srcs = ["stateful_cmac_boringssl_test.cc"], - data = ["@wycheproof//testvectors:aes_cmac"], + data = ["//testvectors:aes_cmac"], deps = [ ":common_enums", ":stateful_cmac_boringssl",
diff --git a/cc/subtle/CMakeLists.txt b/cc/subtle/CMakeLists.txt index 7ac3580..ff38349 100644 --- a/cc/subtle/CMakeLists.txt +++ b/cc/subtle/CMakeLists.txt
@@ -216,14 +216,10 @@ absl::strings crypto tink::core::public_key_sign - tink::internal::bn_util - tink::internal::ec_util - tink::internal::err_util tink::internal::fips_utils tink::internal::md_util - tink::internal::ssl_unique_ptr tink::internal::util - tink::util::errors + tink::signature::internal::ecdsa_raw_sign_boringssl tink::util::statusor ) @@ -648,6 +644,7 @@ tink::internal::test_file_util tink::util::status tink::util::statusor + TESTONLY ) tink_cc_library( @@ -728,6 +725,7 @@ tink::core::output_stream tink::util::status tink::util::statusor + TESTONLY ) tink_cc_library( @@ -741,6 +739,7 @@ tink::core::aead tink::aead::cord_aead tink::util::status + TESTONLY ) tink_cc_library( @@ -753,12 +752,12 @@ absl::strings tink::core::random_access_stream tink::core::streaming_aead + tink::internal::test_random_access_stream tink::util::buffer - tink::util::file_random_access_stream tink::util::istream_input_stream tink::util::ostream_output_stream tink::util::status - tink::util::test_util + TESTONLY ) tink_cc_library( @@ -771,6 +770,7 @@ tink::core::hybrid_decrypt tink::core::hybrid_encrypt tink::util::status + TESTONLY ) tink_cc_library( @@ -946,7 +946,7 @@ absl::status absl::strings tink::core::mac - tink::config::tink_fips + tink::internal::fips_utils tink::util::secret_data tink::util::status tink::util::statusor @@ -965,9 +965,8 @@ absl::status absl::strings tink::aead::internal::wycheproof_aead - tink::config::tink_fips + tink::internal::fips_utils tink::util::secret_data - tink::util::status tink::util::statusor tink::util::test_matchers ) @@ -1097,7 +1096,7 @@ tink::subtle::random gmock absl::status - tink::config::tink_fips + tink::internal::fips_utils tink::util::secret_data tink::util::status tink::util::statusor @@ -1137,8 +1136,8 @@ absl::status tink::core::public_key_sign tink::core::public_key_verify - tink::config::tink_fips tink::internal::ec_util + tink::internal::fips_utils tink::util::status tink::util::statusor tink::util::test_matchers @@ -1162,7 +1161,7 @@ rapidjson tink::core::public_key_sign tink::core::public_key_verify - tink::config::tink_fips + tink::internal::fips_utils tink::util::status tink::util::statusor tink::util::test_matchers @@ -1230,8 +1229,8 @@ rapidjson tink::core::public_key_sign tink::core::public_key_verify - tink::config::tink_fips tink::internal::err_util + tink::internal::fips_utils tink::internal::rsa_util tink::internal::ssl_unique_ptr tink::util::status @@ -1250,7 +1249,7 @@ absl::status absl::strings crypto - tink::config::tink_fips + tink::internal::fips_utils tink::internal::rsa_util tink::internal::ssl_unique_ptr tink::util::test_matchers @@ -1273,8 +1272,8 @@ rapidjson tink::core::public_key_sign tink::core::public_key_verify - tink::config::tink_fips tink::internal::err_util + tink::internal::fips_utils tink::internal::rsa_util tink::internal::ssl_unique_ptr tink::util::status @@ -1293,7 +1292,7 @@ absl::status absl::strings crypto - tink::config::tink_fips + tink::internal::fips_utils tink::internal::rsa_util tink::internal::ssl_unique_ptr tink::util::test_matchers @@ -1457,11 +1456,10 @@ tink::core::output_stream tink::core::random_access_stream tink::core::streaming_aead - tink::util::file_random_access_stream + tink::internal::test_random_access_stream tink::util::ostream_output_stream tink::util::status tink::util::test_matchers - tink::util::test_util ) tink_cc_test(
diff --git a/cc/subtle/aes_cmac_boringssl.cc b/cc/subtle/aes_cmac_boringssl.cc index 9e8cc9c..4afbba1 100644 --- a/cc/subtle/aes_cmac_boringssl.cc +++ b/cc/subtle/aes_cmac_boringssl.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/aes_cmac_boringssl.h" #include <cstdint> +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/aes_cmac_boringssl_test.cc b/cc/subtle/aes_cmac_boringssl_test.cc index d1a6f60..a836211 100644 --- a/cc/subtle/aes_cmac_boringssl_test.cc +++ b/cc/subtle/aes_cmac_boringssl_test.cc
@@ -16,7 +16,9 @@ #include "tink/subtle/aes_cmac_boringssl.h" +#include <memory> #include <string> +#include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/subtle/aes_ctr_boringssl.cc b/cc/subtle/aes_ctr_boringssl.cc index 25d1418..3f6160c 100644 --- a/cc/subtle/aes_ctr_boringssl.cc +++ b/cc/subtle/aes_ctr_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/aes_ctr_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/aes_ctr_boringssl_test.cc b/cc/subtle/aes_ctr_boringssl_test.cc index c812c30..e1d57db 100644 --- a/cc/subtle/aes_ctr_boringssl_test.cc +++ b/cc/subtle/aes_ctr_boringssl_test.cc
@@ -22,7 +22,7 @@ #include "gtest/gtest.h" #include "absl/status/status.h" -#include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/subtle/random.h" #include "tink/util/secret_data.h" #include "tink/util/status.h" @@ -39,7 +39,7 @@ using ::crypto::tink::test::StatusIs; TEST(AesCtrBoringSslTest, TestEncryptDecrypt) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -60,7 +60,7 @@ } TEST(AesCtrBoringSslTest, TestEncryptDecrypt_randomMessage) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -83,7 +83,7 @@ } TEST(AesCtrBoringSslTest, TestEncryptDecrypt_randomKey_randomMessage) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -105,7 +105,7 @@ } TEST(AesCtrBoringSslTest, TestEncryptDecrypt_invalidIvSize) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -122,7 +122,7 @@ } TEST(AesCtrBoringSslTest, TestNistTestVector) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -143,7 +143,7 @@ } TEST(AesCtrBoringSslTest, TestMultipleEncrypt) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -160,7 +160,7 @@ } TEST(AesCtrBoringSslTest, TestFipsOnly) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -175,7 +175,7 @@ } TEST(AesCtrBoringSslTest, TestFipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; }
diff --git a/cc/subtle/aes_ctr_hmac_streaming.cc b/cc/subtle/aes_ctr_hmac_streaming.cc index 597efd3..891ff4c 100644 --- a/cc/subtle/aes_ctr_hmac_streaming.cc +++ b/cc/subtle/aes_ctr_hmac_streaming.cc
@@ -18,6 +18,7 @@ #include <algorithm> #include <limits> +#include <memory> #include <string> #include <utility> #include <vector>
diff --git a/cc/subtle/aes_ctr_hmac_streaming.h b/cc/subtle/aes_ctr_hmac_streaming.h index 4276650..572e5de 100644 --- a/cc/subtle/aes_ctr_hmac_streaming.h +++ b/cc/subtle/aes_ctr_hmac_streaming.h
@@ -183,7 +183,7 @@ return ciphertext_segment_size_; } int get_ciphertext_offset() const override { return ciphertext_offset_; } - ~AesCtrHmacStreamSegmentDecrypter() override {} + ~AesCtrHmacStreamSegmentDecrypter() override = default; private: AesCtrHmacStreamSegmentDecrypter(util::SecretData ikm, HashType hkdf_algo,
diff --git a/cc/subtle/aes_eax_aesni.cc b/cc/subtle/aes_eax_aesni.cc deleted file mode 100644 index 5ad53fb..0000000 --- a/cc/subtle/aes_eax_aesni.cc +++ /dev/null
@@ -1,582 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#include <utility> -#ifdef __SSE4_1__ -#ifdef __AES__ - -#include "tink/subtle/aes_eax_aesni.h" - -#include <emmintrin.h> // SSE2: used for _mm_sub_epi64 _mm_unpacklo_epi64 etc. -#include <smmintrin.h> // SSE4: used for _mm_cmpeq_epi64 -#include <tmmintrin.h> // SSE3: used for _mm_shuffle_epi8 -#include <wmmintrin.h> // AES_NI instructions. -#include <xmmintrin.h> // Datatype _mm128i - -#include <algorithm> -#include <array> -#include <memory> -#include <string> - -#include "absl/algorithm/container.h" -#include "absl/status/status.h" -#include "tink/internal/util.h" -#include "tink/subtle/random.h" -#include "tink/subtle/subtle_util.h" - -namespace crypto { -namespace tink { -namespace subtle { - -namespace { -inline bool EqualBlocks(__m128i x, __m128i y) { - // Compare byte wise. - // A byte in eq is 0xff if the corresponding byte in x and y are equal - // and 0x00 if the corresponding byte in x and y are not equal. - __m128i eq = _mm_cmpeq_epi8(x, y); - // Extract the 16 most significant bits of each byte in eq. - int bits = _mm_movemask_epi8(eq); - return 0xFFFF == bits; -} - -// Reverse the order of the bytes in x. -inline __m128i Reverse(__m128i x) { - const __m128i reverse_order = - _mm_set_epi32(0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f); - return _mm_shuffle_epi8(x, reverse_order); -} - -// Increment x by 1. -// This function assumes that the bytes of x are in little endian order. -// Hence before using the result in EAX the bytes must be reversed, since EAX -// requires a counter value in big endian order. -inline __m128i Increment(__m128i x) { - const __m128i mask = - _mm_set_epi32(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff); - // Determine which of the two 64-bit parts of x overflow. - // The result is 0xff..ff if the corresponding integers overflows and 0 - // otherwise. - __m128i carries = _mm_cmpeq_epi64(x, mask); // SSE4 - // Move the least significant 64 carry bits into the most significant 64 bits - // of diff and fill the least significant bits of diff with 0xff..ff. - __m128i diff = _mm_unpacklo_epi64(mask, carries); - // Use subtraction since the 64-bit parts that must be incremented contain - // the value -1. - return _mm_sub_epi64(x, diff); -} - -// Add y to x. -// This assumes that x is in little endian order. -// So far I've not found a simple way to compute and add the carry using -// xmm instructions. However, optimizing this function is not important, -// since it is used just once during decryption. -inline __m128i Add(__m128i x, uint64 y) { - // Convert to a vector of two uint64. - uint64 vec[2]; - _mm_storeu_si128(reinterpret_cast<__m128i*>(vec), x); - // Perform the addition on the vector. - vec[0] += y; - if (y > vec[0]) { - vec[1]++; - } - // Convert back to xmm. - return _mm_loadu_si128(reinterpret_cast<__m128i*>(vec)); -} - -// Decrement x by 1. -// This function assumes that the bytes of x are in little endian order. -// Hence before using the result in EAX the bytes must be reversed, since EAX -// requires a counter value in big endian order. -inline __m128i Decrement(__m128i x) { - const __m128i zero = _mm_setzero_si128(); - // Moves lower 64 bit of x into higher 64 bits and set the lower 64 bits to 0. - __m128i shifted = _mm_slli_si128(x, 8); - // Determines whether the lower and upper parts must be decremented. - // I.e. the lower 64 bits must always be decremented. - // The upper 64 bits must be decremented if the lower 64 bits of x are 0. - __m128i carries = _mm_cmpeq_epi64(shifted, zero); // SSE4 - // Use add since _mm_cmpeq_epi64 returns -1 for 64-bit parts that are equal. - return _mm_add_epi64(x, carries); -} - -// Rotate a value by 32 bit to the left (assuming little endian order). -inline __m128i RotLeft32(__m128i value) { - return _mm_shuffle_epi32(value, _MM_SHUFFLE(2, 1, 0, 3)); -} - -// Multiply a binary polynomial given in big endian order by x -// and reduce modulo x^128 + x^7 + x^2 + x + 1 -inline __m128i MultiplyByX(__m128i value) { - // Convert big endian to little endian., - value = Reverse(value); - // Sets each dword to 0xffffffff if the most significant bit of the same - // dword in value is set. - __m128i msb = _mm_srai_epi32(value, 31); - __m128i msb_rotated = RotLeft32(msb); - // Determines the carries. If the most signigicant bit in value is set, - // then this bit is reduced to x^7 + x^2 + x + 1 - // (which corresponds to the constant 0x87). - __m128i carry = _mm_and_si128(msb_rotated, _mm_set_epi32(1, 1, 1, 0x87)); - __m128i res = _mm_xor_si128(_mm_slli_epi32(value, 1), carry); - // Converts the result back to big endian order. - return Reverse(res); -} - -// Load block[0]..block[block_size-1] into the least significant bytes of -// a register and set the remaining bytes to 0. The efficiency of this function -// is not critical. -__m128i LoadPartialBlock(const uint8_t* block, size_t block_size) { - std::array<uint8_t, 16> tmp; - tmp.fill(0); - std::copy_n(block, block_size, tmp.begin()); - return _mm_loadu_si128(reinterpret_cast<__m128i*>(tmp.data())); -} - -// Store the block_size least significant bytes from value in -// block[0] .. block[block_size - 1]. The efficiency of this procedure is not -// critical. -void StorePartialBlock(uint8_t* block, size_t block_size, __m128i value) { - std::array<uint8_t, 16> tmp; - _mm_storeu_si128(reinterpret_cast<__m128i*>(tmp.data()), value); - std::copy_n(tmp.begin(), block_size, block); -} - -static const uint8_t kRoundConstant[11] = - {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36}; -// Returns the round constant for round i. -uint8_t Rcon(int round) { - return kRoundConstant[round]; -} - -// Call the AESKEYGENASSIST operation on a 32-bit input. -// This performs a rotation and a substitution with an S-box. -// This implementation uses AESKEYGENASSIST to compute the result twice -// and checks that the two results match. -inline uint32 SubRot(uint32 tmp) { - __m128i inp = _mm_set_epi32(0, 0, tmp, 0); - __m128i out = _mm_aeskeygenassist_si128(inp, 0x00); - return _mm_extract_epi32(out, 1); -} - -// Apply the S-box to the 4 bytes in a word. -// This operation is used in the key expansion of 256-bit keys. -// This implementation computes the result twice and checks equality. -inline uint32 SubWord(uint32 tmp) { - __m128i inp = _mm_set_epi32(0, 0, tmp, 0); - __m128i out = _mm_aeskeygenassist_si128(inp, 0x00); - return _mm_extract_epi32(out, 0); -} - -// The following code uses a key expansion that closely follows FIPS 197. -// If necessary it is possible to unroll the loops. -void Aes128KeyExpansion(const uint8_t* key, __m128i *round_key) { - const int Nk = 4; // Number of words in the key - const int Nb = 4; // Number of words per round key - const int Nr = 10; // Number or rounds - uint32 *w = reinterpret_cast<uint32*>(round_key); - const uint32 *keywords = reinterpret_cast<const uint32*>(key); - for (int i = 0; i < Nk; i++) { - w[i] = keywords[i]; - } - uint32 tmp = w[Nk - 1]; - for (int i = Nk; i < Nb * (Nr + 1); i++) { - if (i % Nk == 0) { - tmp = SubRot(tmp) ^ Rcon(i / Nk); - } - tmp ^= w[i - Nk]; - w[i] = tmp; - } -} - -void Aes256KeyExpansion(const uint8_t* key, __m128i *round_key) { - const int Nk = 8; // Number of words in the key - const int Nb = 4; // Number of words per round key - const int Nr = 14; // Number or rounds - uint32 *w = reinterpret_cast<uint32*>(round_key); - const uint32 *keywords = reinterpret_cast<const uint32*>(key); - for (int i = 0; i < Nk; i++) { - w[i] = keywords[i]; - } - uint32 tmp = w[Nk - 1]; - for (int i = Nk; i < Nb * (Nr + 1); i++) { - if (i % Nk == 0) { - tmp = SubRot(tmp) ^ Rcon(i / Nk); - } else if (i % 4 == 0) { - tmp = SubWord(tmp); - } - tmp ^= w[i - Nk]; - w[i] = tmp; - } -} - -bool IsValidNonceSize(size_t nonce_size) { - return nonce_size == 12 || nonce_size == 16; -} - -bool IsValidKeySize(size_t key_size) { - return key_size == 16 || key_size == 32; -} - -} // namespace - -crypto::tink::util::StatusOr<std::unique_ptr<Aead>> AesEaxAesni::New( - const util::SecretData& key, size_t nonce_size_in_bytes) { - if (!IsValidKeySize(key.size())) { - return util::Status(absl::StatusCode::kInvalidArgument, "Invalid key size"); - } - if (!IsValidNonceSize(nonce_size_in_bytes)) { - return util::Status(absl::StatusCode::kInvalidArgument, - "Invalid nonce size"); - } - auto eax = absl::WrapUnique(new AesEaxAesni(nonce_size_in_bytes)); - if (!eax->SetKey(key)) { - return util::Status(absl::StatusCode::kInternal, "Setting AES key failed"); - } - return {std::move(eax)}; -} - -bool AesEaxAesni::SetKey(const util::SecretData& key) { - size_t key_size = key.size(); - if (key_size == 16) { - rounds_ = 10; - Aes128KeyExpansion(key.data(), round_key_->data()); - } else if (key_size == 32) { - rounds_ = 14; - Aes256KeyExpansion(key.data(), round_key_->data()); - } else { - return false; - } - // Determine the round keys for decryption. - (*round_dec_key_)[0] = (*round_key_)[rounds_]; - (*round_dec_key_)[rounds_] = (*round_key_)[0]; - for (int i = 1; i < rounds_; i++) { - (*round_dec_key_)[i] = _mm_aesimc_si128((*round_key_)[rounds_ - i]); - } - - // Derive the paddings from the key. - __m128i zero = _mm_setzero_si128(); - __m128i zero_encrypted = EncryptBlock(zero); - *B_ = MultiplyByX(zero_encrypted); - *P_ = MultiplyByX(*B_); - return true; -} - -inline void AesEaxAesni::Encrypt3Decrypt1( - const __m128i in0, - const __m128i in1, - const __m128i in2, - const __m128i in_dec, - __m128i* out0, - __m128i* out1, - __m128i* out2, - __m128i* out_dec) const { - __m128i first_round = (*round_key_)[0]; - __m128i tmp0 = _mm_xor_si128(in0, first_round); - __m128i tmp1 = _mm_xor_si128(in1, first_round); - __m128i tmp2 = _mm_xor_si128(in2, first_round); - __m128i tmp3 = _mm_xor_si128(in_dec, (*round_dec_key_)[0]); - for (int i = 1; i < rounds_; i++){ - __m128i round_key = (*round_key_)[i]; - tmp0 = _mm_aesenc_si128(tmp0, round_key); - tmp1 = _mm_aesenc_si128(tmp1, round_key); - tmp2 = _mm_aesenc_si128(tmp2, round_key); - tmp3 = _mm_aesdec_si128(tmp3, (*round_dec_key_)[i]); - } - __m128i last_round = (*round_key_)[rounds_]; - *out0 = _mm_aesenclast_si128(tmp0, last_round); - *out1 = _mm_aesenclast_si128(tmp1, last_round); - *out2 = _mm_aesenclast_si128(tmp2, last_round); - *out_dec = _mm_aesdeclast_si128(tmp3, (*round_dec_key_)[rounds_]); -} - -inline __m128i AesEaxAesni::EncryptBlock(__m128i block) const { - __m128i tmp = _mm_xor_si128(block, (*round_key_)[0]); - for (int i = 1; i < rounds_; i++){ - tmp = _mm_aesenc_si128(tmp, (*round_key_)[i]); - } - return _mm_aesenclast_si128(tmp, (*round_key_)[rounds_]); -} - -inline void AesEaxAesni::Encrypt2Blocks( - const __m128i in0, const __m128i in1, __m128i *out0, __m128i *out1) const { - __m128i tmp0 = _mm_xor_si128(in0, (*round_key_)[0]); - __m128i tmp1 = _mm_xor_si128(in1, (*round_key_)[0]); - for (int i = 1; i < rounds_; i++){ - __m128i round_key = (*round_key_)[i]; - tmp0 = _mm_aesenc_si128(tmp0, round_key); - tmp1 = _mm_aesenc_si128(tmp1, round_key); - } - __m128i last_round = (*round_key_)[rounds_]; - *out0 = _mm_aesenclast_si128(tmp0, last_round); - *out1 = _mm_aesenclast_si128(tmp1, last_round); -} - -__m128i AesEaxAesni::Pad(const uint8_t* data, int len) const { - // CHECK(0 <= len && len <= kBlockSize); - // TODO(bleichen): Is there a better way to load n bytes into a register - std::array<uint8_t, kBlockSize> tmp; - tmp.fill(0); - std::copy_n(data, len, tmp.begin()); - if (len == kBlockSize) { - __m128i block = _mm_loadu_si128(reinterpret_cast<__m128i*>(tmp.data())); - return _mm_xor_si128(block, *B_); - } else { - tmp[len] = 0x80; - __m128i block = _mm_loadu_si128(reinterpret_cast<__m128i*>(tmp.data())); - return _mm_xor_si128(block, *P_); - } -} - -__m128i AesEaxAesni::OMAC(absl::string_view blob, int tag) const { - const uint8_t* data = reinterpret_cast<const uint8_t*>(blob.data()); - size_t len = blob.size(); - __m128i state = _mm_set_epi32(tag << 24, 0, 0, 0); - if (len == 0) { - state = _mm_xor_si128(state, *B_); - } else { - state = EncryptBlock(state); - size_t idx = 0; - while (len - idx > kBlockSize) { - __m128i in = _mm_loadu_si128((__m128i*) (data + idx)); - state = _mm_xor_si128(in, state); - state = EncryptBlock(state); - idx += kBlockSize; - } - state = _mm_xor_si128(state, Pad(data + idx, len - idx)); - } - return EncryptBlock(state); -} - -bool AesEaxAesni::RawEncrypt(absl::string_view nonce, absl::string_view in, - absl::string_view associated_data, - absl::Span<uint8_t> ciphertext) const { - // Sanity check - if (in.size() + kTagSize != ciphertext.size()) { - return false; - } - const uint8_t* plaintext = reinterpret_cast<const uint8_t*>(in.data()); - - // NOTE(bleichen): The author of EAX designed this mode, so that - // it would be possible to compute N and H independently of the encryption. - // So far this possiblity is not used in this implementation. - const __m128i N = OMAC(nonce, 0); - const __m128i H = OMAC(associated_data, 1); - - // Compute the initial counter in little endian order. - // EAX uses big endian order, but it is easier to increment - // a counter if it is in little endian order. - __m128i ctr = Reverse(N); - - // Initialize mac with the header of the input for the MAC. - __m128i mac = _mm_set_epi32(0x2000000, 0, 0, 0); - - uint8_t* out = ciphertext.data(); - size_t idx = 0; - __m128i key_stream; - while (idx + kBlockSize < in.size()) { - __m128i ctr_big_endian = Reverse(ctr); - // Get the key stream for one message block and compute - // the MAC for the previous ciphertext block or header. - Encrypt2Blocks(mac, ctr_big_endian, &mac, &key_stream); - __m128i pt = _mm_loadu_si128(reinterpret_cast<const __m128i*>(plaintext)); - __m128i ct = _mm_xor_si128(pt, key_stream); - mac = _mm_xor_si128(mac, ct); - ctr = Increment(ctr); - _mm_storeu_si128(reinterpret_cast<__m128i*>(out), ct); - plaintext += kBlockSize; - out += kBlockSize; - idx += kBlockSize; - } - - // Last block - size_t last_block_size = in.size() - idx; - if (last_block_size > 0) { - __m128i ctr_big_endian = Reverse(ctr); - Encrypt2Blocks(mac, ctr_big_endian, &mac, &key_stream); - __m128i pt = LoadPartialBlock(plaintext, last_block_size); - __m128i ct = _mm_xor_si128(pt, key_stream); - StorePartialBlock(out, last_block_size, ct); - __m128i padded_last_block = Pad(out, last_block_size); - out += last_block_size; - mac = _mm_xor_si128(mac, padded_last_block); - } else { - // Special code for plaintexts of size 0. - mac = _mm_xor_si128(mac, *B_); - } - mac = EncryptBlock(mac); - __m128i tag = _mm_xor_si128(mac, N); - tag = _mm_xor_si128(tag, H); - StorePartialBlock(out, kTagSize, tag); - return true; -} - -bool AesEaxAesni::RawDecrypt(absl::string_view nonce, absl::string_view in, - absl::string_view associated_data, - absl::Span<uint8_t> plaintext) const { - __m128i N = OMAC(nonce, 0); - __m128i H = OMAC(associated_data, 1); - - const uint8_t* ciphertext = reinterpret_cast<const uint8_t*>(in.data()); - const size_t ciphertext_size = in.size(); - - // Sanity checks: RawDecrypt should always be called with valid sizes. - if (ciphertext_size < kTagSize) { - return false; - } - if (ciphertext_size - kTagSize != plaintext.size()) { - return false; - } - - // Get the tag from the ciphertext. - const __m128i tag = _mm_loadu_si128( - reinterpret_cast<const __m128i*>(&ciphertext[plaintext.size()])); - - // A CBC-MAC is reversible. This allows to pipeline the MAC verification - // by recomputing the MAC for the first half of the ciphertext and - // reversion the MAC for the second half. - __m128i mac_forward = _mm_set_epi32(0x2000000, 0, 0, 0); - __m128i mac_backward = _mm_xor_si128(tag, N); - mac_backward = _mm_xor_si128(mac_backward, H); - - // Special case code for empty messages of size 0. - if (plaintext.empty()) { - mac_forward = _mm_xor_si128(mac_forward, *B_); - mac_forward = EncryptBlock(mac_forward); - return EqualBlocks(mac_forward, mac_backward); - } - - const size_t last_block = (plaintext.size() - 1) / kBlockSize; - const size_t last_block_size = ((plaintext.size() - 1) % kBlockSize) + 1; - const __m128i* ciphertext_blocks = - reinterpret_cast<const __m128i*>(ciphertext); - __m128i* plaintext_blocks = reinterpret_cast<__m128i*>(plaintext.data()); - __m128i ctr_forward = Reverse(N); - __m128i ctr_backward = Add(ctr_forward, last_block); - __m128i unused = _mm_setzero_si128(); - __m128i stream_forward; - __m128i stream_backward; - Encrypt3Decrypt1( - Reverse(ctr_backward), mac_forward, unused, mac_backward, - &stream_backward, &mac_forward, &unused, &mac_backward); - __m128i ct = LoadPartialBlock(&ciphertext[plaintext.size() - last_block_size], - last_block_size); - __m128i pt = _mm_xor_si128(ct, stream_backward); - StorePartialBlock(&plaintext[plaintext.size() - last_block_size], - last_block_size, pt); - __m128i padded_last_block = - Pad(&ciphertext[plaintext.size() - last_block_size], last_block_size); - mac_backward = _mm_xor_si128(mac_backward, padded_last_block); - const size_t mid_block = last_block / 2; - // Decrypts two blocks concurrently as long as there are at least two - // blocks to decrypt. The two blocks are the first block not yet decrypted - // and the last block not yet decrypted. The reason for this is that the - // OMAC can be verified at the same time. mac_forward is the OMAC of leading - // ciphertext blocks that have already been decrypted. mac_backward is the - // partial result for the OMAC up to block last_block - i - 1 that is - // necessary so OMAC of the full encryption results in the tag received from - // the ciphertext. - for (size_t i = 0; i < mid_block; i++) { - ctr_backward = Decrement(ctr_backward); - __m128i ct_forward = _mm_loadu_si128(&ciphertext_blocks[i]); - __m128i ct_backward = - _mm_loadu_si128(&ciphertext_blocks[last_block - i - 1]); - mac_forward = _mm_xor_si128(mac_forward, ct_forward); - Encrypt3Decrypt1( - Reverse(ctr_forward), Reverse(ctr_backward), mac_forward, mac_backward, - &stream_forward, &stream_backward, &mac_forward, &mac_backward); - __m128i plaintext_forward = _mm_xor_si128(ct_forward, stream_forward); - __m128i plaintext_backward = _mm_xor_si128(ct_backward, stream_backward); - _mm_storeu_si128(&plaintext_blocks[i], plaintext_forward); - _mm_storeu_si128(&plaintext_blocks[last_block - i - 1], plaintext_backward); - mac_backward = _mm_xor_si128(mac_backward, ct_backward); - ctr_forward = Increment(ctr_forward); - } - // Decrypts and MACs another block, if there is a single block in the middle. - if (last_block & 1) { - __m128i ct = _mm_loadu_si128(&ciphertext_blocks[mid_block]); - mac_forward = _mm_xor_si128(mac_forward, ct); - Encrypt2Blocks( - Reverse(ctr_forward), mac_forward, &stream_forward, &mac_forward); - __m128i pt = _mm_xor_si128(ct, stream_forward); - _mm_storeu_si128(&plaintext_blocks[mid_block], pt); - } - if (!EqualBlocks(mac_forward, mac_backward)) { - absl::c_fill(plaintext, 0); - return false; - } - return true; -} - -crypto::tink::util::StatusOr<std::string> AesEaxAesni::Encrypt( - absl::string_view plaintext, absl::string_view associated_data) const { - // BoringSSL expects a non-null pointer for plaintext and associated_data, - // regardless of whether the size is 0. - plaintext = internal::EnsureStringNonNull(plaintext); - associated_data = internal::EnsureStringNonNull(associated_data); - - if (SIZE_MAX - nonce_size_ - kTagSize <= plaintext.size()) { - return util::Status(absl::StatusCode::kInvalidArgument, - "Plaintext too long"); - } - size_t ciphertext_size = plaintext.size() + nonce_size_ + kTagSize; - std::string ciphertext; - ResizeStringUninitialized(&ciphertext, ciphertext_size); - const std::string nonce = Random::GetRandomBytes(nonce_size_); - absl::c_copy(nonce, ciphertext.begin()); - bool result = RawEncrypt( - nonce, plaintext, associated_data, - absl::MakeSpan(reinterpret_cast<uint8_t*>(&ciphertext[nonce_size_]), - ciphertext_size - nonce_size_)); - if (!result) { - return util::Status(absl::StatusCode::kInternal, "Encryption failed"); - } - return ciphertext; -} - -crypto::tink::util::StatusOr<std::string> AesEaxAesni::Decrypt( - absl::string_view ciphertext, absl::string_view associated_data) const { - // BoringSSL expects a non-null pointer for associated_data, - // regardless of whether the size is 0. - associated_data = internal::EnsureStringNonNull(associated_data); - - size_t ct_size = ciphertext.size(); - if (ct_size < nonce_size_ + kTagSize) { - return util::Status(absl::StatusCode::kInvalidArgument, - "Ciphertext too short"); - } - size_t out_size = ct_size - kTagSize - nonce_size_; - absl::string_view nonce = ciphertext.substr(0, nonce_size_); - absl::string_view encrypted = - ciphertext.substr(nonce_size_, ct_size - nonce_size_); - std::string res; - ResizeStringUninitialized(&res, out_size); - bool result = RawDecrypt( - nonce, encrypted, associated_data, - absl::MakeSpan(reinterpret_cast<uint8_t*>(&res[0]), res.size())); - if (!result) { - return util::Status(absl::StatusCode::kInternal, "Decryption failed"); - } - return res; -} - -} // namespace subtle -} // namespace tink -} // namespace crypto - -#endif // __AES__ -#endif // __SSE4_1__ - -
diff --git a/cc/subtle/aes_eax_aesni.h b/cc/subtle/aes_eax_aesni.h deleted file mode 100644 index 1a0021e..0000000 --- a/cc/subtle/aes_eax_aesni.h +++ /dev/null
@@ -1,131 +0,0 @@ -// Copyright 2018 Google Inc. -// -// 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef TINK_SUBTLE_AES_EAX_AESNI_H_ -#define TINK_SUBTLE_AES_EAX_AESNI_H_ - -#ifdef __SSE4_1__ -#ifdef __AES__ - -#include <xmmintrin.h> - -#include <array> -#include <memory> -#include <string> - -#include "absl/strings/string_view.h" -#include "absl/types/span.h" -#include "tink/aead.h" -#include "tink/util/secret_data.h" -#include "tink/util/status.h" -#include "tink/util/statusor.h" - -namespace crypto { -namespace tink { -namespace subtle { - -// This class implements AES-EAX on CPUs that support the AESNI instruction set -// (as well as SSE 4.1). -// Currently the implementation supports 128 and 256 bit keys and 96 or 128 bit -// nonces. AES-EAX allows arbitrary nonce sizes. Allowing only 96 or 128 bits -// is a tink specific restriction. -class AesEaxAesni : public Aead { - public: - static crypto::tink::util::StatusOr<std::unique_ptr<Aead>> New( - const util::SecretData& key, size_t nonce_size_in_bytes); - - crypto::tink::util::StatusOr<std::string> Encrypt( - absl::string_view plaintext, - absl::string_view associated_data) const override; - - crypto::tink::util::StatusOr<std::string> Decrypt( - absl::string_view ciphertext, - absl::string_view associated_data) const override; - - protected: - // The tag size is fixed for this implementation. - // Using the full 128-bits of the tag allows an efficient verification. - static constexpr size_t kTagSize = 16; - static constexpr size_t kBlockSize = 16; - - virtual bool RawEncrypt(absl::string_view nonce, absl::string_view in, - absl::string_view associated_data, - absl::Span<uint8_t> ciphertext) const; - - virtual bool RawDecrypt(absl::string_view nonce, absl::string_view in, - absl::string_view associated_data, - absl::Span<uint8_t> plaintext) const; - - private: - explicit AesEaxAesni(size_t nonce_size) : nonce_size_(nonce_size) {} - - // AesEaxAesni instances are immutable objects. - // Therefore, the only place where SetKey should be called is in the - // construction, i.e. in New(). - bool SetKey(const util::SecretData& key); - - // Encrypt a single block. - __m128i EncryptBlock(const __m128i block) const; - - // Encrypt 2 blocks with plain AES. - void Encrypt2Blocks( - const __m128i in0, - const __m128i in1, - __m128i *out0, - __m128i *out1) const; - - // Encrypt 3 blocks and decrypts 1 block. - // This is used to decrypt a ciphertext and verify the MAC concurrently. - void Encrypt3Decrypt1( - const __m128i in0, - const __m128i in1, - const __m128i in2, - const __m128i in_dec, - __m128i* out0, - __m128i* out1, - __m128i* out2, - __m128i* out_dec) const; - - // Pads a partial block of size 1 .. 16. - __m128i Pad(const uint8_t* data, int len) const; - - // Computes an OMAC. - __m128i OMAC(absl::string_view blob, int tag) const; - - static constexpr int kMaxRounds = 14; // maximal number of rounds - static constexpr int kMaxRoundKeys = - kMaxRounds + 1; // max number of round keys - using RoundKeys = std::array<__m128i, kMaxRoundKeys>; - util::SecretUniquePtr<RoundKeys> round_key_ = - util::MakeSecretUniquePtr<RoundKeys>(); - util::SecretUniquePtr<RoundKeys> round_dec_key_ = - util::MakeSecretUniquePtr<RoundKeys>(); - util::SecretUniquePtr<__m128i> B_ = - util::MakeSecretUniquePtr<__m128i>(); // Used for padding - util::SecretUniquePtr<__m128i> P_ = - util::MakeSecretUniquePtr<__m128i>(); // Used for padding - int rounds_; - const size_t nonce_size_; -}; - -} // namespace subtle -} // namespace tink -} // namespace crypto - -#endif // __AES__ -#endif // __SSE4_1__ -#endif // TINK_SUBTLE_AES_EAX_AESNI_H_ -
diff --git a/cc/subtle/aes_eax_aesni_test.cc b/cc/subtle/aes_eax_aesni_test.cc deleted file mode 100644 index 9f4f627..0000000 --- a/cc/subtle/aes_eax_aesni_test.cc +++ /dev/null
@@ -1,280 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -#include <utility> -#ifdef __SSE4_1__ -#ifdef __AES__ - -#include "tink/subtle/aes_eax_aesni.h" - -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "tink/subtle/wycheproof_util.h" -#include "tink/util/secret_data.h" -#include "tink/util/statusor.h" -#include "tink/util/test_util.h" - -namespace crypto { -namespace tink { -namespace subtle { -namespace { - -TEST(AesEaxAesniTest, testBasic) { - util::SecretData key = util::SecretDataFromStringView( - test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); - size_t nonce_size = 12; - auto res = AesEaxAesni::New(key, nonce_size); - EXPECT_TRUE(res.ok()) << res.status(); - auto cipher = std::move(res.value()); - std::string message = "Some data to encrypt."; - std::string aad = "Some data to authenticate."; - auto ct = cipher->Encrypt(message, aad); - EXPECT_TRUE(ct.ok()) << ct.status(); - EXPECT_EQ(ct.value().size(), message.size() + nonce_size + 16); - auto pt = cipher->Decrypt(ct.value(), aad); - EXPECT_TRUE(pt.ok()) << pt.status(); - EXPECT_EQ(pt.value(), message); -} - -TEST(AesEaxAesniTest, testMessageSize) { - util::SecretData key = util::SecretDataFromStringView( - test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); - size_t nonce_size = 12; - auto res = AesEaxAesni::New(key, nonce_size); - EXPECT_TRUE(res.ok()) << res.status(); - auto cipher = std::move(res.value()); - for (size_t size = 0; size < 260; size++) { - std::string message(size, 'x'); - std::string aad = ""; - auto ct = cipher->Encrypt(message, aad); - EXPECT_TRUE(ct.ok()) << ct.status(); - EXPECT_EQ(ct.value().size(), message.size() + nonce_size + 16); - auto pt = cipher->Decrypt(ct.value(), aad); - EXPECT_TRUE(pt.ok()) << pt.status(); - EXPECT_EQ(pt.value(), message); - } -} - -TEST(AesEaxAesniTest, testAadSize) { - util::SecretData key = util::SecretDataFromStringView( - test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); - size_t nonce_size = 12; - auto res = AesEaxAesni::New(key, nonce_size); - EXPECT_TRUE(res.ok()) << res.status(); - auto cipher = std::move(res.value()); - for (size_t size = 0; size < 260; size++) { - std::string message("Some message"); - std::string aad(size, 'x'); - auto ct = cipher->Encrypt(message, aad); - EXPECT_TRUE(ct.ok()) << ct.status(); - EXPECT_EQ(ct.value().size(), message.size() + nonce_size + 16); - auto pt = cipher->Decrypt(ct.value(), aad); - EXPECT_TRUE(pt.ok()) << pt.status(); - EXPECT_EQ(pt.value(), message); - } -} - -TEST(AesEaxAesniTest, testLongNonce) { - util::SecretData key = util::SecretDataFromStringView( - test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); - size_t nonce_size = 16; - auto res = AesEaxAesni::New(key, nonce_size); - EXPECT_TRUE(res.ok()) << res.status(); - auto cipher = std::move(res.value()); - std::string message = "Some data to encrypt."; - std::string aad = "Some data to authenticate."; - auto ct = cipher->Encrypt(message, aad); - EXPECT_TRUE(ct.ok()) << ct.status(); - EXPECT_EQ(ct.value().size(), message.size() + nonce_size + 16); - auto pt = cipher->Decrypt(ct.value(), aad); - EXPECT_TRUE(pt.ok()) << pt.status(); - EXPECT_EQ(pt.value(), message); -} - -TEST(AesEaxAesniTest, testModification) { - size_t nonce_size = 12; - util::SecretData key = util::SecretDataFromStringView( - test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); - auto cipher = std::move(AesEaxAesni::New(key, nonce_size).value()); - std::string message = "Some data to encrypt."; - std::string associated_data = "Some data to authenticate."; - std::string ct = cipher->Encrypt(message, associated_data).value(); - EXPECT_TRUE(cipher->Decrypt(ct, associated_data).ok()); - // Modify the ciphertext - for (size_t i = 0; i < ct.size() * 8; i++) { - std::string modified_ct = ct; - modified_ct[i / 8] ^= 1 << (i % 8); - EXPECT_FALSE(cipher->Decrypt(modified_ct, associated_data).ok()) << i; - } - // Modify the associated data - for (size_t i = 0; i < associated_data.size() * 8; i++) { - std::string modified_associated_data = associated_data; - modified_associated_data[i / 8] ^= 1 << (i % 8); - auto decrypted = cipher->Decrypt(ct, modified_associated_data); - EXPECT_FALSE(decrypted.ok()) << i << " pt:" << decrypted.value(); - } - // Truncate the ciphertext - for (size_t i = 0; i < ct.size(); i++) { - std::string truncated_ct(ct, 0, i); - EXPECT_FALSE(cipher->Decrypt(truncated_ct, associated_data).ok()) << i; - } -} - -TEST(AesEaxAesniTest, testInvalidKeySizes) { - size_t nonce_size = 12; - for (int keysize = 0; keysize < 65; keysize++) { - if (keysize == 16 || keysize == 32) { - continue; - } - util::SecretData key(keysize, 'x'); - auto cipher = AesEaxAesni::New(key, nonce_size); - EXPECT_FALSE(cipher.ok()); - } -} - -TEST(AesEaxAesniTest, testEmpty) { - size_t nonce_size = 12; - util::SecretData key = util::SecretDataFromStringView( - test::HexDecodeOrDie("bedcfb5a011ebc84600fcb296c15af0d")); - std::string nonce(test::HexDecodeOrDie("438a547a94ea88dce46c6c85")); - // Expected tag is an empty string with an empty tag is encrypted with - // the nonce above; - std::string tag(test::HexDecodeOrDie("9607977cd7556b1dfedf0c73a35a5197")); - std::string ciphertext = nonce + tag; - auto res = AesEaxAesni::New(key, nonce_size); - EXPECT_TRUE(res.ok()) << res.status(); - auto cipher = std::move(res.value()); - - // Test decryption of the arguments above. - std::string empty_string(""); - absl::string_view empty_string_view(""); - absl::string_view null_string_view; - - auto pt = cipher->Decrypt(ciphertext, empty_string); - EXPECT_TRUE(pt.ok()); - EXPECT_EQ(0, pt.value().size()); - - pt = cipher->Decrypt(ciphertext, empty_string_view); - EXPECT_TRUE(pt.ok()); - EXPECT_EQ(0, pt.value().size()); - - pt = cipher->Decrypt(ciphertext, null_string_view); - EXPECT_TRUE(pt.ok()); - EXPECT_EQ(0, pt.value().size()); - - // Test encryption. - auto ct = cipher->Encrypt(empty_string, empty_string); - EXPECT_TRUE(ct.ok()); - pt = cipher->Decrypt(ct.value(), empty_string); - EXPECT_TRUE(pt.ok()); - EXPECT_EQ(0, pt.value().size()); - - ct = cipher->Encrypt(empty_string_view, empty_string_view); - EXPECT_TRUE(ct.ok()); - pt = cipher->Decrypt(ct.value(), empty_string); - EXPECT_TRUE(pt.ok()); - EXPECT_EQ(0, pt.value().size()); - - ct = cipher->Encrypt(empty_string_view, empty_string_view); - EXPECT_TRUE(ct.ok()); - pt = cipher->Decrypt(ct.value(), empty_string); - EXPECT_TRUE(pt.ok()); - EXPECT_EQ(0, pt.value().size()); - - ct = cipher->Encrypt(null_string_view, null_string_view); - EXPECT_TRUE(ct.ok()); - pt = cipher->Decrypt(ct.value(), empty_string); - EXPECT_TRUE(pt.ok()); - EXPECT_EQ(0, pt.value().size()); -} - -// Test with test vectors from project Wycheproof. -// AesEaxAesni does not allow to pass in IVs. Therefore this test -// can only test decryption. -// Currently AesEaxAesni is restricted to encryption with 12 byte -// IVs and 16 byte tags. Therefore it is necessary to skip tests with -// other parameter sizes. -bool WycheproofTest(const rapidjson::Document &root) { - int errors = 0; - for (const rapidjson::Value& test_group : root["testGroups"].GetArray()) { - const size_t iv_size = test_group["ivSize"].GetInt(); - const size_t key_size = test_group["keySize"].GetInt(); - const size_t tag_size = test_group["tagSize"].GetInt(); - if (key_size != 128 && key_size != 256) { - // Not supported - continue; - } - if (iv_size != 128 && iv_size != 96) { - // Not supported - continue; - } - if (tag_size != 128) { - // Not supported - continue; - } - for (const rapidjson::Value& test : test_group["tests"].GetArray()) { - std::string comment = test["comment"].GetString(); - util::SecretData key = - util::SecretDataFromStringView(WycheproofUtil::GetBytes(test["key"])); - std::string iv = WycheproofUtil::GetBytes(test["iv"]); - std::string msg = WycheproofUtil::GetBytes(test["msg"]); - std::string ct = WycheproofUtil::GetBytes(test["ct"]); - std::string aad = WycheproofUtil::GetBytes(test["aad"]); - std::string tag = WycheproofUtil::GetBytes(test["tag"]); - int id = test["tcId"].GetInt(); - std::string expected = test["result"].GetString(); - auto cipher = std::move(AesEaxAesni::New(key, iv_size / 8).value()); - auto result = cipher->Decrypt(iv + ct + tag, aad); - bool success = result.ok(); - if (success) { - std::string decrypted = result.value(); - if (expected == "invalid") { - ADD_FAILURE() << "decrypted invalid ciphertext:" << id; - errors++; - } else if (msg != decrypted) { - ADD_FAILURE() << "Incorrect decryption:" << id; - errors++; - } - } else { - if (expected == "valid" || expected == "acceptable") { - ADD_FAILURE() - << "Could not decrypt test with tcId:" << id - << " iv_size:" << iv_size - << " tag_size:" << tag_size - << " key_size:" << key_size; - errors++; - } - } - } - } - return errors == 0; -} - -TEST(AesEaxAesniTest, TestVectors) { - std::unique_ptr<rapidjson::Document> root = - WycheproofUtil::ReadTestVectors("aes_eax_test.json"); - ASSERT_TRUE(WycheproofTest(*root)); -} - -} // namespace -} // namespace subtle -} // namespace tink -} // namespace crypto - -#endif // __AES__ -#endif // __SSE4_1__
diff --git a/cc/subtle/aes_eax_boringssl.cc b/cc/subtle/aes_eax_boringssl.cc index 11eafa0..3805cab 100644 --- a/cc/subtle/aes_eax_boringssl.cc +++ b/cc/subtle/aes_eax_boringssl.cc
@@ -66,7 +66,7 @@ #if defined(ABSL_IS_LITTLE_ENDIAN) return ByteSwap(Load64(src)); #elif defined(ABSL_IS_BIG_ENDIAN) - return val; + return Load64(src); #else #error Unknown endianness #endif
diff --git a/cc/subtle/aes_eax_boringssl_test.cc b/cc/subtle/aes_eax_boringssl_test.cc index 82c5067..9e94fe3 100644 --- a/cc/subtle/aes_eax_boringssl_test.cc +++ b/cc/subtle/aes_eax_boringssl_test.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/aes_eax_boringssl.h" +#include <memory> #include <string> #include <utility> #include <vector>
diff --git a/cc/subtle/aes_gcm_boringssl_test.cc b/cc/subtle/aes_gcm_boringssl_test.cc index dd1ad04..b18294a 100644 --- a/cc/subtle/aes_gcm_boringssl_test.cc +++ b/cc/subtle/aes_gcm_boringssl_test.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/aes_gcm_boringssl.h" +#include <memory> #include <string> #include <utility> #include <vector> @@ -26,7 +27,7 @@ #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" #include "tink/aead/internal/wycheproof_aead.h" -#include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/util/secret_data.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" @@ -54,7 +55,7 @@ class AesGcmBoringSslTest : public Test { protected: void SetUp() override { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is " "unavailable."; } @@ -224,7 +225,7 @@ } TEST(AesGcmBoringSslFipsTest, FipsOnly) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -239,7 +240,7 @@ } TEST(AesGcmBoringSslFipsTest, FipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; } @@ -258,7 +259,7 @@ class AesGcmBoringSslWycheproofTest : public TestWithParam<internal::WycheproofTestVector> { void SetUp() override { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is " "unavailable."; }
diff --git a/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter.cc b/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter.cc index 1d570f4..7f14243 100644 --- a/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter.cc +++ b/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter.cc
@@ -21,6 +21,7 @@ #include <limits> #include <memory> #include <utility> +#include <vector> #include "absl/algorithm/container.h" #include "absl/base/config.h"
diff --git a/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_test.cc b/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_test.cc index 8c3ed14..41910fe 100644 --- a/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_test.cc +++ b/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_test.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/aes_gcm_hkdf_stream_segment_decrypter.h" +#include <memory> #include <string> #include <utility> #include <vector>
diff --git a/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter.cc b/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter.cc index aef5e5e..970918e 100644 --- a/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter.cc +++ b/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter.cc
@@ -22,6 +22,7 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include "absl/algorithm/container.h" #include "absl/base/config.h"
diff --git a/cc/subtle/aes_gcm_hkdf_streaming.cc b/cc/subtle/aes_gcm_hkdf_streaming.cc index cd6fbfd..ab59d5d 100644 --- a/cc/subtle/aes_gcm_hkdf_streaming.cc +++ b/cc/subtle/aes_gcm_hkdf_streaming.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/aes_gcm_hkdf_streaming.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/aes_gcm_siv_boringssl_test.cc b/cc/subtle/aes_gcm_siv_boringssl_test.cc index 836ac24..2b39f2b 100644 --- a/cc/subtle/aes_gcm_siv_boringssl_test.cc +++ b/cc/subtle/aes_gcm_siv_boringssl_test.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/aes_gcm_siv_boringssl.h" +#include <memory> #include <string> #include <vector>
diff --git a/cc/subtle/aes_siv_boringssl.cc b/cc/subtle/aes_siv_boringssl.cc index 6a2ff7e..3d9ac86 100644 --- a/cc/subtle/aes_siv_boringssl.cc +++ b/cc/subtle/aes_siv_boringssl.cc
@@ -19,6 +19,7 @@ #include <algorithm> #include <cstdint> #include <iterator> +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/aes_siv_boringssl_test.cc b/cc/subtle/aes_siv_boringssl_test.cc index 6b4d081..fd91061 100644 --- a/cc/subtle/aes_siv_boringssl_test.cc +++ b/cc/subtle/aes_siv_boringssl_test.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/aes_siv_boringssl.h" +#include <memory> #include <string> #include <utility> #include <vector> @@ -115,7 +116,7 @@ "812731321de508761437195ff231765aa4913219873ac6918639816312130011" "abc900bba11400187984719827431246bbab1231eb4145215ff7141436616beb" "9817298148712fed3aab61000ff123313e")); - for (int keysize = 0; keysize <= keymaterial.size(); ++keysize){ + for (int keysize = 0; keysize < keymaterial.size(); ++keysize){ util::SecretData key(&keymaterial[0], &keymaterial[keysize]); auto cipher = AesSivBoringSsl::New(key); if (keysize == 64) {
diff --git a/cc/subtle/decrypting_random_access_stream.cc b/cc/subtle/decrypting_random_access_stream.cc index 0b6d33e..5a99a53 100644 --- a/cc/subtle/decrypting_random_access_stream.cc +++ b/cc/subtle/decrypting_random_access_stream.cc
@@ -18,6 +18,8 @@ #include <algorithm> #include <cstring> +#include <limits> +#include <memory> #include <utility> #include <vector>
diff --git a/cc/subtle/decrypting_random_access_stream_test.cc b/cc/subtle/decrypting_random_access_stream_test.cc index 1d404f3..8c67e2e 100644 --- a/cc/subtle/decrypting_random_access_stream_test.cc +++ b/cc/subtle/decrypting_random_access_stream_test.cc
@@ -17,6 +17,9 @@ #include "tink/subtle/decrypting_random_access_stream.h" #include <algorithm> +#include <cstring> +#include <limits> +#include <memory> #include <sstream> #include <string> #include <utility> @@ -27,25 +30,24 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "tink/internal/test_random_access_stream.h" #include "tink/output_stream.h" #include "tink/random_access_stream.h" #include "tink/streaming_aead.h" #include "tink/subtle/random.h" #include "tink/subtle/test_util.h" -#include "tink/util/file_random_access_stream.h" #include "tink/util/ostream_output_stream.h" #include "tink/util/status.h" #include "tink/util/test_matchers.h" -#include "tink/util/test_util.h" namespace crypto { namespace tink { namespace subtle { namespace { +using ::crypto::tink::internal::TestRandomAccessStream; using crypto::tink::subtle::test::DummyStreamingAead; using crypto::tink::subtle::test::DummyStreamSegmentDecrypter; -using crypto::tink::test::GetTestFileDescriptor; using crypto::tink::test::IsOk; using crypto::tink::test::StatusIs; using subtle::test::WriteToStream; @@ -77,16 +79,6 @@ int ct_offset_; }; -// Creates a RandomAccessStream with the specified contents. -std::unique_ptr<RandomAccessStream> GetRandomAccessStream( - absl::string_view contents) { - static int index = 1; - std::string filename = absl::StrCat("stream_data_file_", index, ".txt"); - index++; - int input_fd = GetTestFileDescriptor(filename, contents); - return {absl::make_unique<util::FileRandomAccessStream>(input_fd)}; -} - // Returns a ciphertext resulting from encryption of 'pt' with 'aad' as // associated data, using 'saead'. std::string GetCiphertext(StreamingAead* saead, absl::string_view pt, @@ -115,24 +107,8 @@ absl::string_view pt, absl::string_view aad, int ct_offset) { - return GetRandomAccessStream(GetCiphertext(saead, pt, aad, ct_offset)); -} - -// Reads the entire 'ra_stream', until no more bytes can be read, -// and puts the read bytes into 'contents'. -// Returns the status of the last ra_stream->PRead()-operation. -util::Status ReadAll(RandomAccessStream* ra_stream, std::string* contents) { - int chunk_size = 42; - contents->clear(); - auto buffer = std::move(util::Buffer::New(chunk_size).value()); - int64_t position = 0; - auto status = util::OkStatus(); - while (status.ok()) { - status = ra_stream->PRead(position, chunk_size, buffer.get()); - contents->append(buffer->get_mem_block(), buffer->size()); - position = contents->size(); - } - return status; + return std::make_unique<TestRandomAccessStream>( + GetCiphertext(saead, pt, aad, ct_offset)); } TEST(DecryptingRandomAccessStreamTest, NegativeCiphertextOffset) { @@ -222,7 +198,8 @@ auto dec_stream = std::move(dec_stream_result.value()); EXPECT_EQ(pt_size, dec_stream->size().value()); std::string decrypted; - auto status = ReadAll(dec_stream.get(), &decrypted); + auto status = internal::ReadAllFromRandomAccessStream( + dec_stream.get(), decrypted); EXPECT_THAT(status, StatusIs(absl::StatusCode::kOutOfRange, HasSubstr("EOF"))); EXPECT_EQ(plaintext, decrypted); @@ -304,8 +281,8 @@ SCOPED_TRACE(absl::StrCat("ct_size = ", ct.size(), ", trunc_ct_size = ", trunc_ct_size, ", chunk_size = ", chunk_size)); - auto trunc_ct = - GetRandomAccessStream(ct.substr(0, trunc_ct_size)); + auto trunc_ct = std::make_unique<TestRandomAccessStream>( + ct.substr(0, trunc_ct_size)); int position = 0; auto per_stream_seg_decrypter = absl::make_unique<DummyStreamSegmentDecrypter>( @@ -376,8 +353,8 @@ for (int ct_size : {0, 10, 100}) { SCOPED_TRACE(absl::StrCat("ct_size = ", ct_size)); // Try decrypting a wrong ciphertext. - auto wrong_ct = - GetRandomAccessStream(subtle::Random::GetRandomBytes(ct_size)); + auto wrong_ct = std::make_unique<TestRandomAccessStream>( + subtle::Random::GetRandomBytes(ct_size)); auto seg_decrypter = absl::make_unique<DummyStreamSegmentDecrypter>( pt_segment_size, header_size, ct_offset); auto dec_stream_result = DecryptingRandomAccessStream::New( @@ -394,7 +371,8 @@ } TEST(DecryptingRandomAccessStreamTest, NullSegmentDecrypter) { - auto ct_stream = GetRandomAccessStream("some ciphertext contents"); + auto ct_stream = + std::make_unique<TestRandomAccessStream>("some ciphertext contents"); auto dec_stream_result = DecryptingRandomAccessStream::New(nullptr, std::move(ct_stream)); EXPECT_THAT(dec_stream_result.status(),
diff --git a/cc/subtle/ecdsa_sign_boringssl.cc b/cc/subtle/ecdsa_sign_boringssl.cc index e524033..a14346a 100644 --- a/cc/subtle/ecdsa_sign_boringssl.cc +++ b/cc/subtle/ecdsa_sign_boringssl.cc
@@ -16,76 +16,24 @@ #include "tink/subtle/ecdsa_sign_boringssl.h" +#include <memory> #include <string> #include <utility> #include <vector> #include "absl/status/status.h" -#include "absl/strings/str_cat.h" -#include "openssl/bn.h" -#include "openssl/ec.h" -#include "openssl/ecdsa.h" #include "openssl/evp.h" -#include "tink/internal/bn_util.h" -#include "tink/internal/ec_util.h" -#include "tink/internal/err_util.h" #include "tink/internal/md_util.h" -#include "tink/internal/ssl_unique_ptr.h" #include "tink/internal/util.h" +#include "tink/signature/internal/ecdsa_raw_sign_boringssl.h" #include "tink/subtle/common_enums.h" #include "tink/subtle/subtle_util_boringssl.h" -#include "tink/util/errors.h" #include "tink/util/statusor.h" namespace crypto { namespace tink { namespace subtle { -namespace { - -// Transforms ECDSA DER signature encoding to IEEE_P1363 encoding. -// -// The IEEE_P1363 signature's format is r || s, where r and s are zero-padded -// and have the same size in bytes as the order of the curve. For example, for -// NIST P-256 curve, r and s are zero-padded to 32 bytes. -// -// The DER signature is encoded using ASN.1 -// (https://tools.ietf.org/html/rfc5480#appendix-A): ECDSA-Sig-Value :: = -// SEQUENCE { r INTEGER, s INTEGER }. In particular, the encoding is: 0x30 || -// totalLength || 0x02 || r's length || r || 0x02 || s's length || s. -crypto::tink::util::StatusOr<std::string> DerToIeee(absl::string_view der, - const EC_KEY* key) { - size_t field_size_in_bytes = - (EC_GROUP_get_degree(EC_KEY_get0_group(key)) + 7) / 8; - - ECDSA_SIG* ecdsa_ptr = nullptr; - const uint8_t* der_ptr = reinterpret_cast<const uint8_t*>(der.data()); - // Note: d2i_ECDSA_SIG is deprecated in BoringSSL, but it isn't in OpenSSL. - internal::SslUniquePtr<ECDSA_SIG> ecdsa( - d2i_ECDSA_SIG(&ecdsa_ptr, &der_ptr, der.size())); - if (ecdsa == nullptr) { - return util::Status(absl::StatusCode::kInternal, "d2i_ECDSA_SIG failed"); - } - - const BIGNUM* r_bn; - const BIGNUM* s_bn; - ECDSA_SIG_get0(ecdsa.get(), &r_bn, &s_bn); - util::StatusOr<std::string> r = - internal::BignumToString(r_bn, field_size_in_bytes); - if (!r.ok()) { - return r.status(); - } - util::StatusOr<std::string> s = - internal::BignumToString(s_bn, field_size_in_bytes); - if (!s.ok()) { - return s.status(); - } - return absl::StrCat(*r, *s); -} - -} // namespace - -// static util::StatusOr<std::unique_ptr<EcdsaSignBoringSsl>> EcdsaSignBoringSsl::New( const SubtleUtilBoringSSL::EcKey& ec_key, HashType hash_type, EcdsaSignatureEncoding encoding) { @@ -102,47 +50,14 @@ return hash.status(); } - // Check curve. - util::StatusOr<internal::SslUniquePtr<EC_GROUP>> group = - internal::EcGroupFromCurveType(ec_key.curve); - if (!group.ok()) { - return group.status(); - } - internal::SslUniquePtr<EC_KEY> key(EC_KEY_new()); - EC_KEY_set_group(key.get(), group->get()); + util::StatusOr<std::unique_ptr<internal::EcdsaRawSignBoringSsl>> raw_sign = + internal::EcdsaRawSignBoringSsl::New(ec_key, encoding); + if (!raw_sign.ok()) return raw_sign.status(); - // Check key. - util::StatusOr<internal::SslUniquePtr<EC_POINT>> pub_key = - internal::GetEcPoint(ec_key.curve, ec_key.pub_x, ec_key.pub_y); - if (!pub_key.ok()) { - return pub_key.status(); - } - - if (!EC_KEY_set_public_key(key.get(), pub_key->get())) { - return util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("Invalid public key: ", internal::GetSslErrors())); - } - - internal::SslUniquePtr<BIGNUM> priv_key( - BN_bin2bn(ec_key.priv.data(), ec_key.priv.size(), nullptr)); - if (!EC_KEY_set_private_key(key.get(), priv_key.get())) { - return util::Status( - absl::StatusCode::kInvalidArgument, - absl::StrCat("Invalid private key: ", internal::GetSslErrors())); - } - - // Sign. - std::unique_ptr<EcdsaSignBoringSsl> sign( - new EcdsaSignBoringSsl(std::move(key), *hash, encoding)); - return std::move(sign); + return { + absl::WrapUnique(new EcdsaSignBoringSsl(*hash, std::move(*raw_sign)))}; } -EcdsaSignBoringSsl::EcdsaSignBoringSsl(internal::SslUniquePtr<EC_KEY> key, - const EVP_MD* hash, - EcdsaSignatureEncoding encoding) - : key_(std::move(key)), hash_(hash), encoding_(encoding) {} - util::StatusOr<std::string> EcdsaSignBoringSsl::Sign( absl::string_view data) const { // BoringSSL expects a non-null pointer for data, @@ -159,24 +74,8 @@ } // Compute the signature. - std::vector<uint8_t> buffer(ECDSA_size(key_.get())); - unsigned int sig_length; - if (1 != ECDSA_sign(0 /* unused */, digest, digest_size, buffer.data(), - &sig_length, key_.get())) { - return util::Status(absl::StatusCode::kInternal, "Signing failed."); - } - - if (encoding_ == subtle::EcdsaSignatureEncoding::IEEE_P1363) { - auto status_or_sig = DerToIeee( - std::string(reinterpret_cast<char*>(buffer.data()), sig_length), - key_.get()); - if (!status_or_sig.ok()) { - return status_or_sig.status(); - } - return status_or_sig.value(); - } - - return std::string(reinterpret_cast<char*>(buffer.data()), sig_length); + return raw_signer_->Sign( + absl::string_view(reinterpret_cast<char*>(digest), digest_size)); } } // namespace subtle
diff --git a/cc/subtle/ecdsa_sign_boringssl.h b/cc/subtle/ecdsa_sign_boringssl.h index c3323ca..a05d39e 100644 --- a/cc/subtle/ecdsa_sign_boringssl.h +++ b/cc/subtle/ecdsa_sign_boringssl.h
@@ -19,13 +19,13 @@ #include <memory> #include <string> +#include <utility> #include "absl/strings/string_view.h" -#include "openssl/ec.h" #include "openssl/evp.h" #include "tink/internal/fips_utils.h" -#include "tink/internal/ssl_unique_ptr.h" #include "tink/public_key_sign.h" +#include "tink/signature/internal/ecdsa_raw_sign_boringssl.h" #include "tink/subtle/common_enums.h" #include "tink/subtle/subtle_util_boringssl.h" #include "tink/util/statusor.h" @@ -49,12 +49,13 @@ crypto::tink::internal::FipsCompatibility::kRequiresBoringCrypto; private: - EcdsaSignBoringSsl(internal::SslUniquePtr<EC_KEY> key, const EVP_MD* hash, - EcdsaSignatureEncoding encoding); + explicit EcdsaSignBoringSsl( + const EVP_MD* hash, + std::unique_ptr<internal::EcdsaRawSignBoringSsl> raw_signer) + : hash_(hash), raw_signer_(std::move(raw_signer)) {} - internal::SslUniquePtr<EC_KEY> key_; const EVP_MD* hash_; // Owned by BoringSSL. - EcdsaSignatureEncoding encoding_; + std::unique_ptr<internal::EcdsaRawSignBoringSsl> raw_signer_; }; } // namespace subtle
diff --git a/cc/subtle/ecdsa_sign_boringssl_test.cc b/cc/subtle/ecdsa_sign_boringssl_test.cc index d2a236b..666b8db 100644 --- a/cc/subtle/ecdsa_sign_boringssl_test.cc +++ b/cc/subtle/ecdsa_sign_boringssl_test.cc
@@ -21,8 +21,8 @@ #include "gtest/gtest.h" #include "absl/status/status.h" -#include "tink/config/tink_fips.h" #include "tink/internal/ec_util.h" +#include "tink/internal/fips_utils.h" #include "tink/public_key_sign.h" #include "tink/public_key_verify.h" #include "tink/subtle/common_enums.h" @@ -43,7 +43,7 @@ class EcdsaSignBoringSslTest : public ::testing::Test {}; TEST_F(EcdsaSignBoringSslTest, testBasicSigning) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -84,7 +84,7 @@ } TEST_F(EcdsaSignBoringSslTest, testEncodingsMismatch) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -115,7 +115,7 @@ } TEST_F(EcdsaSignBoringSslTest, testSignatureSizesWithIEEE_P1364Encoding) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -149,7 +149,7 @@ } TEST_F(EcdsaSignBoringSslTest, testNewErrors) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -164,7 +164,7 @@ // FIPS-only mode test TEST_F(EcdsaSignBoringSslTest, TestFipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; }
diff --git a/cc/subtle/ecdsa_verify_boringssl.cc b/cc/subtle/ecdsa_verify_boringssl.cc index 962d6d4..b14e475 100644 --- a/cc/subtle/ecdsa_verify_boringssl.cc +++ b/cc/subtle/ecdsa_verify_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/ecdsa_verify_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/ecdsa_verify_boringssl_test.cc b/cc/subtle/ecdsa_verify_boringssl_test.cc index b82dadd..292218f 100644 --- a/cc/subtle/ecdsa_verify_boringssl_test.cc +++ b/cc/subtle/ecdsa_verify_boringssl_test.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/ecdsa_verify_boringssl.h" #include <iostream> +#include <memory> #include <string> #include <utility> @@ -24,7 +25,7 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "include/rapidjson/document.h" -#include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/public_key_sign.h" #include "tink/public_key_verify.h" #include "tink/subtle/common_enums.h" @@ -46,7 +47,7 @@ class EcdsaVerifyBoringSslTest : public ::testing::Test {}; TEST_F(EcdsaVerifyBoringSslTest, BasicSigning) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -88,7 +89,7 @@ } TEST_F(EcdsaVerifyBoringSslTest, EncodingsMismatch) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -124,7 +125,7 @@ } TEST_F(EcdsaVerifyBoringSslTest, NewErrors) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -221,7 +222,7 @@ } TEST_F(EcdsaVerifyBoringSslTest, WycheproofCurveP256) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -230,7 +231,7 @@ } TEST_F(EcdsaVerifyBoringSslTest, WycheproofCurveP384) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -239,7 +240,7 @@ } TEST_F(EcdsaVerifyBoringSslTest, WycheproofCurveP521) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -248,7 +249,7 @@ } TEST_F(EcdsaVerifyBoringSslTest, WycheproofWithIeeeP1363Encoding) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -258,7 +259,7 @@ // FIPS-only mode test TEST_F(EcdsaVerifyBoringSslTest, TestFipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; }
diff --git a/cc/subtle/ecies_hkdf_recipient_kem_boringssl.cc b/cc/subtle/ecies_hkdf_recipient_kem_boringssl.cc index 105672f..0415a99 100644 --- a/cc/subtle/ecies_hkdf_recipient_kem_boringssl.cc +++ b/cc/subtle/ecies_hkdf_recipient_kem_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/ecies_hkdf_recipient_kem_boringssl.h" +#include <memory> #include <utility> #include "absl/memory/memory.h"
diff --git a/cc/subtle/ecies_hkdf_recipient_kem_boringssl_test.cc b/cc/subtle/ecies_hkdf_recipient_kem_boringssl_test.cc index d957a5f..3733920 100644 --- a/cc/subtle/ecies_hkdf_recipient_kem_boringssl_test.cc +++ b/cc/subtle/ecies_hkdf_recipient_kem_boringssl_test.cc
@@ -18,6 +18,7 @@ #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/status/status.h"
diff --git a/cc/subtle/ecies_hkdf_sender_kem_boringssl.cc b/cc/subtle/ecies_hkdf_sender_kem_boringssl.cc index 86f505c..8815a06 100644 --- a/cc/subtle/ecies_hkdf_sender_kem_boringssl.cc +++ b/cc/subtle/ecies_hkdf_sender_kem_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/ecies_hkdf_sender_kem_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/ecies_hkdf_sender_kem_boringssl_test.cc b/cc/subtle/ecies_hkdf_sender_kem_boringssl_test.cc index 01df86f..0e9c815 100644 --- a/cc/subtle/ecies_hkdf_sender_kem_boringssl_test.cc +++ b/cc/subtle/ecies_hkdf_sender_kem_boringssl_test.cc
@@ -17,8 +17,10 @@ #include "tink/subtle/ecies_hkdf_sender_kem_boringssl.h" #include <iostream> +#include <ostream> #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/status/status.h"
diff --git a/cc/subtle/ed25519_sign_boringssl.cc b/cc/subtle/ed25519_sign_boringssl.cc index f8b32d4..2b715e2 100644 --- a/cc/subtle/ed25519_sign_boringssl.cc +++ b/cc/subtle/ed25519_sign_boringssl.cc
@@ -18,6 +18,7 @@ #include <algorithm> #include <iterator> +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/ed25519_sign_boringssl_test.cc b/cc/subtle/ed25519_sign_boringssl_test.cc index c432afc..16e34ee 100644 --- a/cc/subtle/ed25519_sign_boringssl_test.cc +++ b/cc/subtle/ed25519_sign_boringssl_test.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/ed25519_sign_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/ed25519_verify_boringssl.cc b/cc/subtle/ed25519_verify_boringssl.cc index 750c603..853bb22 100644 --- a/cc/subtle/ed25519_verify_boringssl.cc +++ b/cc/subtle/ed25519_verify_boringssl.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/ed25519_verify_boringssl.h" #include <cstring> +#include <memory> #include <utility> #include "absl/memory/memory.h"
diff --git a/cc/subtle/ed25519_verify_boringssl_test.cc b/cc/subtle/ed25519_verify_boringssl_test.cc index b3ed053..a086cc4 100644 --- a/cc/subtle/ed25519_verify_boringssl_test.cc +++ b/cc/subtle/ed25519_verify_boringssl_test.cc
@@ -16,9 +16,11 @@ #include "tink/subtle/ed25519_verify_boringssl.h" +#include <iostream> #include <memory> #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/status/status.h"
diff --git a/cc/subtle/encrypt_then_authenticate.cc b/cc/subtle/encrypt_then_authenticate.cc index 9d84dce..a499a77 100644 --- a/cc/subtle/encrypt_then_authenticate.cc +++ b/cc/subtle/encrypt_then_authenticate.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/encrypt_then_authenticate.h" #include <cstdint> +#include <memory> #include <string> #include <utility> #include <vector> @@ -36,7 +37,7 @@ namespace tink { namespace subtle { -static const std::string longToBigEndianStr(uint64_t value) { +static std::string longToBigEndianStr(uint64_t value) { uint8_t bytes[8]; for (int i = sizeof(bytes) - 1; i >= 0; i--) { bytes[i] = value & 0xff; @@ -72,23 +73,20 @@ "associated data too long"); } - auto ct = ind_cpa_cipher_->Encrypt(plaintext); - if (!ct.ok()) { - return ct.status(); + auto ciphertext = ind_cpa_cipher_->Encrypt(plaintext); + if (!ciphertext.ok()) { + return ciphertext.status(); } - std::string ciphertext(ct.value()); - std::string toAuthData = - absl::StrCat(associated_data, ciphertext, - longToBigEndianStr(associated_data_size_in_bits)); - - auto tag = mac_->ComputeMac(toAuthData); + auto tag = mac_->ComputeMac( + absl::StrCat(associated_data, *ciphertext, + longToBigEndianStr(associated_data_size_in_bits))); if (!tag.ok()) { return tag.status(); } - if (tag.value().size() != tag_size_) { + if (tag->size() != tag_size_) { return util::Status(absl::StatusCode::kInternal, "invalid tag size"); } - return ciphertext.append(tag.value()); + return ciphertext->append(tag.value()); } util::StatusOr<std::string> EncryptThenAuthenticate::Decrypt( @@ -112,11 +110,10 @@ auto payload = ciphertext.substr(0, ciphertext.size() - tag_size_); auto tag = ciphertext.substr(ciphertext.size() - tag_size_, tag_size_); - std::string toAuthData = - absl::StrCat(associated_data, payload, - longToBigEndianStr(associated_data_size_in_bits)); - auto verified = mac_->VerifyMac(tag, toAuthData); + auto verified = mac_->VerifyMac( + tag, absl::StrCat(associated_data, payload, + longToBigEndianStr(associated_data_size_in_bits))); if (!verified.ok()) { return verified; }
diff --git a/cc/subtle/encrypt_then_authenticate_test.cc b/cc/subtle/encrypt_then_authenticate_test.cc index d0dfc2a..fcaa3d3 100644 --- a/cc/subtle/encrypt_then_authenticate_test.cc +++ b/cc/subtle/encrypt_then_authenticate_test.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/encrypt_then_authenticate.h" +#include <memory> #include <string> #include <utility> #include <vector> @@ -274,9 +275,6 @@ // test ensures that the overflow issue and the auth bypass vulnerability are // fixed. TEST(EncryptThenAuthenticateTest, testAuthBypassShouldNotWork) { -// Disable this test when running with ASYLO, because it allocates more memory -// than ASYLO can handle. -#ifndef __ASYLO__ int encryption_key_size = 16; int iv_size = 12; int mac_key_size = 16; @@ -289,7 +287,11 @@ const std::string message = "Some data to encrypt."; // ...with a long associated_data whose size in bits converted to an unsigned // 32-bit integer is 0. - const std::string associated_data = std::string(1 << 29, 'a'); + std::string associated_data; + constexpr size_t kAssociatedDataSize = 1 << 29; + constexpr size_t kCiphertextSpace = 1000; + associated_data.reserve(kAssociatedDataSize + kCiphertextSpace); + associated_data.resize(kAssociatedDataSize, 'a'); auto encrypted = cipher->Encrypt(message, associated_data); EXPECT_TRUE(encrypted.ok()) << encrypted.status(); auto ct = encrypted.value(); @@ -299,10 +301,9 @@ // Test that the 2^29-byte associated_data is NOT considered equal to an empty // associated_data. That is, test that a valid tag for (ciphertext, // associated_data) is INVALID for (associated_data + ciphertext, ""). - ct = associated_data + ct; + ct = std::move(associated_data) + ct; decrypted = cipher->Decrypt(ct, ""); EXPECT_FALSE(decrypted.ok()); -#endif // __ASYLO__ } } // namespace
diff --git a/cc/subtle/hkdf.cc b/cc/subtle/hkdf.cc index ef96eb7..b62da4a 100644 --- a/cc/subtle/hkdf.cc +++ b/cc/subtle/hkdf.cc
@@ -16,20 +16,21 @@ #include "tink/subtle/hkdf.h" +#include <cstdint> #include <string> #include "absl/algorithm/container.h" #include "absl/status/status.h" -#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "openssl/evp.h" -// BoringSSL and OpenSSL have incompatible ways to compute HDKF: BoringSSL +// BoringSSL and OpenSSL have incompatible ways to compute HKDF: BoringSSL // provides a one-shot API HKDF, while OpenSSL doesn't make that API public, but // instead provides this functionality over the EVP interface, which in turn // doesn't provide means to compute HKDF in BoringSSL. As a consequence, we need // to selectively include the correct header and use different implementations. #ifdef OPENSSL_IS_BORINGSSL +#include "openssl/base.h" #include "openssl/hkdf.h" #else #include "openssl/kdf.h" @@ -52,11 +53,12 @@ util::Status SslHkdf(const EVP_MD *evp_md, absl::string_view ikm, absl::string_view salt, absl::string_view info, absl::Span<uint8_t> out_key) { + const uint8_t *ikm_ptr = reinterpret_cast<const uint8_t *>(ikm.data()); + const uint8_t *salt_ptr = reinterpret_cast<const uint8_t *>(salt.data()); + const uint8_t *info_ptr = reinterpret_cast<const uint8_t *>(info.data()); #ifdef OPENSSL_IS_BORINGSSL - if (HKDF(out_key.data(), out_key.size(), evp_md, - reinterpret_cast<const uint8_t *>(ikm.data()), ikm.size(), - reinterpret_cast<const uint8_t *>(salt.data()), salt.size(), - reinterpret_cast<const uint8_t *>(info.data()), info.size()) != 1) { + if (HKDF(out_key.data(), out_key.size(), evp_md, ikm_ptr, ikm.size(), + salt_ptr, salt.size(), info_ptr, info.size()) != 1) { return util::Status(absl::StatusCode::kInternal, "HKDF failed"); } return util::OkStatus(); @@ -65,11 +67,9 @@ EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, /*e=*/nullptr)); if (pctx == nullptr || EVP_PKEY_derive_init(pctx.get()) <= 0 || EVP_PKEY_CTX_set_hkdf_md(pctx.get(), evp_md) <= 0 || - EVP_PKEY_CTX_set1_hkdf_salt(pctx.get(), salt.data(), salt.size()) <= 0 || - EVP_PKEY_CTX_set1_hkdf_key(pctx.get(), - reinterpret_cast<const uint8_t *>(ikm.data()), - ikm.size()) <= 0 || - EVP_PKEY_CTX_add1_hkdf_info(pctx.get(), info.data(), info.size()) <= 0) { + EVP_PKEY_CTX_set1_hkdf_salt(pctx.get(), salt_ptr, salt.size()) <= 0 || + EVP_PKEY_CTX_set1_hkdf_key(pctx.get(), ikm_ptr, ikm.size()) <= 0 || + EVP_PKEY_CTX_add1_hkdf_info(pctx.get(), info_ptr, info.size()) <= 0) { return util::Status(absl::StatusCode::kInternal, "EVP_PKEY_CTX setup failed"); }
diff --git a/cc/subtle/hkdf_test.cc b/cc/subtle/hkdf_test.cc index 77d272c..1cf665c 100644 --- a/cc/subtle/hkdf_test.cc +++ b/cc/subtle/hkdf_test.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/hkdf.h" #include <string> +#include <vector> #include "gtest/gtest.h" #include "absl/strings/escaping.h"
diff --git a/cc/subtle/hmac_boringssl.cc b/cc/subtle/hmac_boringssl.cc index 9cd609b..da2bed3 100644 --- a/cc/subtle/hmac_boringssl.cc +++ b/cc/subtle/hmac_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/hmac_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/hmac_boringssl_test.cc b/cc/subtle/hmac_boringssl_test.cc index 79a45d4..5e4b905 100644 --- a/cc/subtle/hmac_boringssl_test.cc +++ b/cc/subtle/hmac_boringssl_test.cc
@@ -22,7 +22,7 @@ #include "gtest/gtest.h" #include "absl/status/status.h" #include "absl/strings/escaping.h" -#include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/mac.h" #include "tink/subtle/common_enums.h" #include "tink/util/secret_data.h" @@ -57,7 +57,7 @@ }; TEST_F(HmacBoringSslTest, testBasic) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -71,29 +71,29 @@ { // Test with some example data. std::string data = "Some data to test."; auto res = hmac->ComputeMac(data); - EXPECT_TRUE(res.ok()) << res.status().ToString(); + EXPECT_TRUE(res.ok()) << res.status(); std::string tag = res.value(); EXPECT_EQ(tag_size, tag.size()); EXPECT_EQ(tag, absl::HexStringToBytes("9ccdca5b7fffb690df396e4ac49b9cd4")); auto status = hmac->VerifyMac(tag, data); - EXPECT_TRUE(status.ok()) << "tag:" << absl::BytesToHexString(tag) - << " status:" << status.ToString(); + EXPECT_TRUE(status.ok()) + << "tag:" << absl::BytesToHexString(tag) << " status:" << status; } { // Test with empty example data. absl::string_view data; auto res = hmac->ComputeMac(data); - EXPECT_TRUE(res.ok()) << res.status().ToString(); + EXPECT_TRUE(res.ok()) << res.status(); std::string tag = res.value(); EXPECT_EQ(tag_size, tag.size()); EXPECT_EQ(tag, absl::HexStringToBytes("5433122f77bcf8a4d9b874b4149823ef")); auto status = hmac->VerifyMac(tag, data); - EXPECT_TRUE(status.ok()) << "tag:" << absl::BytesToHexString(tag) - << " status:" << status.ToString(); + EXPECT_TRUE(status.ok()) + << "tag:" << absl::BytesToHexString(tag) << " status:" << status; } } TEST_F(HmacBoringSslTest, testModification) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -106,7 +106,7 @@ std::string data = "Some data to test"; std::string tag = hmac->ComputeMac(data).value(); auto status = hmac->VerifyMac(tag, data); - EXPECT_TRUE(status.ok()) << status.ToString(); + EXPECT_TRUE(status.ok()) << status; size_t bits = tag.size() * 8; for (size_t i = 0; i < bits; i++) { std::string modified_tag = tag; @@ -118,7 +118,7 @@ } TEST_F(HmacBoringSslTest, testTruncation) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -131,7 +131,7 @@ std::string data = "Some data to test"; std::string tag = hmac->ComputeMac(data).value(); auto status = hmac->VerifyMac(tag, data); - EXPECT_TRUE(status.ok()) << status.ToString(); + EXPECT_TRUE(status.ok()) << status; for (size_t i = 0; i < tag.size(); i++) { std::string modified_tag(tag, 0, i); EXPECT_FALSE(hmac->VerifyMac(modified_tag, data).ok()) @@ -141,7 +141,7 @@ } TEST_F(HmacBoringSslTest, testInvalidKeySizes) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is unavailable."; } @@ -160,7 +160,7 @@ } TEST_F(HmacBoringSslTest, TestFipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; }
diff --git a/cc/subtle/ind_cpa_cipher.h b/cc/subtle/ind_cpa_cipher.h index 6a6fc77..373f6b8 100644 --- a/cc/subtle/ind_cpa_cipher.h +++ b/cc/subtle/ind_cpa_cipher.h
@@ -42,7 +42,7 @@ virtual crypto::tink::util::StatusOr<std::string> Decrypt( absl::string_view ciphertext) const = 0; - virtual ~IndCpaCipher() {} + virtual ~IndCpaCipher() = default; }; } // namespace subtle
diff --git a/cc/subtle/mac/stateful_mac.h b/cc/subtle/mac/stateful_mac.h index e150a3a..d0a9b4e 100644 --- a/cc/subtle/mac/stateful_mac.h +++ b/cc/subtle/mac/stateful_mac.h
@@ -27,6 +27,7 @@ #ifndef TINK_SUBTLE_MAC_STATEFUL_MAC_H_ #define TINK_SUBTLE_MAC_STATEFUL_MAC_H_ +#include <memory> #include <string> #include "absl/strings/string_view.h" @@ -39,8 +40,8 @@ class StatefulMac { public: - StatefulMac() {} - virtual ~StatefulMac() {} + StatefulMac() = default; + virtual ~StatefulMac() = default; virtual util::Status Update(absl::string_view data) = 0; virtual util::StatusOr<std::string> Finalize() = 0; @@ -48,7 +49,7 @@ class StatefulMacFactory { public: - virtual ~StatefulMacFactory() {} + virtual ~StatefulMacFactory() = default; virtual util::StatusOr<std::unique_ptr<StatefulMac>> Create() const = 0; };
diff --git a/cc/subtle/nonce_based_streaming_aead.cc b/cc/subtle/nonce_based_streaming_aead.cc index 15e9768..4a346d3 100644 --- a/cc/subtle/nonce_based_streaming_aead.cc +++ b/cc/subtle/nonce_based_streaming_aead.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/nonce_based_streaming_aead.h" +#include <memory> #include <utility> #include "absl/strings/string_view.h"
diff --git a/cc/subtle/pem_parser_boringssl.cc b/cc/subtle/pem_parser_boringssl.cc index bebc04e..202e7c1 100644 --- a/cc/subtle/pem_parser_boringssl.cc +++ b/cc/subtle/pem_parser_boringssl.cc
@@ -358,7 +358,7 @@ "PEM Public Key parsing failed"); } // No need to free bssl_ecdsa_key after use. - EC_KEY* bssl_ecdsa_key = EVP_PKEY_get0_EC_KEY(evp_ecdsa_key.get()); + const EC_KEY* bssl_ecdsa_key = EVP_PKEY_get0_EC_KEY(evp_ecdsa_key.get()); auto is_valid = VerifyEcdsaKey(bssl_ecdsa_key); if (!is_valid.ok()) { return is_valid; @@ -413,7 +413,7 @@ } // No need to free bssl_ecdsa_key after use. - EC_KEY* bssl_ecdsa_key = EVP_PKEY_get0_EC_KEY(evp_ecdsa_key.get()); + const EC_KEY* bssl_ecdsa_key = EVP_PKEY_get0_EC_KEY(evp_ecdsa_key.get()); util::Status verify_result = VerifyEcdsaKey(bssl_ecdsa_key); if (!verify_result.ok()) { return verify_result;
diff --git a/cc/subtle/prf/hkdf_streaming_prf.cc b/cc/subtle/prf/hkdf_streaming_prf.cc index 63410f7..e39e745 100644 --- a/cc/subtle/prf/hkdf_streaming_prf.cc +++ b/cc/subtle/prf/hkdf_streaming_prf.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/prf/hkdf_streaming_prf.h" #include <algorithm> +#include <memory> #include <string> #include <utility> @@ -90,8 +91,8 @@ } ti_.resize(digest_size); - // BoringSSL's `HDKF_extract` function is implemented as an HMAC [1]. We - // replace calls to `HDKF_extract` with a direct call to `HMAC` to make this + // BoringSSL's `HKDF_extract` function is implemented as an HMAC [1]. We + // replace calls to `HKDF_extract` with a direct call to `HMAC` to make this // compatible to OpenSSL, which doesn't expose `HKDF*` functions. // // [1] https://github.com/google/boringssl/blob/master/crypto/hkdf/hkdf.c#L42
diff --git a/cc/subtle/prf/hkdf_streaming_prf_test.cc b/cc/subtle/prf/hkdf_streaming_prf_test.cc index 867bd20..fb2e45f 100644 --- a/cc/subtle/prf/hkdf_streaming_prf_test.cc +++ b/cc/subtle/prf/hkdf_streaming_prf_test.cc
@@ -16,6 +16,9 @@ #include "tink/subtle/prf/hkdf_streaming_prf.h" +#include <algorithm> +#include <iterator> +#include <memory> #include <string> #include <utility> @@ -23,6 +26,7 @@ #include "gtest/gtest.h" #include "absl/status/status.h" #include "tink/config/tink_fips.h" +#include "tink/subtle/common_enums.h" #include "tink/subtle/hkdf.h" #include "tink/subtle/random.h" #include "tink/util/input_stream_util.h" @@ -65,6 +69,43 @@ EXPECT_THAT(result_or.value(), SizeIs(10)); } +TEST(HkdfStreamingPrf, EmptySalt) { + if (IsFipsModeEnabled()) { + GTEST_SKIP() << "Not supported in FIPS-only mode"; + } + + crypto::tink::subtle::HashType hash_type = SHA512; + const int hash_length = 64; + util::SecretData secret = util::SecretDataFromStringView("key0123456"); + absl::string_view input = "input"; + int num_bytes = 10; + + std::string prf_empty_salt; + std::string prf_zeroed_salt; + { + auto streaming_prf = HkdfStreamingPrf::New(hash_type, secret, ""); + ASSERT_THAT(streaming_prf, IsOk()); + std::unique_ptr<InputStream> stream = (*streaming_prf)->ComputePrf(input); + auto result = ReadBytesFromStream(num_bytes, stream.get()); + ASSERT_THAT(result, IsOk()); + prf_empty_salt = *result; + } + { + uint8_t zeroedSalt[hash_length]; + std::fill(std::begin(zeroedSalt), std::end(zeroedSalt), 0); + auto streaming_prf = HkdfStreamingPrf::New(hash_type, secret, + std::string((char*)zeroedSalt)); + ASSERT_THAT(streaming_prf, IsOk()); + std::unique_ptr<InputStream> stream = (*streaming_prf)->ComputePrf(input); + auto result = ReadBytesFromStream(num_bytes, stream.get()); + ASSERT_THAT(result, IsOk()); + prf_zeroed_salt = *result; + } + EXPECT_THAT(prf_empty_salt, SizeIs(num_bytes)); + EXPECT_THAT(prf_zeroed_salt, SizeIs(num_bytes)); + ASSERT_EQ(prf_empty_salt, prf_zeroed_salt); +} + TEST(HkdfStreamingPrf, DifferentInputsGiveDifferentvalues) { if (IsFipsModeEnabled()) { GTEST_SKIP() << "Not supported in FIPS-only mode"; @@ -528,12 +569,11 @@ std::string salt = Random::GetRandomBytes(234); std::string info = Random::GetRandomBytes(345); - auto streaming_result_or = ComputeWithHkdfStreamingPrf( - hash, ikm, salt, info, 456); + auto streaming_result_or = + ComputeWithHkdfStreamingPrf(hash, ikm, salt, info, 456); ASSERT_THAT(streaming_result_or, IsOk()); - auto compute_hkdf_result_or = Hkdf::ComputeHkdf( - hash, ikm, salt, info, 456); + auto compute_hkdf_result_or = Hkdf::ComputeHkdf(hash, ikm, salt, info, 456); ASSERT_THAT(compute_hkdf_result_or, IsOk()); util::SecretData compute_hkdf_result =
diff --git a/cc/subtle/prf/streaming_prf.h b/cc/subtle/prf/streaming_prf.h index 7ba055d..76eeff8 100644 --- a/cc/subtle/prf/streaming_prf.h +++ b/cc/subtle/prf/streaming_prf.h
@@ -38,7 +38,7 @@ virtual std::unique_ptr<InputStream> ComputePrf( absl::string_view input) const = 0; - virtual ~StreamingPrf() {} + virtual ~StreamingPrf() = default; }; } // namespace tink
diff --git a/cc/subtle/prf/streaming_prf_wrapper.cc b/cc/subtle/prf/streaming_prf_wrapper.cc index 85e4a93..fafc370 100644 --- a/cc/subtle/prf/streaming_prf_wrapper.cc +++ b/cc/subtle/prf/streaming_prf_wrapper.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/prf/streaming_prf_wrapper.h" +#include <memory> #include <utility> #include "absl/status/status.h" @@ -36,7 +37,7 @@ return streaming_prf_set_->get_primary()->get_primitive().ComputePrf(input); } - ~StreamingPrfSetWrapper() override {} + ~StreamingPrfSetWrapper() override = default; private: std::unique_ptr<PrimitiveSet<StreamingPrf>> streaming_prf_set_;
diff --git a/cc/subtle/prf/streaming_prf_wrapper.h b/cc/subtle/prf/streaming_prf_wrapper.h index 5d8f0c8..82d5dc4 100644 --- a/cc/subtle/prf/streaming_prf_wrapper.h +++ b/cc/subtle/prf/streaming_prf_wrapper.h
@@ -17,6 +17,8 @@ #ifndef TINK_SUBTLE_PRF_STREAMING_PRF_WRAPPER_H_ #define TINK_SUBTLE_PRF_STREAMING_PRF_WRAPPER_H_ +#include <memory> + #include "tink/primitive_set.h" #include "tink/primitive_wrapper.h" #include "tink/subtle/prf/streaming_prf.h"
diff --git a/cc/subtle/prf/streaming_prf_wrapper_test.cc b/cc/subtle/prf/streaming_prf_wrapper_test.cc index fa8a90c..eb5ee6c 100644 --- a/cc/subtle/prf/streaming_prf_wrapper_test.cc +++ b/cc/subtle/prf/streaming_prf_wrapper_test.cc
@@ -16,6 +16,8 @@ #include "tink/subtle/prf/streaming_prf_wrapper.h" +#include <memory> +#include <sstream> #include <string> #include <utility>
diff --git a/cc/subtle/rsa_ssa_pkcs1_sign_boringssl.cc b/cc/subtle/rsa_ssa_pkcs1_sign_boringssl.cc index 4cbc58f..ec1d9bf 100644 --- a/cc/subtle/rsa_ssa_pkcs1_sign_boringssl.cc +++ b/cc/subtle/rsa_ssa_pkcs1_sign_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/rsa_ssa_pkcs1_sign_boringssl.h" +#include <memory> #include <string> #include <utility> #include <vector>
diff --git a/cc/subtle/rsa_ssa_pkcs1_sign_boringssl_test.cc b/cc/subtle/rsa_ssa_pkcs1_sign_boringssl_test.cc index dab0d7d..e1ae90b 100644 --- a/cc/subtle/rsa_ssa_pkcs1_sign_boringssl_test.cc +++ b/cc/subtle/rsa_ssa_pkcs1_sign_boringssl_test.cc
@@ -25,7 +25,7 @@ #include "openssl/bn.h" #include "openssl/crypto.h" #include "openssl/rsa.h" -#include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/internal/rsa_util.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/subtle/rsa_ssa_pkcs1_verify_boringssl.h" @@ -58,7 +58,7 @@ }; TEST_F(RsaPkcs1SignBoringsslTest, EncodesPkcs1) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -78,7 +78,7 @@ } TEST_F(RsaPkcs1SignBoringsslTest, EncodesPkcs1WithSeparateHashes) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -98,7 +98,7 @@ } TEST_F(RsaPkcs1SignBoringsslTest, RejectsUnsafeHash) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -108,7 +108,7 @@ } TEST_F(RsaPkcs1SignBoringsslTest, RejectsInvalidCrtParams) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -143,7 +143,7 @@ // FIPS-only mode test TEST_F(RsaPkcs1SignBoringsslTest, TestFipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; } @@ -154,7 +154,7 @@ } TEST_F(RsaPkcs1SignBoringsslTest, TestRestrictedFipsModuli) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto."; } @@ -171,7 +171,7 @@ } TEST_F(RsaPkcs1SignBoringsslTest, TestAllowedFipsModuli) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto."; }
diff --git a/cc/subtle/rsa_ssa_pkcs1_verify_boringssl.cc b/cc/subtle/rsa_ssa_pkcs1_verify_boringssl.cc index 7d8ee33..8164591 100644 --- a/cc/subtle/rsa_ssa_pkcs1_verify_boringssl.cc +++ b/cc/subtle/rsa_ssa_pkcs1_verify_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/rsa_ssa_pkcs1_verify_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/rsa_ssa_pkcs1_verify_boringssl_test.cc b/cc/subtle/rsa_ssa_pkcs1_verify_boringssl_test.cc index b4d8d82..a5e560a 100644 --- a/cc/subtle/rsa_ssa_pkcs1_verify_boringssl_test.cc +++ b/cc/subtle/rsa_ssa_pkcs1_verify_boringssl_test.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/rsa_ssa_pkcs1_verify_boringssl.h" #include <iostream> +#include <memory> #include <string> #include <utility> @@ -26,8 +27,8 @@ #include "absl/strings/str_cat.h" #include "openssl/bn.h" #include "include/rapidjson/document.h" -#include "tink/config/tink_fips.h" #include "tink/internal/err_util.h" +#include "tink/internal/fips_utils.h" #include "tink/internal/rsa_util.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/public_key_sign.h" @@ -86,7 +87,7 @@ HashType::SHA256}; TEST_F(RsaSsaPkcs1VerifyBoringSslTest, BasicVerify) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -102,7 +103,7 @@ } TEST_F(RsaSsaPkcs1VerifyBoringSslTest, NewErrors) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -132,7 +133,7 @@ } TEST_F(RsaSsaPkcs1VerifyBoringSslTest, Modification) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -257,7 +258,7 @@ } TEST_F(RsaSsaPkcs1VerifyBoringSslTest, WycheproofRsaPkcs12048SHA256) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } ASSERT_TRUE(TestSignatures("rsa_signature_2048_sha256_test.json", @@ -265,7 +266,7 @@ } TEST_F(RsaSsaPkcs1VerifyBoringSslTest, WycheproofRsaPkcs13072SHA256) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -274,7 +275,7 @@ } TEST_F(RsaSsaPkcs1VerifyBoringSslTest, WycheproofRsaPkcs13072SHA512) { - if (IsFipsModeEnabled() && !FIPS_mode()) { + if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable."; } @@ -283,7 +284,7 @@ } TEST_F(RsaSsaPkcs1VerifyBoringSslTest, WycheproofRsaPkcs14096SHA512) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } ASSERT_TRUE(TestSignatures("rsa_signature_4096_sha512_test.json", @@ -292,7 +293,7 @@ // FIPS-only mode test TEST_F(RsaSsaPkcs1VerifyBoringSslTest, TestFipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; } @@ -304,7 +305,7 @@ } TEST_F(RsaSsaPkcs1VerifyBoringSslTest, TestAllowedFipsModuli) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto."; } @@ -323,7 +324,7 @@ } TEST_F(RsaSsaPkcs1VerifyBoringSslTest, TestRestrictedFipsModuli) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto."; }
diff --git a/cc/subtle/rsa_ssa_pss_sign_boringssl.cc b/cc/subtle/rsa_ssa_pss_sign_boringssl.cc index cd873b2..43736a2 100644 --- a/cc/subtle/rsa_ssa_pss_sign_boringssl.cc +++ b/cc/subtle/rsa_ssa_pss_sign_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/rsa_ssa_pss_sign_boringssl.h" +#include <memory> #include <string> #include <utility> #include <vector>
diff --git a/cc/subtle/rsa_ssa_pss_sign_boringssl_test.cc b/cc/subtle/rsa_ssa_pss_sign_boringssl_test.cc index dddc52c..1b6feee 100644 --- a/cc/subtle/rsa_ssa_pss_sign_boringssl_test.cc +++ b/cc/subtle/rsa_ssa_pss_sign_boringssl_test.cc
@@ -22,7 +22,7 @@ #include "absl/strings/escaping.h" #include "openssl/bn.h" #include "openssl/rsa.h" -#include "tink/config/tink_fips.h" +#include "tink/internal/fips_utils.h" #include "tink/internal/rsa_util.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/subtle/rsa_ssa_pss_verify_boringssl.h" @@ -55,7 +55,7 @@ }; TEST_F(RsaPssSignBoringsslTest, EncodesPss) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -77,7 +77,7 @@ } TEST_F(RsaPssSignBoringsslTest, EncodesPssWithSeparateHashes) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -99,7 +99,7 @@ } TEST_F(RsaPssSignBoringsslTest, RejectsInvalidPaddingHash) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -111,7 +111,7 @@ } TEST_F(RsaPssSignBoringsslTest, RejectsUnsafePaddingHash) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -123,7 +123,7 @@ } TEST_F(RsaPssSignBoringsslTest, RejectsInvalidCrtParams) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } @@ -160,7 +160,7 @@ // FIPS-only mode test TEST_F(RsaPssSignBoringsslTest, TestFipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; } @@ -173,7 +173,7 @@ } TEST_F(RsaPssSignBoringsslTest, TestRestrictedFipsModuli) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto."; } internal::RsaPrivateKey private_key; @@ -191,7 +191,7 @@ } TEST_F(RsaPssSignBoringsslTest, TestAllowedFipsModuli) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto."; } internal::RsaPrivateKey private_key;
diff --git a/cc/subtle/rsa_ssa_pss_verify_boringssl.cc b/cc/subtle/rsa_ssa_pss_verify_boringssl.cc index f5e5b76..e1dbe02 100644 --- a/cc/subtle/rsa_ssa_pss_verify_boringssl.cc +++ b/cc/subtle/rsa_ssa_pss_verify_boringssl.cc
@@ -17,8 +17,10 @@ #include "tink/subtle/rsa_ssa_pss_verify_boringssl.h" #include <cstdint> +#include <memory> #include <string> #include <utility> +#include <vector> #include "absl/memory/memory.h" #include "absl/status/status.h"
diff --git a/cc/subtle/rsa_ssa_pss_verify_boringssl_test.cc b/cc/subtle/rsa_ssa_pss_verify_boringssl_test.cc index f654bda..20c8921 100644 --- a/cc/subtle/rsa_ssa_pss_verify_boringssl_test.cc +++ b/cc/subtle/rsa_ssa_pss_verify_boringssl_test.cc
@@ -19,6 +19,7 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/status/status.h" @@ -28,8 +29,8 @@ #include "absl/strings/string_view.h" #include "openssl/bn.h" #include "include/rapidjson/document.h" -#include "tink/config/tink_fips.h" #include "tink/internal/err_util.h" +#include "tink/internal/fips_utils.h" #include "tink/internal/rsa_util.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/public_key_sign.h" @@ -64,7 +65,7 @@ int salt_length; }; -const NistTestVector GetNistTestVector() { +NistTestVector GetNistTestVector() { NistTestVector test_vector = { absl::HexStringToBytes( "a47d04e7cacdba4ea26eca8a4c6e14563c2ce03b623b768c0d49868a57121301dbf7" @@ -98,7 +99,7 @@ } TEST(RsaSsaPssVerifyBoringSslTest, BasicVerify) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } const NistTestVector kNistTestVector = GetNistTestVector(); @@ -118,7 +119,7 @@ } TEST(RsaSsaPssVerifyBoringSslTest, NewErrors) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } const NistTestVector kNistTestVector = GetNistTestVector(); @@ -152,7 +153,7 @@ } TEST(RsaSsaPssVerifyBoringSslTest, Modification) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } const NistTestVector kNistTestVector = GetNistTestVector(); @@ -252,7 +253,7 @@ // Tests signature verification using a test vector. TEST_P(RsaSsaPssWycheproofTest, SignatureVerify) { - if (IsFipsModeEnabled()) { + if (internal::IsFipsModeEnabled()) { GTEST_SKIP() << "Test not run in FIPS-only mode"; } RsaSsaPssWycheproofTestVector params = GetParam(); @@ -304,7 +305,7 @@ // FIPS-only mode test TEST(RsaSsaPssVerifyBoringSslTest, TestFipsFailWithoutBoringCrypto) { - if (!IsFipsModeEnabled() || FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; } @@ -320,7 +321,7 @@ } TEST(RsaSsaPssVerifyBoringSslTest, TestAllowedFipsModuli) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto."; } @@ -341,7 +342,7 @@ } TEST(RsaSsaPssVerifyBoringSslTest, TestRestrictedFipsModuli) { - if (!IsFipsModeEnabled() || !FIPS_mode()) { + if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) { GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto."; }
diff --git a/cc/subtle/stateful_cmac_boringssl.cc b/cc/subtle/stateful_cmac_boringssl.cc index 951e126..09279c4 100644 --- a/cc/subtle/stateful_cmac_boringssl.cc +++ b/cc/subtle/stateful_cmac_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/stateful_cmac_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/stateful_cmac_boringssl_test.cc b/cc/subtle/stateful_cmac_boringssl_test.cc index f85d017..946676f 100644 --- a/cc/subtle/stateful_cmac_boringssl_test.cc +++ b/cc/subtle/stateful_cmac_boringssl_test.cc
@@ -19,6 +19,7 @@ #include <cstddef> #include <memory> #include <string> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h"
diff --git a/cc/subtle/stateful_hmac_boringssl.cc b/cc/subtle/stateful_hmac_boringssl.cc index 631b630..16c3e54 100644 --- a/cc/subtle/stateful_hmac_boringssl.cc +++ b/cc/subtle/stateful_hmac_boringssl.cc
@@ -16,6 +16,7 @@ #include "tink/subtle/stateful_hmac_boringssl.h" +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/stream_segment_decrypter.h b/cc/subtle/stream_segment_decrypter.h index 55425e3..6604125 100644 --- a/cc/subtle/stream_segment_decrypter.h +++ b/cc/subtle/stream_segment_decrypter.h
@@ -72,7 +72,7 @@ // segment_overhead = ciphertext_segment_size - get_plaintext_segment_size() virtual int get_ciphertext_offset() const = 0; - virtual ~StreamSegmentDecrypter() {} + virtual ~StreamSegmentDecrypter() = default; }; } // namespace subtle
diff --git a/cc/subtle/stream_segment_encrypter.h b/cc/subtle/stream_segment_encrypter.h index 93f6952..ff8aeba 100644 --- a/cc/subtle/stream_segment_encrypter.h +++ b/cc/subtle/stream_segment_encrypter.h
@@ -98,7 +98,7 @@ // segment_overhead = ciphertext_segment_size - get_plaintext_segment_size() virtual int get_ciphertext_offset() const = 0; - virtual ~StreamSegmentEncrypter() {} + virtual ~StreamSegmentEncrypter() = default; protected: // Increments the segment number.
diff --git a/cc/subtle/streaming_aead_decrypting_stream.cc b/cc/subtle/streaming_aead_decrypting_stream.cc index 01c26b8..c46293b 100644 --- a/cc/subtle/streaming_aead_decrypting_stream.cc +++ b/cc/subtle/streaming_aead_decrypting_stream.cc
@@ -18,7 +18,9 @@ #include <algorithm> #include <cstring> +#include <memory> #include <utility> +#include <vector> #include "absl/memory/memory.h" #include "absl/status/status.h"
diff --git a/cc/subtle/streaming_aead_decrypting_stream_test.cc b/cc/subtle/streaming_aead_decrypting_stream_test.cc index f8579f7..9569caa 100644 --- a/cc/subtle/streaming_aead_decrypting_stream_test.cc +++ b/cc/subtle/streaming_aead_decrypting_stream_test.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/streaming_aead_decrypting_stream.h" #include <algorithm> +#include <memory> #include <sstream> #include <string> #include <utility>
diff --git a/cc/subtle/streaming_aead_encrypting_stream.cc b/cc/subtle/streaming_aead_encrypting_stream.cc index 5fb4741..419bc06 100644 --- a/cc/subtle/streaming_aead_encrypting_stream.cc +++ b/cc/subtle/streaming_aead_encrypting_stream.cc
@@ -18,7 +18,9 @@ #include <algorithm> #include <cstring> +#include <memory> #include <utility> +#include <vector> #include "absl/memory/memory.h" #include "absl/status/status.h"
diff --git a/cc/subtle/streaming_aead_encrypting_stream_test.cc b/cc/subtle/streaming_aead_encrypting_stream_test.cc index aa2a5e8..cba162f 100644 --- a/cc/subtle/streaming_aead_encrypting_stream_test.cc +++ b/cc/subtle/streaming_aead_encrypting_stream_test.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/streaming_aead_encrypting_stream.h" #include <algorithm> +#include <memory> #include <sstream> #include <string> #include <utility>
diff --git a/cc/subtle/streaming_aead_test_util.cc b/cc/subtle/streaming_aead_test_util.cc index 9c21c00..bc4019c 100644 --- a/cc/subtle/streaming_aead_test_util.cc +++ b/cc/subtle/streaming_aead_test_util.cc
@@ -16,39 +16,30 @@ #include "tink/subtle/streaming_aead_test_util.h" #include <algorithm> +#include <cstring> +#include <memory> #include <sstream> #include <string> #include <utility> +#include "tink/internal/test_random_access_stream.h" #include "tink/random_access_stream.h" #include "tink/subtle/test_util.h" #include "tink/util/buffer.h" -#include "tink/util/file_random_access_stream.h" #include "tink/util/istream_input_stream.h" #include "tink/util/ostream_output_stream.h" #include "tink/util/status.h" -#include "tink/util/test_util.h" namespace crypto { namespace tink { -using ::crypto::tink::test::GetTestFileDescriptor; +using ::crypto::tink::internal::TestRandomAccessStream; using ::crypto::tink::util::IstreamInputStream; using ::crypto::tink::util::OstreamOutputStream; using ::crypto::tink::util::Status; namespace { -// Creates a RandomAccessStream with the specified contents. -std::unique_ptr<RandomAccessStream> GetRandomAccessStreamContaining( - absl::string_view contents) { - static int index = 1; - std::string filename = absl::StrCat("stream_data_file_", index, ".txt"); - index++; - int input_fd = GetTestFileDescriptor(filename, contents); - return {absl::make_unique<util::FileRandomAccessStream>(input_fd)}; -} - // Reads up to 'count' bytes from 'ras' starting at position 'pos' // and verifies that the read bytes are equal to the corresponding // subsequence in 'full_contents'. @@ -136,7 +127,8 @@ } // Prepare a RandomAccessStream with the ciphertext. - auto ct_ras = GetRandomAccessStreamContaining(std::string(ct_buf->str())); + auto ct_ras = + std::make_unique<TestRandomAccessStream>(std::string(ct_buf->str())); // Decrypt fragments of the ciphertext using the decrypter. auto dec_ras_result = decrypter->NewDecryptingRandomAccessStream(
diff --git a/cc/subtle/streaming_mac_impl.cc b/cc/subtle/streaming_mac_impl.cc index 029c7ec..f200e69 100644 --- a/cc/subtle/streaming_mac_impl.cc +++ b/cc/subtle/streaming_mac_impl.cc
@@ -17,6 +17,7 @@ #include "tink/subtle/streaming_mac_impl.h" #include <algorithm> +#include <memory> #include <string> #include <utility>
diff --git a/cc/subtle/streaming_mac_impl_test.cc b/cc/subtle/streaming_mac_impl_test.cc index 0672f12..d7b8f04 100644 --- a/cc/subtle/streaming_mac_impl_test.cc +++ b/cc/subtle/streaming_mac_impl_test.cc
@@ -16,8 +16,10 @@ #include "tink/subtle/streaming_mac_impl.h" +#include <memory> #include <string> #include <utility> +#include <vector> #include "gtest/gtest.h" #include "absl/status/status.h" @@ -40,8 +42,8 @@ class DummyStatefulMacFactory : public StatefulMacFactory { public: - DummyStatefulMacFactory() {} - ~DummyStatefulMacFactory() override {} + DummyStatefulMacFactory() = default; + ~DummyStatefulMacFactory() override = default; // Constructs a StatefulMac using the DummyStatefulMac, which creates // returns a MAC of the header concatenated with the plaintext.
diff --git a/cc/subtle/test_util.h b/cc/subtle/test_util.h index 962bec5..9403b1a 100644 --- a/cc/subtle/test_util.h +++ b/cc/subtle/test_util.h
@@ -138,7 +138,7 @@ return ct_offset_; } - ~DummyStreamSegmentEncrypter() override {} + ~DummyStreamSegmentEncrypter() override = default; int get_generated_output_size() { return generated_output_size_; @@ -227,7 +227,7 @@ return ct_offset_; } - ~DummyStreamSegmentDecrypter() override {} + ~DummyStreamSegmentDecrypter() override = default; int get_generated_output_size() { return generated_output_size_;
diff --git a/cc/subtle/wycheproof_util.cc b/cc/subtle/wycheproof_util.cc index a69a147..6289057 100644 --- a/cc/subtle/wycheproof_util.cc +++ b/cc/subtle/wycheproof_util.cc
@@ -19,6 +19,7 @@ #include <fstream> #include <iostream> #include <memory> +#include <ostream> #include <string> #include "absl/status/status.h" @@ -79,8 +80,7 @@ std::unique_ptr<rapidjson::Document> WycheproofUtil::ReadTestVectors( const std::string &filename) { std::string test_vectors_path = crypto::tink::internal::RunfilesPath( - absl::StrCat( - "external/wycheproof/testvectors/", filename)); + absl::StrCat("testvectors/", filename)); std::ifstream input_stream; input_stream.open(test_vectors_path); rapidjson::IStreamWrapper input(input_stream);
diff --git a/cc/testvectors/BUILD.bazel b/cc/testvectors/BUILD.bazel new file mode 100644 index 0000000..b602435 --- /dev/null +++ b/cc/testvectors/BUILD.bazel
@@ -0,0 +1,200 @@ +"""Defines a set of genrules to copy test vectors from Wycheproof. + +This is needed to assist the transition to using Bazel Modules, in that Bazel +Modules packages use a different folder naming for dependencies compared to +WORKSPACE-based packages. +""" + +package(default_visibility = ["//:__subpackages__"]) + +licenses(["notice"]) + +genrule( + name = "aes_cmac", + srcs = ["@wycheproof//testvectors:aes_cmac"], + outs = ["aes_cmac_test.json"], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "aes_gcm", + srcs = ["@wycheproof//testvectors:aes_gcm"], + outs = ["aes_gcm_test.json"], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "aes_gcm_siv", + srcs = ["@wycheproof//testvectors:aes_gcm_siv"], + outs = ["aes_gcm_siv_test.json"], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "aes_eax", + srcs = ["@wycheproof//testvectors:aes_eax"], + outs = ["aes_eax_test.json"], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "aes_siv_cmac", + srcs = ["@wycheproof//testvectors:aes_siv_cmac"], + outs = [ + "aead_aes_siv_cmac_test.json", + "aes_siv_cmac_test.json", + ], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "chacha20_poly1305", + srcs = ["@wycheproof//testvectors:chacha20_poly1305"], + outs = [ + "chacha20_poly1305_test.json", + "xchacha20_poly1305_test.json", + ], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "hmac", + srcs = ["@wycheproof//testvectors:hmac"], + outs = [ + "hmac_sha1_test.json", + "hmac_sha224_test.json", + "hmac_sha256_test.json", + "hmac_sha384_test.json", + "hmac_sha3_224_test.json", + "hmac_sha3_256_test.json", + "hmac_sha3_384_test.json", + "hmac_sha3_512_test.json", + "hmac_sha512_test.json", + ], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "rsa_pss", + srcs = ["@wycheproof//testvectors:rsa_pss"], + outs = [ + "rsa_pss_2048_sha1_mgf1_20_test.json", + "rsa_pss_2048_sha256_mgf1_0_test.json", + "rsa_pss_2048_sha256_mgf1_32_test.json", + "rsa_pss_3072_sha256_mgf1_32_test.json", + "rsa_pss_4096_sha256_mgf1_32_test.json", + "rsa_pss_4096_sha512_mgf1_32_test.json", + "rsa_pss_misc_test.json", + ], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "rsa_signature", + srcs = ["@wycheproof//testvectors:rsa_signature"], + outs = [ + # Signature verification + "rsa_signature_2048_sha224_test.json", + "rsa_signature_2048_sha256_test.json", + "rsa_signature_2048_sha512_test.json", + "rsa_signature_3072_sha256_test.json", + "rsa_signature_3072_sha384_test.json", + "rsa_signature_3072_sha512_test.json", + "rsa_signature_4096_sha384_test.json", + "rsa_signature_4096_sha512_test.json", + "rsa_signature_2048_sha3_224_test.json", + "rsa_signature_2048_sha3_256_test.json", + "rsa_signature_2048_sha3_384_test.json", + "rsa_signature_2048_sha3_512_test.json", + "rsa_signature_3072_sha3_256_test.json", + "rsa_signature_3072_sha3_384_test.json", + "rsa_signature_3072_sha3_512_test.json", + "rsa_signature_test.json", + # Signature generation + "rsa_sig_gen_misc_test.json", + ], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "ecdsa_webcrypto", + srcs = ["@wycheproof//testvectors:ecdsa_webcrypto"], + outs = ["ecdsa_webcrypto_test.json"], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "ecdsa", + srcs = ["@wycheproof//testvectors:ecdsa"], + outs = [ + "ecdsa_brainpoolP224r1_sha224_test.json", + "ecdsa_brainpoolP256r1_sha256_test.json", + "ecdsa_brainpoolP320r1_sha384_test.json", + "ecdsa_brainpoolP384r1_sha384_test.json", + "ecdsa_brainpoolP512r1_sha512_test.json", + "ecdsa_secp224r1_sha224_test.json", + "ecdsa_secp224r1_sha256_test.json", + "ecdsa_secp224r1_sha3_224_test.json", + "ecdsa_secp224r1_sha3_256_test.json", + "ecdsa_secp224r1_sha3_512_test.json", + "ecdsa_secp224r1_sha512_test.json", + "ecdsa_secp256k1_sha256_test.json", + "ecdsa_secp256k1_sha3_256_test.json", + "ecdsa_secp256k1_sha3_512_test.json", + "ecdsa_secp256k1_sha512_test.json", + "ecdsa_secp256r1_sha256_test.json", + "ecdsa_secp256r1_sha3_256_test.json", + "ecdsa_secp256r1_sha3_512_test.json", + "ecdsa_secp256r1_sha512_test.json", + "ecdsa_secp384r1_sha384_test.json", + "ecdsa_secp384r1_sha3_384_test.json", + "ecdsa_secp384r1_sha3_512_test.json", + "ecdsa_secp384r1_sha512_test.json", + "ecdsa_secp521r1_sha3_512_test.json", + "ecdsa_secp521r1_sha512_test.json", + "ecdsa_test.json", # deprecated: use the files above + ], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "eddsa", + srcs = ["@wycheproof//testvectors:eddsa"], + outs = [ + "ed448_test.json", + "eddsa_test.json", + ], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +) + +genrule( + name = "ecdh", + srcs = ["@wycheproof//testvectors:ecdh"], + outs = [ + "ecdh_brainpoolP224r1_test.json", + "ecdh_brainpoolP256r1_test.json", + "ecdh_brainpoolP320r1_test.json", + "ecdh_brainpoolP384r1_test.json", + "ecdh_brainpoolP512r1_test.json", + "ecdh_secp224r1_test.json", + "ecdh_secp256k1_test.json", + "ecdh_secp256r1_test.json", + "ecdh_secp384r1_test.json", + "ecdh_secp521r1_test.json", + "ecdh_test.json", # deprecated use the files above + ], + cmd = "cp $(SRCS) $(@D)/", + testonly = 1, +)
diff --git a/cc/tink_cc_deps.bzl b/cc/tink_cc_deps.bzl index 9a24734..d1de209 100644 --- a/cc/tink_cc_deps.bzl +++ b/cc/tink_cc_deps.bzl
@@ -7,14 +7,14 @@ # Basic rules we need to add to bazel. if not native.existing_rule("bazel_skylib"): - # Release from 2021-09-27. + # Release from 2022-09-01: https://github.com/bazelbuild/bazel-skylib/releases/tag/1.3.0 http_archive( name = "bazel_skylib", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", ], - sha256 = "c6966ec828da198c5d9adbaa94c05e3a1c7f21bd012a0b29ba8ddbccb2c93b0d", + sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", ) # ------------------------------------------------------------------------- @@ -27,51 +27,36 @@ # * @com_google_protobuf//:java_toolchain # This statement defines the @com_google_protobuf repo. if not native.existing_rule("com_google_protobuf"): - # Release from 2021-06-08. + # Release X.21.9 from 2022-10-26. http_archive( name = "com_google_protobuf", - strip_prefix = "protobuf-3.19.3", - urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.3.zip"], - sha256 = "6b6bf5cd8d0cca442745c4c3c9f527c83ad6ef35a405f64db5215889ac779b42", - ) - - # ------------------------------------------------------------------------- - # Remote Build Execution (RBE). - # ------------------------------------------------------------------------- - if not native.existing_rule("bazel_toolchains"): - # Latest bazel_toolchains package on 2021-10-13. - http_archive( - name = "bazel_toolchains", - sha256 = "179ec02f809e86abf56356d8898c8bd74069f1bd7c56044050c2cd3d79d0e024", - strip_prefix = "bazel-toolchains-4.1.0", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/4.1.0/bazel-toolchains-4.1.0.tar.gz", - "https://github.com/bazelbuild/bazel-toolchains/releases/download/4.1.0/bazel-toolchains-4.1.0.tar.gz", - ], + strip_prefix = "protobuf-21.9", + urls = ["https://github.com/protocolbuffers/protobuf/archive/refs/tags/v21.9.zip"], + sha256 = "5babb8571f1cceafe0c18e13ddb3be556e87e12ceea3463d6b0d0064e6cc1ac3", ) # ------------------------------------------------------------------------- # Abseil. # ------------------------------------------------------------------------- if not native.existing_rule("com_google_absl"): - # Commit from 2021-12-03. + # Release from 2023-05-04. http_archive( name = "com_google_absl", - strip_prefix = "abseil-cpp-9336be04a242237cd41a525bedfcf3be1bb55377", - url = "https://github.com/abseil/abseil-cpp/archive/9336be04a242237cd41a525bedfcf3be1bb55377.zip", - sha256 = "368be019fc8d69a566ac2cf7a75262d5ba8f6409e3ef3cdbcf0106bdeb32e91c", + strip_prefix = "abseil-cpp-20230125.3", + url = "https://github.com/abseil/abseil-cpp/archive/refs/tags/20230125.3.zip", + sha256 = "51d676b6846440210da48899e4df618a357e6e44ecde7106f1e44ea16ae8adc7", ) # ------------------------------------------------------------------------- # BoringSSL. # ------------------------------------------------------------------------- if not native.existing_rule("boringssl"): - # Commit from 2022-02-25. + # Commit from 2023-02-15. http_archive( name = "boringssl", - strip_prefix = "boringssl-88cdf7dd2dbce1ecb9057c183095103d83373abe", - url = "https://github.com/google/boringssl/archive/88cdf7dd2dbce1ecb9057c183095103d83373abe.zip", - sha256 = "24092815136f956069fcfa5172166ad4e025166ce6fe500420c9e3e3c4f3da38", + strip_prefix = "boringssl-5c22014ca513807ed03c657e8ede076164663979", + url = "https://github.com/google/boringssl/archive/5c22014ca513807ed03c657e8ede076164663979.zip", + sha256 = "863fc670c456f30923740c1639305132fdfb9d1b25ba385a67ae3862ef12a8af", ) # -------------------------------------------------------------------------
diff --git a/cc/util/BUILD.bazel b/cc/util/BUILD.bazel index f57b17d..f92575e 100644 --- a/cc/util/BUILD.bazel +++ b/cc/util/BUILD.bazel
@@ -28,8 +28,10 @@ name = "secret_data_internal", hdrs = ["secret_data_internal.h"], include_prefix = "tink/util", + visibility = ["//visibility:private"], deps = [ "@boringssl//:crypto", + "@com_google_absl//absl/base:config", "@com_google_absl//absl/base:core_headers", ], ) @@ -55,7 +57,7 @@ ":status", ":statusor", "@com_google_absl//absl/memory", - "@com_google_protobuf//:protobuf_lite", + "@com_google_protobuf//:protobuf", ], ) @@ -76,6 +78,7 @@ include_prefix = "tink/util", visibility = ["//visibility:public"], deps = [ + ":status", ":statusor", "//proto:common_cc_proto", "//proto:ecdsa_cc_proto", @@ -88,39 +91,15 @@ cc_library( name = "status", - srcs = ["status.cc"], hdrs = ["status.h"], - defines = select({ - "//config:absl_status_enabled": ["TINK_USE_ABSL_STATUS"], - "//conditions:default": [], - }), include_prefix = "tink/util", visibility = ["//visibility:public"], - deps = [ - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/status", - "@com_google_absl//absl/strings", - ], -) - -cc_test( - name = "status_test", - srcs = ["status_test.cc"], - deps = [ - ":status", - "@com_google_absl//absl/status", - "@com_google_googletest//:gtest_main", - ], + deps = ["@com_google_absl//absl/status"], ) cc_library( name = "statusor", - srcs = ["statusor.h"], hdrs = ["statusor.h"], - defines = select({ - "//config:absl_statusor_enabled": ["TINK_USE_ABSL_STATUSOR"], - "//conditions:default": [], - }), include_prefix = "tink/util", visibility = ["//visibility:public"], deps = [ @@ -129,20 +108,6 @@ ], ) -cc_test( - name = "statusor_test", - srcs = ["statusor_test.cc"], - deps = [ - ":status", - ":statusor", - ":test_matchers", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_googletest//:gtest_main", - ], -) - cc_library( name = "validation", srcs = ["validation.cc"], @@ -161,13 +126,16 @@ srcs = ["file_input_stream.cc"], hdrs = ["file_input_stream.h"], include_prefix = "tink/util", + target_compatible_with = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], + }), visibility = ["//visibility:public"], deps = [ ":errors", ":status", ":statusor", "//:input_stream", - "@com_google_absl//absl/memory", "@com_google_absl//absl/status", ], ) @@ -177,6 +145,10 @@ srcs = ["file_output_stream.cc"], hdrs = ["file_output_stream.h"], include_prefix = "tink/util", + target_compatible_with = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], + }), visibility = ["//visibility:public"], deps = [ ":errors", @@ -193,6 +165,10 @@ srcs = ["file_random_access_stream.cc"], hdrs = ["file_random_access_stream.h"], include_prefix = "tink/util", + target_compatible_with = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], + }), visibility = ["//visibility:public"], deps = [ ":buffer", @@ -312,7 +288,7 @@ name = "protobuf_helper", hdrs = ["protobuf_helper.h"], include_prefix = "tink/util", - deps = ["@com_google_protobuf//:protobuf_lite"], + deps = ["@com_google_protobuf//:protobuf"], ) cc_library( @@ -446,10 +422,19 @@ cc_test( name = "file_input_stream_test", srcs = ["file_input_stream_test.cc"], + target_compatible_with = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], + }), deps = [ ":file_input_stream", + ":status", + ":test_matchers", ":test_util", + "//internal:test_file_util", + "//subtle:random", "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -458,9 +443,15 @@ cc_test( name = "file_output_stream_test", srcs = ["file_output_stream_test.cc"], + target_compatible_with = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], + }), deps = [ ":file_output_stream", + ":test_matchers", ":test_util", + "//internal:test_file_util", "//subtle:random", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", @@ -471,11 +462,19 @@ cc_test( name = "file_random_access_stream_test", srcs = ["file_random_access_stream_test.cc"], + target_compatible_with = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], + }), deps = [ ":buffer", ":file_random_access_stream", + ":test_matchers", ":test_util", + "//internal:test_file_util", + "//subtle:random", "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -487,8 +486,11 @@ deps = [ ":istream_input_stream", ":test_util", + "//internal:test_file_util", "//subtle:random", "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -500,6 +502,7 @@ deps = [ ":ostream_output_stream", ":test_util", + "//internal:test_file_util", "//subtle:random", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", @@ -533,11 +536,19 @@ name = "test_util_test", srcs = ["test_util_test.cc"], deps = [ + ":buffer", + ":ostream_output_stream", + ":statusor", ":test_matchers", ":test_util", + "//:output_stream", + "//:random_access_stream", + "//internal:test_random_access_stream", "//proto:aes_gcm_cc_proto", "//proto:tink_cc_proto", "//subtle", + "//subtle:test_util", + "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], )
diff --git a/cc/util/BUILD.gn b/cc/util/BUILD.gn index 3e0cff4..5bedeca 100644 --- a/cc/util/BUILD.gn +++ b/cc/util/BUILD.gn
@@ -23,6 +23,7 @@ configs -= [ "//build/config:no_rtti" ] sources = [ "secret_data_internal.h" ] public_deps = [ + "//third_party/abseil-cpp/absl/base:config", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/boringssl:crypto", ] @@ -62,6 +63,7 @@ "enums.h", ] public_deps = [ + ":status", ":statusor", "//third_party/abseil-cpp/absl/status:status", "//third_party/abseil-cpp/absl/strings:strings", @@ -77,15 +79,8 @@ source_set("status") { configs += [ "//build/config:no_rtti" ] configs -= [ "//build/config:no_rtti" ] - sources = [ - "status.cc", - "status.h", - ] - public_deps = [ - "//third_party/abseil-cpp/absl/base:core_headers", - "//third_party/abseil-cpp/absl/status:status", - "//third_party/abseil-cpp/absl/strings:strings", - ] + sources = [ "status.h" ] + public_deps = [ "//third_party/abseil-cpp/absl/status:status" ] public_configs = [ "//third_party/tink:tink_config" ] } @@ -93,10 +88,7 @@ source_set("statusor") { configs += [ "//build/config:no_rtti" ] configs -= [ "//build/config:no_rtti" ] - sources = [ - "statusor.h", - "statusor.h", - ] + sources = [ "statusor.h" ] public_deps = [ ":status", "//third_party/abseil-cpp/absl/status:statusor",
diff --git a/cc/util/CMakeLists.txt b/cc/util/CMakeLists.txt index 0836633..c906230 100644 --- a/cc/util/CMakeLists.txt +++ b/cc/util/CMakeLists.txt
@@ -36,6 +36,7 @@ enums.cc enums.h DEPS + tink::util::status tink::util::statusor absl::status absl::strings @@ -48,12 +49,9 @@ tink_cc_library( NAME status SRCS - status.cc status.h DEPS - absl::core_headers absl::status - absl::strings PUBLIC ) @@ -61,7 +59,6 @@ NAME statusor SRCS statusor.h - statusor.h DEPS tink::util::status absl::statusor @@ -89,9 +86,10 @@ tink::util::errors tink::util::status tink::util::statusor - absl::memory absl::status tink::core::input_stream + TAGS + exclude_if_windows ) tink_cc_library( @@ -106,6 +104,8 @@ absl::memory absl::status tink::core::output_stream + TAGS + exclude_if_windows ) tink_cc_library( @@ -121,6 +121,8 @@ absl::memory absl::status tink::core::random_access_stream + TAGS + exclude_if_windows ) tink_cc_library( @@ -203,6 +205,7 @@ tink::proto::hmac_cc_proto tink::proto::tink_cc_proto tink::proto::xchacha20_poly1305_cc_proto + TESTONLY ) tink_cc_library( @@ -214,6 +217,7 @@ tink::util::statusor gmock absl::status + TESTONLY ) tink_cc_library( @@ -233,6 +237,7 @@ absl::memory tink::core::keyset_handle tink::proto::tink_cc_proto + TESTONLY ) tink_cc_library( @@ -303,10 +308,17 @@ file_input_stream_test.cc DEPS tink::util::file_input_stream + tink::util::status + tink::util::test_matchers tink::util::test_util gmock absl::memory + absl::status absl::strings + tink::internal::test_file_util + tink::subtle::random + TAGS + exclude_if_windows ) tink_cc_test( @@ -315,11 +327,15 @@ file_output_stream_test.cc DEPS tink::util::file_output_stream + tink::util::test_matchers tink::util::test_util gmock absl::memory absl::strings + tink::internal::test_file_util tink::subtle::random + TAGS + exclude_if_windows ) tink_cc_test( @@ -329,10 +345,16 @@ DEPS tink::util::buffer tink::util::file_random_access_stream + tink::util::test_matchers tink::util::test_util gmock absl::memory + absl::status absl::strings + tink::internal::test_file_util + tink::subtle::random + TAGS + exclude_if_windows ) tink_cc_test( @@ -344,7 +366,10 @@ tink::util::test_util gmock absl::memory + absl::status + absl::statusor absl::strings + tink::internal::test_file_util tink::subtle::random ) @@ -358,6 +383,7 @@ gmock absl::memory absl::strings + tink::internal::test_file_util tink::subtle::random ) @@ -366,10 +392,18 @@ SRCS test_util_test.cc DEPS + tink::util::buffer + tink::util::ostream_output_stream + tink::util::statusor tink::util::test_matchers tink::util::test_util gmock + absl::strings + tink::core::output_stream + tink::core::random_access_stream + tink::internal::test_random_access_stream tink::subtle::subtle + tink::subtle::test_util tink::proto::aes_gcm_cc_proto tink::proto::tink_cc_proto ) @@ -404,8 +438,8 @@ SRCS secret_data_internal.h DEPS - absl::strings - absl::base + absl::config + absl::core_headers crypto ) @@ -471,6 +505,7 @@ tink::core::kms_client tink::core::kms_clients tink::aead::aead_key_templates + TESTONLY ) tink_cc_test( @@ -489,27 +524,3 @@ tink::proto::kms_aead_cc_proto tink::proto::kms_envelope_cc_proto ) - -tink_cc_test( - NAME status_test - SRCS - status_test.cc - DEPS - tink::util::status - gmock - absl::status -) - -tink_cc_test( - NAME statusor_test - SRCS - statusor_test.cc - DEPS - tink::util::status - tink::util::statusor - tink::util::test_matchers - gmock - absl::memory - absl::status - absl::statusor -)
diff --git a/cc/util/buffer.cc b/cc/util/buffer.cc index f98adac..2fa67e5 100644 --- a/cc/util/buffer.cc +++ b/cc/util/buffer.cc
@@ -16,6 +16,8 @@ #include "tink/util/buffer.h" +#include <memory> + #include "absl/memory/memory.h" #include "absl/status/status.h" #include "tink/util/status.h" @@ -54,7 +56,7 @@ return OkStatus(); } - ~OwningBuffer() override {} + ~OwningBuffer() override = default; private: std::unique_ptr<char[]> owned_mem_block_; @@ -91,7 +93,7 @@ return OkStatus(); } - ~NonOwningBuffer() override {} + ~NonOwningBuffer() override = default; private: char* const mem_block_;
diff --git a/cc/util/buffer.h b/cc/util/buffer.h index f46de54..3eaa558 100644 --- a/cc/util/buffer.h +++ b/cc/util/buffer.h
@@ -17,6 +17,8 @@ #ifndef TINK_UTIL_BUFFER_H_ #define TINK_UTIL_BUFFER_H_ +#include <memory> + #include "absl/memory/memory.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -58,7 +60,7 @@ // Returns OK iff 0 <= new_size <= allocated_size(); virtual util::Status set_size(int new_size) = 0; - virtual ~Buffer() {} + virtual ~Buffer() = default; }; } // namespace util
diff --git a/cc/util/enums.cc b/cc/util/enums.cc index 201ca1b..5100162 100644 --- a/cc/util/enums.cc +++ b/cc/util/enums.cc
@@ -18,6 +18,7 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" +#include "tink/util/status.h" #include "proto/common.pb.h" #include "proto/ecdsa.pb.h" #include "proto/tink.pb.h"
diff --git a/cc/util/errors.h b/cc/util/errors.h index cbb0ebe..5086ce4 100644 --- a/cc/util/errors.h +++ b/cc/util/errors.h
@@ -23,17 +23,6 @@ namespace crypto { namespace tink { -#ifndef TINK_USE_ABSL_STATUS -// Constructs a Status with formatted error message. -template <typename... Args> -ABSL_DEPRECATED("Prefer using absl::StatusCode as a first argument.") -util::Status ToStatusF(util::error::Code code, - const absl::FormatSpec<Args...>& format, - const Args&... args) { - return util::Status(code, absl::StrFormat(format, args...)); -} -#endif - // Constructs a Status with formatted error message using absl::StatusCode. template <typename... Args> util::Status ToStatusF(absl::StatusCode code,
diff --git a/cc/util/errors_test.cc b/cc/util/errors_test.cc index 73e74c2..cec75e6 100644 --- a/cc/util/errors_test.cc +++ b/cc/util/errors_test.cc
@@ -24,49 +24,13 @@ namespace tink { namespace { -#ifndef TINK_USE_ABSL_STATUS -TEST(ErrorsTest, ToStatusFTest) { - const char* const msg1 = "test message 1"; - const char* const msg2 = "test message %s 2 %d"; - crypto::tink::util::Status status; - - status = util::Status(crypto::tink::util::error::OK, msg1); - EXPECT_TRUE(status.ok()); - // if status is OK, error message is ignored - EXPECT_EQ("", status.message()); - EXPECT_EQ(crypto::tink::util::error::OK, status.error_code()); - - const char* expected_msg2 = "test message asdf 2 42"; - status = ToStatusF(crypto::tink::util::error::UNKNOWN, msg2, "asdf", 42); - EXPECT_FALSE(status.ok()); - EXPECT_EQ(expected_msg2, status.message()); - EXPECT_EQ(crypto::tink::util::error::UNKNOWN, status.error_code()); -} - -TEST(ErrorsTest, ToAbslStatus) { - crypto::tink::util::Status tink_status(util::error::INVALID_ARGUMENT, - "error"); - ::absl::Status g3_status(tink_status); - EXPECT_FALSE(g3_status.ok()); - EXPECT_EQ(g3_status.message(), "error"); - - EXPECT_EQ(::absl::Status(crypto::tink::util::OkStatus()), ::absl::OkStatus()); -} -#endif - TEST(ErrorsTest, ToStatusFAbslStatusCodeTest) { const char* const msg = "test message %s 2 %d"; const char* expected_msg = "test message asdf 2 42"; - crypto::tink::util::Status status = - ToStatusF(absl::StatusCode::kUnknown, msg, "asdf", 42); + util::Status status = ToStatusF(absl::StatusCode::kUnknown, msg, "asdf", 42); EXPECT_FALSE(status.ok()); EXPECT_EQ(expected_msg, status.message()); EXPECT_EQ(absl::StatusCode::kUnknown, status.code()); - - #ifndef TINK_USE_ABSL_STATUS - EXPECT_EQ(expected_msg, status.error_message()); - EXPECT_EQ(crypto::tink::util::error::UNKNOWN, status.error_code()); - #endif } } // namespace
diff --git a/cc/util/fake_kms_client.cc b/cc/util/fake_kms_client.cc index cb5f9ed..1f7922f 100644 --- a/cc/util/fake_kms_client.cc +++ b/cc/util/fake_kms_client.cc
@@ -17,6 +17,8 @@ #include <fstream> #include <iostream> +#include <memory> +#include <ostream> #include <sstream> #include <string> #include <utility>
diff --git a/cc/util/fake_kms_client_test.cc b/cc/util/fake_kms_client_test.cc index b1039c7..4436915 100644 --- a/cc/util/fake_kms_client_test.cc +++ b/cc/util/fake_kms_client_test.cc
@@ -31,7 +31,6 @@ #include "proto/kms_aead.pb.h" #include "proto/kms_envelope.pb.h" -using ::crypto::tink::test::IsOk; using google::crypto::tink::KeyTemplate; using google::crypto::tink::KmsAeadKeyFormat; using google::crypto::tink::KmsEnvelopeAeadKeyFormat;
diff --git a/cc/util/file_input_stream.cc b/cc/util/file_input_stream.cc index 3903f20..695651a 100644 --- a/cc/util/file_input_stream.cc +++ b/cc/util/file_input_stream.cc
@@ -19,9 +19,7 @@ #include <unistd.h> #include <algorithm> -#include "absl/memory/memory.h" #include "absl/status/status.h" -#include "tink/input_stream.h" #include "tink/util/errors.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -29,9 +27,10 @@ namespace crypto { namespace tink { namespace util { - namespace { +constexpr int kDefaultBufferSize = 128 * 1024; + // Attempts to close file descriptor fd, while ignoring EINTR. // (code borrowed from ZeroCopy-streams) int close_ignoring_eintr(int fd) { @@ -42,7 +41,6 @@ return result; } - // Attempts to read 'count' bytes of data data from file descriptor fd // to 'buf' while ignoring EINTR. int read_ignoring_eintr(int fd, void *buf, size_t count) { @@ -55,29 +53,27 @@ } // anonymous namespace -FileInputStream::FileInputStream(int file_descriptor, int buffer_size) : - buffer_size_(buffer_size > 0 ? buffer_size : 128 * 1024) { // 128 KB - fd_ = file_descriptor; - count_in_buffer_ = 0; - count_backedup_ = 0; - position_ = 0; - buffer_ = absl::make_unique<uint8_t[]>(buffer_size_); - buffer_offset_ = 0; - status_ = util::OkStatus(); -} +FileInputStream::FileInputStream(int file_descriptor, int buffer_size) + : status_(util::OkStatus()), + fd_(file_descriptor), + buffer_(buffer_size > 0 ? buffer_size : kDefaultBufferSize) {} -crypto::tink::util::StatusOr<int> FileInputStream::Next(const void** data) { +util::StatusOr<int> FileInputStream::Next(const void** data) { + if (data == nullptr) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Data pointer must not be nullptr"); + } if (!status_.ok()) return status_; if (count_backedup_ > 0) { // Return the backed-up bytes. buffer_offset_ = buffer_offset_ + (count_in_buffer_ - count_backedup_); count_in_buffer_ = count_backedup_; count_backedup_ = 0; - *data = buffer_.get() + buffer_offset_; + *data = buffer_.data() + buffer_offset_; position_ = position_ + count_in_buffer_; return count_in_buffer_; } // Read new bytes to buffer_. - int read_result = read_ignoring_eintr(fd_, buffer_.get(), buffer_size_); + int read_result = read_ignoring_eintr(fd_, buffer_.data(), buffer_.size()); if (read_result <= 0) { // EOF or an I/O error. if (read_result == 0) { status_ = Status(absl::StatusCode::kOutOfRange, "EOF"); @@ -91,7 +87,7 @@ count_backedup_ = 0; count_in_buffer_ = read_result; position_ = position_ + count_in_buffer_; - *data = buffer_.get(); + *data = buffer_.data(); return count_in_buffer_; } @@ -102,13 +98,9 @@ position_ = position_ - actual_count; } -FileInputStream::~FileInputStream() { - close_ignoring_eintr(fd_); -} +FileInputStream::~FileInputStream() { close_ignoring_eintr(fd_); } -int64_t FileInputStream::Position() const { - return position_; -} +int64_t FileInputStream::Position() const { return position_; } } // namespace util } // namespace tink
diff --git a/cc/util/file_input_stream.h b/cc/util/file_input_stream.h index 5c816b0..f01c362 100644 --- a/cc/util/file_input_stream.h +++ b/cc/util/file_input_stream.h
@@ -17,7 +17,9 @@ #ifndef TINK_UTIL_FILE_INPUT_STREAM_H_ #define TINK_UTIL_FILE_INPUT_STREAM_H_ +#include <cstdint> #include <memory> +#include <vector> #include "tink/input_stream.h" #include "tink/util/status.h" @@ -28,11 +30,13 @@ namespace util { // An InputStream that reads from a file descriptor. +// +// NOTE: This class in not available when building on Windows. class FileInputStream : public crypto::tink::InputStream { public: // Constructs an InputStream that will read from the file specified - // via 'file_descriptor', using a buffer of the specified size, if any - // (if no legal 'buffer_size' is given, a reasonable default will be used). + // via `file_descriptor`, using a buffer of the specified size, if any + // (if no legal `buffer_size` is given, a reasonable default will be used). // Takes the ownership of the file, and will close it upon destruction. explicit FileInputStream(int file_descriptor, int buffer_size = -1); @@ -45,16 +49,20 @@ int64_t Position() const override; private: - util::Status status_; + // Status of the stream. + util::Status status_ = util::OkStatus(); int fd_; - std::unique_ptr<uint8_t[]> buffer_; - const int buffer_size_; - int64_t position_; // current position in the file (from the beginning) + std::vector<uint8_t> buffer_; + // Current position in the stream (from the beginning). + int64_t position_ = 0; // Counters that describe the state of the data in buffer_. - int count_in_buffer_; // # of bytes available in buffer_ - int count_backedup_; // # of bytes available in buffer_ that were backed up - int buffer_offset_; // offset at which the returned bytes start in buffer_ + // # of bytes available in buffer_. + int count_in_buffer_ = 0; + // # of bytes available in buffer_ that were backed up. + int count_backedup_ = 0; + // offset at which the returned bytes start in buffer_. + int buffer_offset_ = 0; }; } // namespace util
diff --git a/cc/util/file_input_stream_test.cc b/cc/util/file_input_stream_test.cc index 14ecc1a..2f7a172 100644 --- a/cc/util/file_input_stream_test.cc +++ b/cc/util/file_input_stream_test.cc
@@ -13,27 +13,56 @@ // limitations under the License. // /////////////////////////////////////////////////////////////////////////////// - #include "tink/util/file_input_stream.h" +#include <fcntl.h> + #include <algorithm> +#include <cstdint> +#include <cstring> +#include <iostream> +#include <ostream> #include <string> +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/memory/memory.h" +#include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "tink/internal/test_file_util.h" +#include "tink/subtle/random.h" +#include "tink/util/status.h" +#include "tink/util/test_matchers.h" #include "tink/util/test_util.h" namespace crypto { namespace tink { namespace { -// Reads the specified 'input_stream' until no more bytes can be read, -// and puts the read bytes into 'contents'. +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; + +constexpr int kDefaultTestStreamSize = 100 * 1024; // 100 KB. + +// Opens test file `filename` and returns a file descriptor to it. +util::StatusOr<int> OpenTestFileToRead(absl::string_view filename) { + std::string full_filename = absl::StrCat(test::TmpDir(), "/", filename); + int fd = open(full_filename.c_str(), O_RDONLY); + if (fd == -1) { + return util::Status(absl::StatusCode::kInternal, + absl::StrCat("Cannot open file ", full_filename, + " error: ", std::strerror(errno))); + } + return fd; +} + +// Reads the specified `input_stream` until no more bytes can be read, +// and puts the read bytes into `contents`. // Returns the status of the last input_stream->Next()-operation. -util::Status ReadTillEnd(util::FileInputStream* input_stream, - std::string* contents) { +util::Status ReadAll(util::FileInputStream* input_stream, + std::string* contents) { contents->clear(); const void* buffer; auto next_result = input_stream->Next(&buffer); @@ -44,128 +73,241 @@ return next_result.status(); } -class FileInputStreamTest : public ::testing::Test { -}; +using FileInputStreamTestDefaultBufferSize = testing::TestWithParam<int>; -TEST_F(FileInputStreamTest, testReadingStreams) { - for (auto stream_size : {0, 10, 100, 1000, 10000, 100000, 1000000}) { - SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size)); - std::string file_contents; - std::string filename = absl::StrCat(stream_size, "_reading_test.bin"); - int input_fd = - test::GetTestFileDescriptor(filename, stream_size, &file_contents); - EXPECT_EQ(stream_size, file_contents.size()); - auto input_stream = absl::make_unique<util::FileInputStream>(input_fd); - std::string stream_contents; - auto status = ReadTillEnd(input_stream.get(), &stream_contents); - EXPECT_EQ(absl::StatusCode::kOutOfRange, status.code()); - EXPECT_EQ("EOF", status.message()); - EXPECT_EQ(file_contents, stream_contents); - } -} - -TEST_F(FileInputStreamTest, testCustomBufferSizes) { - int stream_size = 100000; - for (auto buffer_size : {1, 10, 100, 1000, 10000}) { - SCOPED_TRACE(absl::StrCat("buffer_size = ", buffer_size)); - std::string file_contents; - std::string filename = absl::StrCat(buffer_size, "_buffer_size_test.bin"); - int input_fd = - test::GetTestFileDescriptor(filename, stream_size, &file_contents); - EXPECT_EQ(stream_size, file_contents.size()); - auto input_stream = - absl::make_unique<util::FileInputStream>(input_fd, buffer_size); - const void* buffer; - auto next_result = input_stream->Next(&buffer); - EXPECT_TRUE(next_result.ok()) << next_result.status(); - EXPECT_EQ(buffer_size, next_result.value()); - EXPECT_EQ(file_contents.substr(0, buffer_size), - std::string(static_cast<const char*>(buffer), buffer_size)); - } -} - -TEST_F(FileInputStreamTest, testBackupAndPosition) { - int stream_size = 100000; - int buffer_size = 1234; - const void* buffer; - std::string file_contents; - std::string filename = absl::StrCat(buffer_size, "_backup_test.bin"); - int input_fd = - test::GetTestFileDescriptor(filename, stream_size, &file_contents); +TEST_P(FileInputStreamTestDefaultBufferSize, ReadAllfFromInputStreamSucceeds) { + int stream_size = GetParam(); + SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size)); + std::string file_contents = subtle::Random::GetRandomBytes(stream_size); + std::string filename = absl::StrCat( + stream_size, "_", internal::GetTestFileNamePrefix(), "_file.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); EXPECT_EQ(stream_size, file_contents.size()); + auto input_stream = absl::make_unique<util::FileInputStream>(*input_fd); + std::string stream_contents; + auto status = ReadAll(input_stream.get(), &stream_contents); + EXPECT_THAT(status, StatusIs(absl::StatusCode::kOutOfRange)); + EXPECT_EQ(status.message(), "EOF"); + EXPECT_EQ(file_contents, stream_contents); +} - // Prepare the stream and do the first call to Next(). +INSTANTIATE_TEST_SUITE_P(FileInputStreamTest, + FileInputStreamTestDefaultBufferSize, + testing::ValuesIn({0, 10, 100, 1000, 10000, 100000, + 1000000})); + +using FileInputStreamTestCustomBufferSizes = testing::TestWithParam<int>; + +TEST_P(FileInputStreamTestCustomBufferSizes, + ReadAllWithCustomBufferSizeSucceeds) { + int buffer_size = GetParam(); + SCOPED_TRACE(absl::StrCat("buffer_size = ", buffer_size)); + std::string file_contents = + subtle::Random::GetRandomBytes(kDefaultTestStreamSize); + std::string filename = absl::StrCat( + buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); + EXPECT_EQ(kDefaultTestStreamSize, file_contents.size()); auto input_stream = - absl::make_unique<util::FileInputStream>(input_fd, buffer_size); - EXPECT_EQ(0, input_stream->Position()); + absl::make_unique<util::FileInputStream>(*input_fd, buffer_size); + const void* buffer; auto next_result = input_stream->Next(&buffer); - EXPECT_TRUE(next_result.ok()) << next_result.status(); + ASSERT_THAT(next_result, IsOk()); EXPECT_EQ(buffer_size, next_result.value()); - EXPECT_EQ(buffer_size, input_stream->Position()); EXPECT_EQ(file_contents.substr(0, buffer_size), std::string(static_cast<const char*>(buffer), buffer_size)); +} - // BackUp several times, but in total fewer bytes than returned by Next(). - int total_backup_size = 0; - for (auto backup_size : {0, 1, 5, 0, 10, 100, -42, 400, 20, -100}) { - SCOPED_TRACE(absl::StrCat("backup_size = ", backup_size)); - input_stream->BackUp(backup_size); - total_backup_size += std::max(0, backup_size); - EXPECT_EQ(buffer_size - total_backup_size, input_stream->Position()); - } - // Call Next(), it should return exactly the backed up bytes. - next_result = input_stream->Next(&buffer); - EXPECT_TRUE(next_result.ok()) << next_result.status(); - EXPECT_EQ(total_backup_size, next_result.value()); - EXPECT_EQ(buffer_size, input_stream->Position()); - EXPECT_EQ( - file_contents.substr(buffer_size - total_backup_size, total_backup_size), - std::string(static_cast<const char*>(buffer), total_backup_size)); +INSTANTIATE_TEST_SUITE_P(FileInputStreamTest, + FileInputStreamTestCustomBufferSizes, + testing::ValuesIn({1, 10, 100, 1000, 10000})); - // BackUp() some bytes, again fewer than returned by Next(). - total_backup_size = 0; - for (auto backup_size : {0, 72, -94, 37, 82}) { - SCOPED_TRACE(absl::StrCat("backup_size = ", backup_size)); - input_stream->BackUp(backup_size); - total_backup_size += std::max(0, backup_size); - EXPECT_EQ(buffer_size - total_backup_size, input_stream->Position()); - } +TEST(FileInputStreamTest, NextFailsIfFdIsInvalid) { + int buffer_size = 4 * 1024; + auto input_stream = absl::make_unique<util::FileInputStream>(-1, buffer_size); + const void* buffer = nullptr; + EXPECT_THAT(input_stream->Next(&buffer).status(), + StatusIs(absl::StatusCode::kInternal)); +} - // Call Next(), it should return exactly the backed up bytes. - next_result = input_stream->Next(&buffer); - EXPECT_TRUE(next_result.ok()) << next_result.status(); - EXPECT_EQ(total_backup_size, next_result.value()); - EXPECT_EQ(buffer_size, input_stream->Position()); - EXPECT_EQ( - file_contents.substr(buffer_size - total_backup_size, total_backup_size), - std::string(static_cast<const char*>(buffer), total_backup_size)); +TEST(FileInputStreamTest, NextFailsIfDataIsNull) { + int buffer_size = 4 * 1024; + std::string file_contents = + subtle::Random::GetRandomBytes(kDefaultTestStreamSize); + std::string filename = absl::StrCat( + buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); + EXPECT_EQ(kDefaultTestStreamSize, file_contents.size()); + auto input_stream = + absl::make_unique<util::FileInputStream>(*input_fd, buffer_size); - // Call Next() again, it should return the second block. - next_result = input_stream->Next(&buffer); - EXPECT_TRUE(next_result.ok()) << next_result.status(); - EXPECT_EQ(buffer_size, next_result.value()); - EXPECT_EQ(2 * buffer_size, input_stream->Position()); - EXPECT_EQ(file_contents.substr(buffer_size, buffer_size), - std::string(static_cast<const char*>(buffer), buffer_size)); + EXPECT_THAT(input_stream->Next(nullptr).status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} - // BackUp a few times, with total over the returned buffer_size. - total_backup_size = 0; - for (auto backup_size : - {0, 72, -100, buffer_size/2, 200, -25, buffer_size, 42}) { - SCOPED_TRACE(absl::StrCat("backup_size = ", backup_size)); - input_stream->BackUp(backup_size); - total_backup_size = std::min(buffer_size, - total_backup_size + std::max(0, backup_size)); - EXPECT_EQ(2 * buffer_size - total_backup_size, input_stream->Position()); - } +TEST(FileInputStreamTest, NextReadsExactlyOneBlockOfData) { + int buffer_size = 4 * 1024; + std::string file_contents = + subtle::Random::GetRandomBytes(kDefaultTestStreamSize); + std::string filename = absl::StrCat( + buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); + EXPECT_EQ(kDefaultTestStreamSize, file_contents.size()); + auto input_stream = + absl::make_unique<util::FileInputStream>(*input_fd, buffer_size); - // Call Next() again, it should return the second block. - next_result = input_stream->Next(&buffer); - EXPECT_TRUE(next_result.ok()) << next_result.status(); - EXPECT_EQ(buffer_size, next_result.value()); - EXPECT_EQ(2 * buffer_size, input_stream->Position()); - EXPECT_EQ(file_contents.substr(buffer_size, buffer_size), - std::string(static_cast<const char*>(buffer), buffer_size)); + auto expected_file_content_block = + absl::string_view(file_contents).substr(0, buffer_size); + const void* buffer = nullptr; + util::StatusOr<int> next_result = input_stream->Next(&buffer); + ASSERT_THAT(next_result, IsOkAndHolds(buffer_size)); + // Check that we advanced of buffer_size bytes. + EXPECT_EQ(input_stream->Position(), buffer_size); + EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size), + expected_file_content_block); +} + +TEST(FileInputStreamTest, BackupForNegativeOrZeroBytesIsANoop) { + int buffer_size = 4 * 1024; + std::string file_contents = + subtle::Random::GetRandomBytes(kDefaultTestStreamSize); + std::string filename = absl::StrCat( + buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); + EXPECT_EQ(kDefaultTestStreamSize, file_contents.size()); + auto input_stream = + absl::make_unique<util::FileInputStream>(*input_fd, buffer_size); + EXPECT_EQ(input_stream->Position(), 0); + + auto expected_file_content_block = + absl::string_view(file_contents).substr(0, buffer_size); + const void* buffer = nullptr; + ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size)); + // Check that we advanced of buffer_size bytes. + EXPECT_EQ(input_stream->Position(), buffer_size); + EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size), + expected_file_content_block); + + // The calls below are noops. + input_stream->BackUp(0); + EXPECT_EQ(input_stream->Position(), buffer_size); + input_stream->BackUp(-12); + EXPECT_EQ(input_stream->Position(), buffer_size); + + // A subsequent call to `Next` returns the 2nd block. + auto expected_2nd_file_content_block = + absl::string_view(file_contents).substr(buffer_size, buffer_size); + ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size)); + // Check that we advanced of buffer_size bytes. + EXPECT_EQ(input_stream->Position(), 2 * buffer_size); + EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size), + expected_2nd_file_content_block); +} + +TEST(FileInputStreamTest, BackupForLessThanOneBlockOfData) { + int buffer_size = 4 * 1024; + std::string file_contents = + subtle::Random::GetRandomBytes(kDefaultTestStreamSize); + std::string filename = absl::StrCat( + buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); + EXPECT_EQ(kDefaultTestStreamSize, file_contents.size()); + auto input_stream = + absl::make_unique<util::FileInputStream>(*input_fd, buffer_size); + + auto expected_file_content_block = + absl::string_view(file_contents).substr(0, buffer_size); + const void* buffer = nullptr; + ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size)); + // Check that we advanced of buffer_size bytes. + EXPECT_EQ(input_stream->Position(), buffer_size); + EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size), + expected_file_content_block); + + int64_t position_after_next = input_stream->Position(); + // Number of bytes that were backed up. + int num_backed_up_bytes = 0; + input_stream->BackUp(0); // This should be a noop. + EXPECT_EQ(input_stream->Position(), position_after_next); + input_stream->BackUp(-12); // This should be a noop. + EXPECT_EQ(input_stream->Position(), position_after_next); + input_stream->BackUp(10); + num_backed_up_bytes += 10; + EXPECT_EQ(input_stream->Position(), + position_after_next - num_backed_up_bytes); + input_stream->BackUp(5); + num_backed_up_bytes += 5; + EXPECT_EQ(input_stream->Position(), + position_after_next - num_backed_up_bytes); + + // A subsequent call to Next should return only the backed up bytes. + auto expected_backed_up_bytes = + absl::string_view(file_contents) + .substr(buffer_size - num_backed_up_bytes, num_backed_up_bytes); + ASSERT_THAT(input_stream->Next(&buffer), + IsOkAndHolds(expected_backed_up_bytes.size())); + EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), + expected_backed_up_bytes.size()), + expected_backed_up_bytes); +} + +// When backing up of a number of bytes larger than the size of a block, backup +// of one block. +TEST(FileInputStreamTest, BackupAtMostOfOneBlock) { + int buffer_size = 4 * 1024; + std::string file_contents = + subtle::Random::GetRandomBytes(kDefaultTestStreamSize); + std::string filename = absl::StrCat( + buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, file_contents), IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); + EXPECT_EQ(kDefaultTestStreamSize, file_contents.size()); + auto input_stream = + absl::make_unique<util::FileInputStream>(*input_fd, buffer_size); + + // Read two blocks of size buffer_size, then back up of more than buffer_size + // bytes. + auto expected_1st_file_content_block = + absl::string_view(file_contents).substr(0, buffer_size); + const void* buffer = nullptr; + ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size)); + // Check that we advanced of buffer_size bytes. + EXPECT_EQ(input_stream->Position(), buffer_size); + EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size), + expected_1st_file_content_block); + + auto expected_2nd_file_content_block = + absl::string_view(file_contents).substr(buffer_size, buffer_size); + ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size)); + // Check that we advanced of buffer_size bytes. + EXPECT_EQ(input_stream->Position(), 2 * buffer_size); + EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size), + expected_2nd_file_content_block); + + int64_t position_after_next = input_stream->Position(); + EXPECT_EQ(input_stream->Position(), position_after_next); + input_stream->BackUp(10); + EXPECT_EQ(input_stream->Position(), position_after_next - 10); + input_stream->BackUp(buffer_size); + EXPECT_EQ(input_stream->Position(), position_after_next - buffer_size); + + // This call to Next is expected to read the second block again. + ASSERT_THAT(input_stream->Next(&buffer), IsOkAndHolds(buffer_size)); + EXPECT_EQ(absl::string_view(static_cast<const char*>(buffer), buffer_size), + expected_2nd_file_content_block); } } // namespace
diff --git a/cc/util/file_output_stream.h b/cc/util/file_output_stream.h index 43b638a..2a6dd0f 100644 --- a/cc/util/file_output_stream.h +++ b/cc/util/file_output_stream.h
@@ -28,6 +28,8 @@ namespace util { // An OutputStream that writes to a file descriptor. +// +// NOTE: This class in not available when building on Windows. class FileOutputStream : public crypto::tink::OutputStream { public: // Constructs an OutputStream that will write to the file specified
diff --git a/cc/util/file_output_stream_test.cc b/cc/util/file_output_stream_test.cc index dbdf7f2..6e46c7b 100644 --- a/cc/util/file_output_stream_test.cc +++ b/cc/util/file_output_stream_test.cc
@@ -16,20 +16,42 @@ #include "tink/util/file_output_stream.h" +#include <fcntl.h> + #include <algorithm> +#include <cstring> +#include <iostream> +#include <ostream> #include <string> #include "gtest/gtest.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "tink/internal/test_file_util.h" #include "tink/subtle/random.h" +#include "tink/util/test_matchers.h" #include "tink/util/test_util.h" namespace crypto { namespace tink { namespace { +using ::crypto::tink::test::IsOk; + +// Opens test file `filename` and returns a file descriptor to it. +util::StatusOr<int> OpenTestFileToWrite(absl::string_view filename) { + std::string full_filename = absl::StrCat(test::TmpDir(), "/", filename); + mode_t mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH; + int fd = open(full_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode); + if (fd == -1) { + return util::Status(absl::StatusCode::kInternal, + absl::StrCat("Cannot open file ", full_filename, + " error: ", std::strerror(errno))); + } + return fd; +} + // Writes 'contents' the specified 'output_stream', and closes the stream. // Returns the status of output_stream->Close()-operation, or a non-OK status // of a prior output_stream->Next()-operation, if any. @@ -62,9 +84,12 @@ for (auto stream_size : {0, 10, 100, 1000, 10000, 100000, 1000000}) { SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size)); std::string stream_contents = subtle::Random::GetRandomBytes(stream_size); - std::string filename = absl::StrCat(stream_size, "_writing_test.bin"); - int output_fd = test::GetTestFileDescriptor(filename); - auto output_stream = absl::make_unique<util::FileOutputStream>(output_fd); + std::string filename = absl::StrCat( + stream_size, internal::GetTestFileNamePrefix(), "_test.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, stream_contents), IsOk()); + util::StatusOr<int> output_fd = OpenTestFileToWrite(filename); + ASSERT_THAT(output_fd.status(), IsOk()); + auto output_stream = absl::make_unique<util::FileOutputStream>(*output_fd); auto status = WriteToStream(output_stream.get(), stream_contents); EXPECT_TRUE(status.ok()) << status; std::string file_contents = test::ReadTestFile(filename); @@ -78,10 +103,13 @@ std::string stream_contents = subtle::Random::GetRandomBytes(stream_size); for (auto buffer_size : {1, 10, 100, 1000, 10000, 100000, 1000000}) { SCOPED_TRACE(absl::StrCat("buffer_size = ", buffer_size)); - std::string filename = absl::StrCat(buffer_size, "_buffer_size_test.bin"); - int output_fd = test::GetTestFileDescriptor(filename); + std::string filename = absl::StrCat( + buffer_size, internal::GetTestFileNamePrefix(), "_test.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, stream_contents), IsOk()); + util::StatusOr<int> output_fd = OpenTestFileToWrite(filename); + ASSERT_THAT(output_fd.status(), IsOk()); auto output_stream = - absl::make_unique<util::FileOutputStream>(output_fd, buffer_size); + absl::make_unique<util::FileOutputStream>(*output_fd, buffer_size); void* buffer; auto next_result = output_stream->Next(&buffer); EXPECT_TRUE(next_result.ok()) << next_result.status(); @@ -101,12 +129,15 @@ int buffer_size = 1234; void* buffer; std::string stream_contents = subtle::Random::GetRandomBytes(stream_size); - std::string filename = absl::StrCat(buffer_size, "_backup_test.bin"); - int output_fd = test::GetTestFileDescriptor(filename); + std::string filename = + absl::StrCat(buffer_size, internal::GetTestFileNamePrefix(), "_test.bin"); + ASSERT_THAT(internal::CreateTestFile(filename, stream_contents), IsOk()); + util::StatusOr<int> output_fd = OpenTestFileToWrite(filename); + ASSERT_THAT(output_fd.status(), IsOk()); // Prepare the stream and do the first call to Next(). auto output_stream = - absl::make_unique<util::FileOutputStream>(output_fd, buffer_size); + absl::make_unique<util::FileOutputStream>(*output_fd, buffer_size); EXPECT_EQ(0, output_stream->Position()); auto next_result = output_stream->Next(&buffer); EXPECT_TRUE(next_result.ok()) << next_result.status();
diff --git a/cc/util/file_random_access_stream.h b/cc/util/file_random_access_stream.h index 47bfc13..75966c9 100644 --- a/cc/util/file_random_access_stream.h +++ b/cc/util/file_random_access_stream.h
@@ -29,6 +29,8 @@ namespace util { // An RandomAccessStream that reads from a file descriptor. +// +// NOTE: This class in not available when building on Windows. class FileRandomAccessStream : public crypto::tink::RandomAccessStream { public: // Constructs a FileRandomAccessStream that will read from the file specified
diff --git a/cc/util/file_random_access_stream_test.cc b/cc/util/file_random_access_stream_test.cc index 70c510a..3aebe63 100644 --- a/cc/util/file_random_access_stream_test.cc +++ b/cc/util/file_random_access_stream_test.cc
@@ -16,15 +16,25 @@ #include "tink/util/file_random_access_stream.h" +#include <fcntl.h> +#include <unistd.h> + +#include <cstring> +#include <iostream> +#include <ostream> #include <string> #include <thread> // NOLINT(build/c++11) #include <utility> #include "gtest/gtest.h" #include "absl/memory/memory.h" +#include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "tink/internal/test_file_util.h" +#include "tink/subtle/random.h" #include "tink/util/buffer.h" +#include "tink/util/test_matchers.h" #include "tink/util/test_util.h" namespace crypto { @@ -32,6 +42,20 @@ namespace util { namespace { +using ::crypto::tink::test::IsOk; + +// Opens test file `filename` and returns a file descriptor to it. +util::StatusOr<int> OpenTestFileToRead(absl::string_view filename) { + std::string full_filename = absl::StrCat(test::TmpDir(), "/", filename); + int fd = open(full_filename.c_str(), O_RDONLY); + if (fd == -1) { + return util::Status(absl::StatusCode::kInternal, + absl::StrCat("Cannot open file ", full_filename, + " error: ", std::strerror(errno))); + } + return fd; +} + // Reads the entire 'ra_stream' in chunks of size 'chunk_size', // until no more bytes can be read, and puts the read bytes into 'contents'. // Returns the status of the last ra_stream->Next()-operation. @@ -79,15 +103,19 @@ TEST(FileRandomAccessStreamTest, ReadingStreams) { for (auto stream_size : {1, 10, 100, 1000, 10000, 1000000}) { SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size)); - std::string file_contents; - std::string filename = absl::StrCat(stream_size, "_reading_test.bin"); - int input_fd = - test::GetTestFileDescriptor(filename, stream_size, &file_contents); + std::string file_contents = subtle::Random::GetRandomBytes(stream_size); + std::string filename = absl::StrCat( + stream_size, crypto::tink::internal::GetTestFileNamePrefix(), + "_file.bin"); + ASSERT_THAT(crypto::tink::internal::CreateTestFile(filename, file_contents), + IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); EXPECT_EQ(stream_size, file_contents.size()); - auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(input_fd); + auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(*input_fd); std::string stream_contents; - auto status = ReadAll(ra_stream.get(), 1 + (stream_size / 10), - &stream_contents); + auto status = + ReadAll(ra_stream.get(), 1 + (stream_size / 10), &stream_contents); EXPECT_EQ(absl::StatusCode::kOutOfRange, status.code()); EXPECT_EQ("EOF", status.message()); EXPECT_EQ(file_contents, stream_contents); @@ -98,12 +126,16 @@ TEST(FileRandomAccessStreamTest, ReadingStreamsTillLastByte) { for (auto stream_size : {1, 10, 100, 1000, 10000}) { SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size)); - std::string file_contents; - std::string filename = absl::StrCat(stream_size, "_reading_test.bin"); - int input_fd = - test::GetTestFileDescriptor(filename, stream_size, &file_contents); + std::string file_contents = subtle::Random::GetRandomBytes(stream_size); + std::string filename = absl::StrCat( + stream_size, crypto::tink::internal::GetTestFileNamePrefix(), + "_file.bin"); + ASSERT_THAT(crypto::tink::internal::CreateTestFile(filename, file_contents), + IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); EXPECT_EQ(stream_size, file_contents.size()); - auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(input_fd); + auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(*input_fd); auto buffer = std::move(Buffer::New(stream_size).value()); // Read from the beginning till the last byte. @@ -116,15 +148,18 @@ } } - TEST(FileRandomAccessStreamTest, ConcurrentReads) { for (auto stream_size : {100, 1000, 10000, 100000}) { - std::string file_contents; - std::string filename = absl::StrCat(stream_size, "_reading_test.bin"); - int input_fd = - test::GetTestFileDescriptor(filename, stream_size, &file_contents); + std::string file_contents = subtle::Random::GetRandomBytes(stream_size); + std::string filename = absl::StrCat( + stream_size, crypto::tink::internal::GetTestFileNamePrefix(), + "_file.bin"); + ASSERT_THAT(crypto::tink::internal::CreateTestFile(filename, file_contents), + IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); EXPECT_EQ(stream_size, file_contents.size()); - auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(input_fd); + auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(*input_fd); std::thread read_0(ReadAndVerifyChunk, ra_stream.get(), 0, stream_size / 2, file_contents); std::thread read_1(ReadAndVerifyChunk, @@ -142,11 +177,15 @@ TEST(FileRandomAccessStreamTest, NegativeReadPosition) { for (auto stream_size : {0, 10, 100, 1000, 10000}) { - std::string file_contents; - std::string filename = absl::StrCat(stream_size, "_reading_test.bin"); - int input_fd = - test::GetTestFileDescriptor(filename, stream_size, &file_contents); - auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(input_fd); + std::string file_contents = subtle::Random::GetRandomBytes(stream_size); + std::string filename = absl::StrCat( + stream_size, crypto::tink::internal::GetTestFileNamePrefix(), + "_file.bin"); + ASSERT_THAT(crypto::tink::internal::CreateTestFile(filename, file_contents), + IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); + auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(*input_fd); int count = 42; auto buffer = std::move(Buffer::New(count).value()); for (auto position : {-100, -10, -1}) { @@ -161,11 +200,15 @@ TEST(FileRandomAccessStreamTest, NotPositiveReadCount) { for (auto stream_size : {0, 10, 100, 1000, 10000}) { - std::string file_contents; - std::string filename = absl::StrCat(stream_size, "_reading_test.bin"); - int input_fd = - test::GetTestFileDescriptor(filename, stream_size, &file_contents); - auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(input_fd); + std::string file_contents = subtle::Random::GetRandomBytes(stream_size); + std::string filename = absl::StrCat( + stream_size, crypto::tink::internal::GetTestFileNamePrefix(), + "_file.bin"); + ASSERT_THAT(crypto::tink::internal::CreateTestFile(filename, file_contents), + IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); + auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(*input_fd); auto buffer = std::move(Buffer::New(42).value()); int64_t position = 0; for (auto count : {-100, -10, -1, 0}) { @@ -179,11 +222,15 @@ TEST(FileRandomAccessStreamTest, ReadPositionAfterEof) { for (auto stream_size : {0, 10, 100, 1000, 10000}) { - std::string file_contents; - std::string filename = absl::StrCat(stream_size, "_reading_test.bin"); - int input_fd = - test::GetTestFileDescriptor(filename, stream_size, &file_contents); - auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(input_fd); + std::string file_contents = subtle::Random::GetRandomBytes(stream_size); + std::string filename = absl::StrCat( + stream_size, crypto::tink::internal::GetTestFileNamePrefix(), + "_file.bin"); + ASSERT_THAT(crypto::tink::internal::CreateTestFile(filename, file_contents), + IsOk()); + util::StatusOr<int> input_fd = OpenTestFileToRead(filename); + ASSERT_THAT(input_fd.status(), IsOk()); + auto ra_stream = absl::make_unique<util::FileRandomAccessStream>(*input_fd); int count = 42; auto buffer = std::move(Buffer::New(count).value()); for (auto position : {stream_size + 1, stream_size + 10}) {
diff --git a/cc/util/input_stream_util_test.cc b/cc/util/input_stream_util_test.cc index 1700588..e54f8a7 100644 --- a/cc/util/input_stream_util_test.cc +++ b/cc/util/input_stream_util_test.cc
@@ -16,6 +16,7 @@ #include "tink/util/input_stream_util.h" +#include <sstream> #include <string> #include <utility>
diff --git a/cc/util/istream_input_stream.cc b/cc/util/istream_input_stream.cc index 71c642b..45a0449 100644 --- a/cc/util/istream_input_stream.cc +++ b/cc/util/istream_input_stream.cc
@@ -16,16 +16,17 @@ #include "tink/util/istream_input_stream.h" -#include <unistd.h> +#include <errno.h> +#include <stdint.h> #include <algorithm> #include <cstring> #include <istream> +#include <memory> #include <utility> #include "absl/memory/memory.h" #include "absl/status/status.h" -#include "tink/input_stream.h" #include "tink/util/errors.h" #include "tink/util/status.h" #include "tink/util/statusor.h" @@ -85,8 +86,7 @@ position_ = position_ - actual_count; } -IstreamInputStream::~IstreamInputStream() { -} +IstreamInputStream::~IstreamInputStream() = default; int64_t IstreamInputStream::Position() const { return position_;
diff --git a/cc/util/istream_input_stream.h b/cc/util/istream_input_stream.h index 530192a..5ed369a 100644 --- a/cc/util/istream_input_stream.h +++ b/cc/util/istream_input_stream.h
@@ -17,6 +17,8 @@ #ifndef TINK_UTIL_ISTREAM_INPUT_STREAM_H_ #define TINK_UTIL_ISTREAM_INPUT_STREAM_H_ +#include <stdint.h> + #include <istream> #include <memory>
diff --git a/cc/util/istream_input_stream_test.cc b/cc/util/istream_input_stream_test.cc index 2586348..de5c44f 100644 --- a/cc/util/istream_input_stream_test.cc +++ b/cc/util/istream_input_stream_test.cc
@@ -16,18 +16,25 @@ #include "tink/util/istream_input_stream.h" -#include <unistd.h> +#include <errno.h> +#include <stdlib.h> #include <algorithm> #include <fstream> #include <iostream> #include <istream> +#include <memory> +#include <ostream> #include <string> #include <utility> #include "gtest/gtest.h" #include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "tink/internal/test_file_util.h" #include "tink/subtle/random.h" #include "tink/util/test_util.h" @@ -76,9 +83,10 @@ }; TEST_F(IstreamInputStreamTest, testReadingStreams) { - for (int stream_size : {0, 10, 100, 1000, 10000, 100000, 1000000}) { + for (int stream_size : {0, 10, 100, 1000, 10000, 100000, 1000000}) { std::string file_contents; - std::string filename = absl::StrCat(stream_size, "_reading_test.bin"); + std::string filename = absl::StrCat( + stream_size, "_", internal::GetTestFileNamePrefix(), "_file.bin"); auto input = GetTestIstream(filename, stream_size, &file_contents); EXPECT_EQ(stream_size, file_contents.size()); auto input_stream = absl::make_unique<util::IstreamInputStream>( @@ -95,7 +103,8 @@ int stream_size = 100000; for (int buffer_size : {1, 10, 100, 1000, 10000}) { std::string file_contents; - std::string filename = absl::StrCat(buffer_size, "_buffer_size_test.bin"); + std::string filename = absl::StrCat( + buffer_size, "_", internal::GetTestFileNamePrefix(), "_file.bin"); auto input = GetTestIstream(filename, stream_size, &file_contents); EXPECT_EQ(stream_size, file_contents.size()); auto input_stream = absl::make_unique<util::IstreamInputStream>( @@ -114,7 +123,8 @@ int buffer_size = 1234; const void* buffer; std::string file_contents; - std::string filename = absl::StrCat(buffer_size, "_backup_test.bin"); + std::string filename = + absl::StrCat(buffer_size, internal::GetTestFileNamePrefix(), "_file.bin"); auto input = GetTestIstream(filename, stream_size, &file_contents); EXPECT_EQ(stream_size, file_contents.size());
diff --git a/cc/util/ostream_output_stream_test.cc b/cc/util/ostream_output_stream_test.cc index 05db8e54..8a7cf6a 100644 --- a/cc/util/ostream_output_stream_test.cc +++ b/cc/util/ostream_output_stream_test.cc
@@ -17,9 +17,11 @@ #include "tink/util/ostream_output_stream.h" #include <algorithm> +#include <cstring> #include <fstream> #include <iostream> #include <memory> +#include <ostream> #include <string> #include <utility> @@ -27,6 +29,7 @@ #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "tink/internal/test_file_util.h" #include "tink/subtle/random.h" #include "tink/util/test_util.h" @@ -75,7 +78,8 @@ for (size_t stream_size : {0, 10, 100, 1000, 10000, 100000, 1000000}) { SCOPED_TRACE(absl::StrCat("stream_size = ", stream_size)); std::string stream_contents = subtle::Random::GetRandomBytes(stream_size); - std::string filename = absl::StrCat(stream_size, "_writing_test.bin"); + std::string filename = absl::StrCat( + stream_size, internal::GetTestFileNamePrefix(), "_file.bin"); auto output = GetTestOstream(filename); auto output_stream = absl::make_unique<util::OstreamOutputStream>( std::move(output)); @@ -92,7 +96,8 @@ std::string stream_contents = subtle::Random::GetRandomBytes(stream_size); for (int buffer_size : {1, 10, 100, 1000, 10000, 100000, 1000000}) { SCOPED_TRACE(absl::StrCat("buffer_size = ", buffer_size)); - std::string filename = absl::StrCat(buffer_size, "_buffer_size_test.bin"); + std::string filename = absl::StrCat( + buffer_size, internal::GetTestFileNamePrefix(), "_file.bin"); auto output = GetTestOstream(filename); auto output_stream = absl::make_unique<util::OstreamOutputStream>( std::move(output), buffer_size); @@ -114,7 +119,8 @@ int buffer_size = 1234; void* buffer; std::string stream_contents = subtle::Random::GetRandomBytes(stream_size); - std::string filename = absl::StrCat(buffer_size, "_backup_test.bin"); + std::string filename = + absl::StrCat(buffer_size, internal::GetTestFileNamePrefix(), "_file.bin"); auto output = GetTestOstream(filename); // Prepare the stream and do the first call to Next().
diff --git a/cc/util/secret_data.h b/cc/util/secret_data.h index ab87500..54ce234 100644 --- a/cc/util/secret_data.h +++ b/cc/util/secret_data.h
@@ -28,6 +28,17 @@ namespace crypto { namespace tink { namespace util { +namespace internal { + +template <typename T> +struct SanitizingDeleter { + void operator()(T* ptr) { + ptr->~T(); // Invoke destructor. Must do this before sanitize. + SanitizingAllocator<T>().deallocate(ptr, 1); + } +}; + +} // namespace internal // Stores secret (sensitive) data and makes sure it's marked as such and // destroyed in a safe way. @@ -74,7 +85,7 @@ using element_type = typename Value::element_type; using deleter_type = typename Value::deleter_type; - SecretUniquePtr() {} + SecretUniquePtr() = default; pointer get() const { return value_.get(); } deleter_type& get_deleter() { return value_.get_deleter(); }
diff --git a/cc/util/secret_data_internal.h b/cc/util/secret_data_internal.h index ddcacd5..7d88b93 100644 --- a/cc/util/secret_data_internal.h +++ b/cc/util/secret_data_internal.h
@@ -18,7 +18,8 @@ #define TINK_UTIL_SECRET_DATA_INTERNAL_H_ #include <cstddef> -#include <memory> +#include <cstdlib> +#include <limits> #include <new> #include "absl/base/attributes.h" @@ -30,15 +31,12 @@ namespace util { namespace internal { -// placeholder for sanitization_functions, please ignore inline void SafeZeroMemory(void* ptr, std::size_t size) { OPENSSL_cleanse(ptr, size); } template <typename T> -struct SanitizingAllocator { - typedef T value_type; - +struct SanitizingAllocatorImpl { // If aligned operator new is not supported this only supports under aligned // types. #ifndef __cpp_aligned_new @@ -47,12 +45,7 @@ "before C++17"); #endif - SanitizingAllocator() = default; - template <class U> - explicit constexpr SanitizingAllocator( - const SanitizingAllocator<U>&) noexcept {} - - ABSL_MUST_USE_RESULT T* allocate(std::size_t n) { + static T* allocate(std::size_t n) { if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) { #ifdef ABSL_HAVE_EXCEPTIONS throw std::bad_array_new_length(); @@ -62,14 +55,13 @@ } std::size_t size = n * sizeof(T); #ifdef __cpp_aligned_new - void* result = ::operator new(size, std::align_val_t(alignof(T))); + return static_cast<T*>(::operator new(size, std::align_val_t(alignof(T)))); #else - void* result = ::operator new(size); + return static_cast<T*>(::operator new(size)); #endif - return static_cast<T*>(result); } - void deallocate(T* ptr, std::size_t n) noexcept { + static void deallocate(void* ptr, std::size_t n) { SafeZeroMemory(ptr, n * sizeof(T)); #ifdef __cpp_aligned_new ::operator delete(ptr, std::align_val_t(alignof(T))); @@ -77,42 +69,39 @@ ::operator delete(ptr); #endif } - - // Allocator requirements mandate definition of eq and neq operators - bool operator==(const SanitizingAllocator&) { return true; } - bool operator!=(const SanitizingAllocator&) { return false; } }; // Specialization for malloc-like aligned storage. template <> -struct SanitizingAllocator<void> { - typedef void value_type; +struct SanitizingAllocatorImpl<void> { + static void* allocate(std::size_t n) { return std::malloc(n); } + static void deallocate(void* ptr, std::size_t n) { + SafeZeroMemory(ptr, n); + return std::free(ptr); + } +}; + +template <typename T> +struct SanitizingAllocator { + typedef T value_type; SanitizingAllocator() = default; template <class U> explicit constexpr SanitizingAllocator( const SanitizingAllocator<U>&) noexcept {} - ABSL_MUST_USE_RESULT void* allocate(std::size_t n) { return std::malloc(n); } + ABSL_MUST_USE_RESULT T* allocate(std::size_t n) { + return SanitizingAllocatorImpl<T>::allocate(n); + } - void deallocate(void* ptr, std::size_t n) noexcept { - SafeZeroMemory(ptr, n); - std::free(ptr); + void deallocate(T* ptr, std::size_t n) noexcept { + SanitizingAllocatorImpl<T>::deallocate(ptr, n); } // Allocator requirements mandate definition of eq and neq operators bool operator==(const SanitizingAllocator&) { return true; } bool operator!=(const SanitizingAllocator&) { return false; } }; -// placeholder 2 for sanitization_functions, please ignore - -template <typename T> -struct SanitizingDeleter { - void operator()(T* ptr) { - ptr->~T(); // Invoke destructor. Must do this before sanitize. - SanitizingAllocator<T>().deallocate(ptr, 1); - } -}; } // namespace internal } // namespace util
diff --git a/cc/util/secret_data_test.cc b/cc/util/secret_data_test.cc index 72f1167..3de92e9 100644 --- a/cc/util/secret_data_test.cc +++ b/cc/util/secret_data_test.cc
@@ -121,6 +121,7 @@ SecretValue<int> s(102); SecretValue<int> t(std::move(s)); EXPECT_THAT(t.value(), Eq(102)); + // NOLINTNEXTLINE(bugprone-use-after-move) EXPECT_THAT(s.value(), AnyOf(Eq(0), Eq(102))); } @@ -129,6 +130,7 @@ SecretValue<int> t; t = std::move(s); EXPECT_THAT(t.value(), Eq(102)); + // NOLINTNEXTLINE(bugprone-use-after-move) EXPECT_THAT(s.value(), AnyOf(Eq(0), Eq(102))); }
diff --git a/cc/util/secret_proto.h b/cc/util/secret_proto.h index af4c9f2..ea67556 100644 --- a/cc/util/secret_proto.h +++ b/cc/util/secret_proto.h
@@ -61,7 +61,7 @@ return proto; } - SecretProto() {} + SecretProto() = default; SecretProto(const SecretProto& other) { *value_ = *other.value_; }
diff --git a/cc/util/status.cc b/cc/util/status.cc deleted file mode 100644 index bc7b389..0000000 --- a/cc/util/status.cc +++ /dev/null
@@ -1,162 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "tink/util/status.h" - -#include <sstream> -#include <string> - -#include "absl/status/status.h" -#include "absl/strings/str_cat.h" - -using ::std::ostream; - -namespace crypto { -namespace tink { -namespace util { - -#ifndef TINK_USE_ABSL_STATUS -namespace { - - -const Status& GetCancelled() { - static const Status* status = - new Status(::crypto::tink::util::error::CANCELLED, ""); - return *status; -} - -const Status& GetUnknown() { - static const Status* status = - new Status(::crypto::tink::util::error::UNKNOWN, ""); - return *status; -} - -const Status& GetOk() { - static const Status* status = new Status; - return *status; -} - -} // namespace - -Status::Status(const ::absl::Status& status) - : code_(absl::StatusCode::kOk) { - if (status.ok()) return; - code_ = status.code(); - message_ = std::string(status.message()); -} - -Status::operator ::absl::Status() const { - if (ok()) return ::absl::OkStatus(); - return ::absl::Status(code_, message_); -} - -Status::Status() : code_(absl::StatusCode::kOk), message_("") { -} - -Status::Status(::crypto::tink::util::error::Code error, - const std::string& error_message) - : code_(static_cast<absl::StatusCode>(error)), message_(error_message) { - if (code_ == absl::StatusCode::kOk) { - message_.clear(); - } -} - -Status::Status(absl::StatusCode code, absl::string_view error_message) - : code_(code), - message_(error_message) { - if (code_ == absl::StatusCode::kOk) { - message_.clear(); - } -} - -Status& Status::operator=(const Status& other) { - code_ = other.code_; - message_ = other.message_; - return *this; -} - -const Status& Status::CANCELLED = GetCancelled(); -const Status& Status::UNKNOWN = GetUnknown(); -const Status& Status::OK = GetOk(); - -std::string Status::ToString() const { - if (code_ == absl::StatusCode::kOk) { - return "OK"; - } - - std::ostringstream oss; - oss << code_ << ": " << message_; - return oss.str(); -} - -std::string ErrorCodeString(crypto::tink::util::error::Code error) { - switch (error) { - case crypto::tink::util::error::OK: - return "OK"; - case crypto::tink::util::error::CANCELLED: - return "CANCELLED"; - case crypto::tink::util::error::UNKNOWN: - return "UNKNOWN"; - case crypto::tink::util::error::INVALID_ARGUMENT: - return "INVALID_ARGUMENT"; - case crypto::tink::util::error::DEADLINE_EXCEEDED: - return "DEADLINE_EXCEEDED"; - case crypto::tink::util::error::NOT_FOUND: - return "NOT_FOUND"; - case crypto::tink::util::error::ALREADY_EXISTS: - return "ALREADY_EXISTS"; - case crypto::tink::util::error::PERMISSION_DENIED: - return "PERMISSION_DENIED"; - case crypto::tink::util::error::RESOURCE_EXHAUSTED: - return "RESOURCE_EXHAUSTED"; - case crypto::tink::util::error::FAILED_PRECONDITION: - return "FAILED_PRECONDITION"; - case crypto::tink::util::error::ABORTED: - return "ABORTED"; - case crypto::tink::util::error::OUT_OF_RANGE: - return "OUT_OF_RANGE"; - case crypto::tink::util::error::UNIMPLEMENTED: - return "UNIMPLEMENTED"; - case crypto::tink::util::error::INTERNAL: - return "INTERNAL"; - case crypto::tink::util::error::UNAVAILABLE: - return "UNAVAILABLE"; - case crypto::tink::util::error::DATA_LOSS: - return "DATA_LOSS"; - case crypto::tink::util::error::UNAUTHENTICATED: - return "UNAUTHENTICATED"; - } - // Avoid using a "default" in the switch, so that the compiler can - // give us a warning, but still provide a fallback here. - return absl::StrCat(error); -} - -extern ostream& operator<<(ostream& os, crypto::tink::util::error::Code code) { - os << ErrorCodeString(code); - return os; -} - -extern ostream& operator<<(ostream& os, const Status& other) { - os << other.ToString(); - return os; -} - -#endif // TINK_USE_ABSL_STATUS - - -} // namespace util -} // namespace tink -} // namespace crypto
diff --git a/cc/util/status.h b/cc/util/status.h index a130e2e..ef58949 100644 --- a/cc/util/status.h +++ b/cc/util/status.h
@@ -20,206 +20,16 @@ #ifndef TINK_UTIL_STATUS_H_ #define TINK_UTIL_STATUS_H_ -#include <ostream> -#include <string> - -#include "absl/base/attributes.h" #include "absl/status/status.h" +#define TINK_USE_ABSL_STATUS + namespace crypto { namespace tink { namespace util { -#ifndef TINK_USE_ABSL_STATUS - -namespace error { - -// These values match the error codes in the codes.proto file of the original. -enum ABSL_DEPRECATED("Prefer using absl::StatusCode instead.") Code { - // Not an error; returned on success - OK = 0, - - // The operation was cancelled (typically by the caller). - CANCELLED = 1, - - // Unknown error. - UNKNOWN = 2, - - // Client specified an invalid argument. Note that this differs - // from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments - // that are problematic regardless of the state of the system - // (e.g., a malformed file name). - INVALID_ARGUMENT = 3, - - // Deadline expired before operation could complete. - DEADLINE_EXCEEDED = 4, - - // Some requested entity (e.g., file or directory) was not found. - NOT_FOUND = 5, - - // Some entity that we attempted to create (e.g., file or directory) - // already exists. - ALREADY_EXISTS = 6, - - // The caller does not have permission to execute the specified - // operation. - PERMISSION_DENIED = 7, - - // Some resource has been exhausted, perhaps a per-user quota, or - // perhaps the entire file system is out of space. - RESOURCE_EXHAUSTED = 8, - - // Operation was rejected because the system is not in a state - // required for the operation's execution. For example, directory - // to be deleted may be non-empty, an rmdir operation is applied to - // a non-directory, etc. - // - // A litmus test that may help a service implementor in deciding - // between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: - // (a) Use UNAVAILABLE if the client can retry just the failing call. - // (b) Use ABORTED if the client should retry at a higher-level - // (e.g., restarting a read-modify-write sequence). - // (c) Use FAILED_PRECONDITION if the client should not retry until - // the system state has been explicitly fixed. E.g., if an "rmdir" - // fails because the directory is non-empty, FAILED_PRECONDITION - // should be returned since the client should not retry unless - // they have first fixed up the directory by deleting files from it. - FAILED_PRECONDITION = 9, - - // The operation was aborted, typically due to a concurrency issue - // like sequencer check failures, transaction aborts, etc. - // - // See litmus test above for deciding between FAILED_PRECONDITION, - // ABORTED, and UNAVAILABLE. - ABORTED = 10, - - // Operation was attempted past the valid range. E.g., seeking or - // reading past end of file. - // - // Unlike INVALID_ARGUMENT, this error indicates a problem that may - // be fixed if the system state changes. For example, a 32-bit file - // system will generate INVALID_ARGUMENT if asked to read at an - // offset that is not in the range [0,2^32-1], but it will generate - // OUT_OF_RANGE if asked to read from an offset past the current - // file size. - OUT_OF_RANGE = 11, - - // Operation is not implemented or not supported/enabled in this service. - UNIMPLEMENTED = 12, - - // Internal errors. Means some invariants expected by underlying - // system has been broken. If you see one of these errors, - // something is very broken. - INTERNAL = 13, - - // The service is currently unavailable. This is a most likely a - // transient condition and may be corrected by retrying with - // a backoff. - // - // See litmus test above for deciding between FAILED_PRECONDITION, - // ABORTED, and UNAVAILABLE. - UNAVAILABLE = 14, - - // Unrecoverable data loss or corruption. - DATA_LOSS = 15, - - // Invalid authentication credentials. - UNAUTHENTICATED = 16, -}; - -} // namespace error - -// TODO(tholenst) Remove this compile time flag in Tink 1.5. This should not be -// used, except as a temporary measure. -#ifndef CPP_TINK_TEMPORARY_STATUS_MUST_NOT_USE_RESULT -class ABSL_MUST_USE_RESULT Status; -#endif - -// A Status is a combination of an error code and a string message (for non-OK -// error codes). -class Status { - public: - // Creates an OK status - Status(); - - // Make a Status from the specified error and message. - Status(::crypto::tink::util::error::Code error, - const std::string& error_message); - // Abseil-compatible constructor from an error and a message - Status(absl::StatusCode code, absl::string_view error_message); - - Status(const Status& other) = default; - - Status& operator=(const Status& other); - - // Some pre-defined Status objects - ABSL_DEPRECATED("Use OkStatus() instead.") - static const Status& OK; // Identical to 0-arg constructor - ABSL_DEPRECATED("Use Status(absl::StatusCode::kCancelled, "") instead.") - static const Status& CANCELLED; - ABSL_DEPRECATED("Use Status(absl::StatusCode::kUnknown, "") instead.") - static const Status& UNKNOWN; - - // Accessors - bool ok() const { - return code_ == absl::StatusCode::kOk; - } - ABSL_DEPRECATED("Use its absl-compatible version code() instead.") - int error_code() const { - return static_cast<int>(code_); - } - ABSL_DEPRECATED("Use its absl-compatible version code() instead.") - ::crypto::tink::util::error::Code CanonicalCode() const { - return static_cast<::crypto::tink::util::error::Code>(code_); - } - ABSL_DEPRECATED("Use its absl-compatible version message() instead.") - const std::string& error_message() const { return message_; } - - // Abseil-compatible accessors - absl::StatusCode code() const { - return static_cast<absl::StatusCode>(code_); - } - absl::string_view message() const { - return message_; - } - - bool operator==(const Status& other) const; - bool operator!=(const Status& other) const; - - // NoOp - void IgnoreError() const { - } - - std::string ToString() const; - - Status(const ::absl::Status& status); - operator ::absl::Status() const; - - private: - absl::StatusCode code_; - std::string message_; -}; - -inline bool Status::operator==(const Status& other) const { - return (this->code_ == other.code_) && (this->message_ == other.message_); -} - -inline bool Status::operator!=(const Status& other) const { - return !(*this == other); -} - -extern std::string ErrorCodeString(crypto::tink::util::error::Code error); - -extern ::std::ostream& operator<<(::std::ostream& os, - ::crypto::tink::util::error::Code code); -extern ::std::ostream& operator<<(::std::ostream& os, const Status& other); - -#else - using Status = absl::Status; -#endif // TINK_USE_ABSL_STATUS - // Returns an OK status, equivalent to a default constructed instance. inline Status OkStatus() { return Status(); }
diff --git a/cc/util/status_test.cc b/cc/util/status_test.cc deleted file mode 100644 index 4be7542..0000000 --- a/cc/util/status_test.cc +++ /dev/null
@@ -1,60 +0,0 @@ -// Copyright 2021 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "tink/util/status.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/status/status.h" - -namespace crypto { -namespace tink { -namespace util { -namespace { - -#ifndef TINK_USE_ABSL_STATUS -TEST(StatusTest, CreateNonOkStatusWithAbslStatusCode) { - Status util_status = Status(error::Code::CANCELLED, "message"); - Status absl_status = Status(absl::StatusCode::kCancelled, "message"); - ASSERT_EQ(util_status, absl_status); -} - -TEST(StatusTest, CreateOkStatusWithAbslStatusCode) { - Status util_status = Status(error::Code::OK, "message"); - Status absl_status = Status(absl::StatusCode::kOk, "message"); - ASSERT_EQ(util_status, absl_status); - ASSERT_EQ(absl_status.message(), ""); -} - -TEST(StatusTest, ConvertNonOkStatus) { - Status util_status = Status(error::Code::RESOURCE_EXHAUSTED, "message"); - absl::Status absl_status = util_status; - ASSERT_EQ(util_status.code(), absl_status.code()); - ASSERT_EQ(util_status.message(), absl_status.message()); -} - -TEST(StatusTest, ConvertOkStatus) { - Status util_status = OkStatus(); - absl::Status absl_status = util_status; - ASSERT_TRUE(absl_status.ok()); - ASSERT_EQ(absl_status.message(), ""); -} -#endif - -} // namespace -} // namespace util -} // namespace tink -} // namespace crypto
diff --git a/cc/util/statusor.h b/cc/util/statusor.h index 28cc35e..859cf11 100644 --- a/cc/util/statusor.h +++ b/cc/util/statusor.h
@@ -17,254 +17,18 @@ #ifndef TINK_UTIL_STATUSOR_H_ #define TINK_UTIL_STATUSOR_H_ -#include <cstdlib> -#include <iostream> -#include <utility> - #include "absl/status/statusor.h" #include "tink/util/status.h" +#define TINK_USE_ABSL_STATUSOR + namespace crypto { namespace tink { namespace util { -#ifndef TINK_USE_ABSL_STATUSOR - -#ifndef CPP_TINK_TEMPORARY_STATUS_MUST_NOT_USE_RESULT -template <typename T> -class ABSL_MUST_USE_RESULT StatusOr; -#endif - -// TODO(b/122292096): Migrate this to absl::StatusOr -// A StatusOr holds a Status (in the case of an error), or a value T. -template <typename T> -class StatusOr { - public: - // StatusOr<T>::value_type - // - // This instance data provides a generic `value_type` member for use within - // generic programming. This usage is analogous to that of - // `optional::value_type` in the case of `std::optional`. - using value_type = T; - - using type = T; - // Has status UNKNOWN. - inline StatusOr(); - - // Builds from a non-OK status. Crashes if an OK status is specified. - inline StatusOr(const ::crypto::tink::util::Status& status); // NOLINT - - // Builds from the specified value. - inline StatusOr(const T& value); // NOLINT - inline StatusOr(T&& value); // NOLINT - - // Copy constructor. - inline StatusOr(const StatusOr& other); - - // Move constructor. - inline StatusOr(StatusOr&& other); - - // Conversion copy constructor, T must be copy constructible from U. - template <typename U> - inline StatusOr(const StatusOr<U>& other); - - // Assignment operator. - inline const StatusOr& operator=(const StatusOr& other); - - // Conversion assignment operator, T must be assignable from U - template <typename U> - inline const StatusOr& operator=(const StatusOr<U>& other); - - // Accessors. - inline const ::crypto::tink::util::Status& status() const { - return status_; - } - - // Shorthand for status().ok(). - inline bool ok() const { - return status_.ok(); - } - - // Returns value or crashes if ok() is false. - inline const T& ValueOrDie() const& { - EnsureOk(); - return *value_; - } - inline T& ValueOrDie() & { - EnsureOk(); - return *value_; - } - inline const T&& ValueOrDie() const&& { - EnsureOk(); - return *std::move(value_); - } - inline T&& ValueOrDie() && { - EnsureOk(); - return *std::move(value_); - } - - // Returns value if ok(), otherwise crashes if exceptions are disabled OR - // throws if exceptions are enabled. - inline const T& value() const& { - if (!ok()) AbortWithMessageFrom(status_); - return *value_; - } - inline T& value() & { - if (!ok()) AbortWithMessageFrom(status_); - return *value_; - } - inline const T&& value() const&& { - if (!ok()) AbortWithMessageFrom(std::move(status_)); - return *std::move(value_); - } - inline T&& value() && { - if (!ok()) AbortWithMessageFrom(std::move(status_)); - return *std::move(value_); - } - - // Implicitly convertible to absl::StatusOr. Implicit conversions explicitly - // allowed by style arbiter waiver in cl/351594378. - operator ::absl::StatusOr<T>() const&; // NOLINT - operator ::absl::StatusOr<T>() &&; // NOLINT - - // Returns value or crashes if ok() is false. - inline const T& operator*() const& { - EnsureOk(); - return *value_; - } - - inline T& operator*() & { - EnsureOk(); - return *value_; - } - - inline T&& operator*() && { - EnsureOk(); - return *std::move(value_); - } - - inline const T&& operator*() const&& { - EnsureOk(); - return *std::move(value_); - } - - // Returns reference to value or crashes if ok() is false. - T* operator->() { - EnsureOk(); - return &(value_.value()); - } - - const T* operator->() const { - EnsureOk(); - return &(value_.value()); - } - - template <typename U> - friend class StatusOr; - - private: - void EnsureOk() const { - if (ABSL_PREDICT_FALSE(!ok())) { - std::cerr << "Attempting to fetch value of non-OK StatusOr\n"; - std::cerr << status() << std::endl; - std::_Exit(1); - } - } - - void AbortWithMessageFrom(crypto::tink::util::Status status) const { - std::cerr << "Attempting to fetch value instead of handling error\n"; - std::cerr << status.ToString(); - std::abort(); - } - - - Status status_; - absl::optional<T> value_; -}; - -// Implementation. - -template <typename T> -inline StatusOr<T>::StatusOr() - : status_(absl::StatusCode::kUnknown, "") { -} - -template <typename T> -inline StatusOr<T>::StatusOr( - const ::crypto::tink::util::Status& status) : status_(status) { - if (status.ok()) { - std::cerr << "::crypto::tink::util::OkStatus() " - << "is not a valid argument to StatusOr\n"; - std::_Exit(1); - } -} - -template <typename T> -inline StatusOr<T>::StatusOr(const T& value) : value_(value) { -} - -template <typename T> -inline StatusOr<T>::StatusOr(T&& value) : value_(std::move(value)) { -} - -template <typename T> -inline StatusOr<T>::StatusOr(const StatusOr& other) - : status_(other.status_), value_(other.value_) { -} - -template <typename T> -inline StatusOr<T>::StatusOr(StatusOr&& other) - : status_(other.status_), value_(std::move(other.value_)) { -} - -template <typename T> -template <typename U> -inline StatusOr<T>::StatusOr(const StatusOr<U>& other) - : status_(other.status_), value_(other.value_) { -} - -template <typename T> -inline const StatusOr<T>& StatusOr<T>::operator=(const StatusOr& other) { - status_ = other.status_; - if (status_.ok()) { - value_ = *other.value_; - } else { - value_ = absl::nullopt; - } - return *this; -} - -template <typename T> -template <typename U> -inline const StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) { - status_ = other.status_; - if (status_.ok()) { - value_ = *other.value_; - } else { - value_ = absl::nullopt; - } - return *this; -} - -template <typename T> -StatusOr<T>::operator ::absl::StatusOr<T>() const& { - if (!ok()) return ::absl::Status(status_); - return *value_; -} - -template <typename T> -StatusOr<T>::operator ::absl::StatusOr<T>() && { - if (!ok()) return ::absl::Status(std::move(status_)); - return std::move(*value_); -} - -#else - template <typename T> using StatusOr = absl::StatusOr<T>; -#endif // TINK_USE_ABSL_STATUSOR - } // namespace util } // namespace tink } // namespace crypto
diff --git a/cc/util/statusor_test.cc b/cc/util/statusor_test.cc deleted file mode 100644 index 7aa8583..0000000 --- a/cc/util/statusor_test.cc +++ /dev/null
@@ -1,242 +0,0 @@ -// Copyright 2021 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "tink/util/statusor.h" - -#include <memory> -#include <string> -#include <utility> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "tink/util/status.h" -#include "tink/util/test_matchers.h" - -namespace crypto { -namespace tink { -namespace util { -namespace { - -using ::crypto::tink::test::IsOk; -using ::testing::Eq; -using ::testing::Not; -using ::testing::Pointee; - -TEST(StatusOrTest, ConvertOkToAbsl) { - StatusOr<int> instance = 1; - - absl::StatusOr<int> converted = instance; - ASSERT_TRUE(converted.ok()); - EXPECT_EQ(*converted, 1); -} - -TEST(StatusOrTest, ConvertErrorToAbsl) { - #ifndef TINK_USE_ABSL_STATUS - StatusOr<int> instance{ - Status(error::Code::INVALID_ARGUMENT, "Error message")}; - #else - StatusOr<int> instance{ - Status(absl::StatusCode::kInvalidArgument, "Error message")}; - #endif - - absl::StatusOr<int> converted = instance; - ASSERT_FALSE(converted.ok()); - EXPECT_EQ(converted.status().code(), absl::StatusCode::kInvalidArgument); - EXPECT_EQ(converted.status().message(), "Error message"); -} - -TEST(StatusOrTest, ConvertUncopyableToAbsl) { - StatusOr<std::unique_ptr<int>> instance = absl::make_unique<int>(1); - - absl::StatusOr<std::unique_ptr<int>> converted = std::move(instance); - ASSERT_TRUE(converted.ok()); - EXPECT_THAT(*converted, Pointee(Eq(1))); -} - -class NoDefaultConstructor { - public: - explicit NoDefaultConstructor(int i) {} - - NoDefaultConstructor() = delete; - NoDefaultConstructor(const NoDefaultConstructor&) = default; - NoDefaultConstructor& operator=(const NoDefaultConstructor&) = - default; - NoDefaultConstructor(NoDefaultConstructor&&) = default; - NoDefaultConstructor& operator=(NoDefaultConstructor&&) = default; -}; - -// Tests that we can construct a StatusOr<T> even if there is no default -// constructor for T. -TEST(StatusOrTest, WithNoDefaultConstructor) { - StatusOr<NoDefaultConstructor> value = NoDefaultConstructor(13); - StatusOr<NoDefaultConstructor> error = - Status(absl::StatusCode::kInvalidArgument, "Error message"); -} - -// This tests that when we assign to something which is previously an error, -// we create a new optional inside the StatusOr, and do not try to assign to -// the value of the optional instead. -TEST(StatusOrTest, AssignToErrorStatus) { - StatusOr<std::string> error_initially = - Status(absl::StatusCode::kInvalidArgument, "Error message"); - ASSERT_THAT(error_initially, Not(IsOk())); - StatusOr<std::string> ok_initially = std::string("Hi"); - error_initially = ok_initially; - ASSERT_THAT(error_initially, IsOk()); - ASSERT_THAT(error_initially.value(), Eq("Hi")); - -#ifndef TINK_USE_ABSL_STATUSOR - ASSERT_THAT(error_initially.ValueOrDie(), Eq("Hi")); -#endif -} - -// This tests that when we assign to something which is previously an error and -// at the same time use the implicit conversion operator, we create a new -// optional inside the StatusOr, and do not try to assign to the value of the -// optional instead. -TEST(StatusOrTest, AssignToErrorStatusImplicitConvertible) { - StatusOr<std::string> error_initially = - Status(absl::StatusCode::kInvalidArgument, "Error message"); - ASSERT_THAT(error_initially, Not(IsOk())); - StatusOr<char const*> ok_initially = "Hi"; - error_initially = ok_initially; - ASSERT_THAT(error_initially, IsOk()); - ASSERT_THAT(error_initially.value(), Eq("Hi")); - -#ifndef TINK_USE_ABSL_STATUSOR - ASSERT_THAT(error_initially.ValueOrDie(), Eq("Hi")); -#endif -} - -#ifndef TINK_USE_ABSL_STATUSOR -TEST(StatusOrTest, MoveOutMoveOnlyValueOrDie) { - StatusOr<std::unique_ptr<int>> status_or_unique_ptr_int = - absl::make_unique<int>(10); - std::unique_ptr<int> ten = std::move(status_or_unique_ptr_int.ValueOrDie()); - ASSERT_THAT(*ten, Eq(10)); -} -#endif - -TEST(StatusOrTest, MoveOutMoveOnlyValue) { - StatusOr<std::unique_ptr<int>> status_or_unique_ptr_int = - absl::make_unique<int>(10); - std::unique_ptr<int> ten = std::move(status_or_unique_ptr_int.value()); - ASSERT_THAT(*ten, Eq(10)); -} - -TEST(STatusOrTest, CallValueOnConst) { - const StatusOr<int> const_status_or_ten = 10; - ASSERT_THAT(const_status_or_ten.value(), Eq(10)); -} - -TEST(StatusOrTest, CallValueOnConstTemp) { - const StatusOr<int> const_status_or_ten = 10; - ASSERT_THAT(std::move(const_status_or_ten).value(), Eq(10)); -} - -TEST(StatusOrTest, TestValueConst) { - const int kI = 4; - const absl::StatusOr<int> thing(kI); - EXPECT_EQ(kI, *thing); -} - -TEST(StatusOrTest, TestPointerValue) { - const int kI = 0; - absl::StatusOr<const int*> thing(&kI); - EXPECT_EQ(&kI, *thing); -} - -TEST(StatusOrTest, TestPointerValueConst) { - const int kI = 0; - const absl::StatusOr<const int*> thing(&kI); - EXPECT_EQ(&kI, *thing); -} - -TEST(StatusOrTest, OperatorStarRefQualifiers) { - static_assert( - std::is_same<const int&, - decltype(*std::declval<const absl::StatusOr<int>&>())>(), - "Unexpected ref-qualifiers"); - static_assert( - std::is_same<int&, decltype(*std::declval<absl::StatusOr<int>&>())>(), - "Unexpected ref-qualifiers"); - static_assert( - std::is_same<const int&&, - decltype(*std::declval<const absl::StatusOr<int>&&>())>(), - "Unexpected ref-qualifiers"); - static_assert( - std::is_same<int&&, decltype(*std::declval<absl::StatusOr<int>&&>())>(), - "Unexpected ref-qualifiers"); -} - -TEST(StatusOrTest, OperatorStar) { - const util::StatusOr<std::string> const_lvalue("hello"); - EXPECT_EQ("hello", *const_lvalue); - - util::StatusOr<std::string> lvalue("hello"); - EXPECT_EQ("hello", *lvalue); - - // Note: Recall that std::move() is equivalent to a static_cast to an rvalue - // reference type. - const util::StatusOr<std::string> const_rvalue("hello"); - EXPECT_EQ("hello", *std::move(const_rvalue)); // NOLINT - - util::StatusOr<std::string> rvalue("hello"); - EXPECT_EQ("hello", *std::move(rvalue)); -} - -TEST(StatusOrTest, OperatorArrowQualifiers) { - static_assert( - std::is_same< - const int*, - decltype(std::declval<const util::StatusOr<int>&>().operator->())>(), - "Unexpected qualifiers"); - static_assert( - std::is_same< - int*, decltype(std::declval<util::StatusOr<int>&>().operator->())>(), - "Unexpected qualifiers"); - static_assert( - std::is_same< - const int*, - decltype(std::declval<const util::StatusOr<int>&&>().operator->())>(), - "Unexpected qualifiers"); - static_assert( - std::is_same< - int*, decltype(std::declval<util::StatusOr<int>&&>().operator->())>(), - "Unexpected qualifiers"); -} - -TEST(StatusOrTest, OperatorArrow) { - const util::StatusOr<std::string> const_lvalue("hello"); - EXPECT_EQ(std::string("hello"), const_lvalue->c_str()); - - util::StatusOr<std::string> lvalue("hello"); - EXPECT_EQ(std::string("hello"), lvalue->c_str()); -} - -TEST(StatusOr, ElementType) { - static_assert(std::is_same<absl::StatusOr<int>::value_type, int>(), ""); - static_assert(std::is_same<absl::StatusOr<char>::value_type, char>(), ""); -} - -} // namespace - -} // namespace util -} // namespace tink -} // namespace crypto
diff --git a/cc/util/test_keyset_handle.cc b/cc/util/test_keyset_handle.cc index f60ca75..dd2de49 100644 --- a/cc/util/test_keyset_handle.cc +++ b/cc/util/test_keyset_handle.cc
@@ -16,6 +16,7 @@ #include "tink/util/test_keyset_handle.h" +#include <memory> #include <utility> #include "absl/memory/memory.h"
diff --git a/cc/util/test_keyset_handle.h b/cc/util/test_keyset_handle.h index dc086da..da13136 100644 --- a/cc/util/test_keyset_handle.h +++ b/cc/util/test_keyset_handle.h
@@ -17,6 +17,8 @@ #ifndef TINK_UTIL_TEST_KEYSET_HANDLE_H_ #define TINK_UTIL_TEST_KEYSET_HANDLE_H_ +#include <memory> + #include "tink/keyset_handle.h" #include "proto/tink.pb.h"
diff --git a/cc/util/test_matchers.h b/cc/util/test_matchers.h index 9f42156..0a83996 100644 --- a/cc/util/test_matchers.h +++ b/cc/util/test_matchers.h
@@ -17,6 +17,7 @@ #ifndef TINK_UTIL_TEST_MATCHERS_H_ #define TINK_UTIL_TEST_MATCHERS_H_ +#include <ostream> #include <string> #include "gmock/gmock.h" @@ -114,7 +115,8 @@ // Matches a util::StatusOk() value. // This is better than EXPECT_TRUE(status.ok()) // because the error message is a part of the failure messsage. -MATCHER(IsOk, "is a Status with an OK value") { +MATCHER(IsOk, + absl::StrCat(negation ? "isn't" : "is", " a Status with an OK value")) { if (arg.ok()) { return true; } @@ -131,26 +133,20 @@ std::forward<InnerMatcher>(inner_matcher)); } -// Matches a Status with the specified 'code' as error_code(). -// TODO(lizatretyakova): remove the static_cast and fix the comment above to -// use code() after all StatusIs usages are migrated to use absl::StatusCode. +// Matches a Status with the specified 'code' as code(). MATCHER_P(StatusIs, code, - "is a Status with a " + - absl::StatusCodeToString(static_cast<absl::StatusCode>(code)) + - " code") { - if (arg.code() == static_cast<absl::StatusCode>(code)) { + "is a Status with a " + absl::StatusCodeToString(code) + " code") { + if (arg.code() == code) { return true; } *result_listener << ::testing::PrintToString(arg); return false; } -// Matches a Status whose error_code() equals 'code', and whose -// error_message() matches 'message_macher'. -// TODO(lizatretyakova): remove the static_cast and fix the comment above to -// use code() after all StatusIs usages are migrated to use absl::StatusCode. +// Matches a Status whose code() equals 'code', and whose message() matches +// 'message_macher'. MATCHER_P2(StatusIs, code, message_matcher, "") { - return (arg.code() == static_cast<absl::StatusCode>(code)) && + return (arg.code() == code) && testing::Matches(message_matcher)(std::string(arg.message())); }
diff --git a/cc/util/test_util.cc b/cc/util/test_util.cc index ad6a6d9..2e58dae 100644 --- a/cc/util/test_util.cc +++ b/cc/util/test_util.cc
@@ -16,15 +16,20 @@ #include "tink/util/test_util.h" -#include <fcntl.h> #include <stdarg.h> #include <stdlib.h> -#include <unistd.h> #include <cmath> #include <cstdint> #include <cstdlib> +#include <fstream> +#include <ios> +#include <iostream> +#include <memory> +#include <ostream> +#include <sstream> #include <string> +#include <vector> #include "absl/memory/memory.h" #include "absl/status/status.h" @@ -59,7 +64,6 @@ using crypto::tink::util::Status; using google::crypto::tink::AesGcmKeyFormat; using google::crypto::tink::EcdsaPrivateKey; -using google::crypto::tink::EcdsaSignatureEncoding; using google::crypto::tink::EciesAeadHkdfPrivateKey; using google::crypto::tink::Ed25519PrivateKey; using google::crypto::tink::Keyset; @@ -69,80 +73,16 @@ namespace tink { namespace test { -int GetTestFileDescriptor(absl::string_view filename, int size, - std::string* file_contents) { - (*file_contents) = subtle::Random::GetRandomBytes(size); - return GetTestFileDescriptor(filename, *file_contents); -} - -int GetTestFileDescriptor( - absl::string_view filename, absl::string_view file_contents) { - std::string full_filename = - absl::StrCat(crypto::tink::test::TmpDir(), "/", filename); - mode_t mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH; - int fd = open(full_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode); - if (fd == -1) { - std::clog << "Cannot create file " << full_filename - << " error: " << errno << std::endl; +std::string ReadTestFile(absl::string_view filename) { + std::string full_filename = absl::StrCat(test::TmpDir(), "/", filename); + std::ifstream input_stream(full_filename, std::ios::binary); + if (!input_stream) { + std::clog << "Cannot open file " << full_filename << std::endl; exit(1); } - auto size = file_contents.size(); - if (write(fd, file_contents.data(), size) != size) { - std::clog << "Failed to write " << size << " bytes to file " - << full_filename << " error: " << errno << std::endl; - - exit(1); - } - close(fd); - fd = open(full_filename.c_str(), O_RDONLY); - if (fd == -1) { - std::clog << "Cannot re-open file " << full_filename - << " error: " << errno << std::endl; - exit(1); - } - return fd; -} - - -int GetTestFileDescriptor(absl::string_view filename) { - std::string full_filename = - absl::StrCat(crypto::tink::test::TmpDir(), "/", filename); - mode_t mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH; - int fd = open(full_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode); - if (fd == -1) { - std::clog << "Cannot create file " << full_filename - << " error: " << errno << std::endl; - exit(1); - } - return fd; -} - -std::string ReadTestFile(std::string filename) { - std::string full_filename = - absl::StrCat(crypto::tink::test::TmpDir(), "/", filename); - int fd = open(full_filename.c_str(), O_RDONLY); - if (fd == -1) { - std::clog << "Cannot open file " << full_filename - << " error: " << errno << std::endl; - exit(1); - } - std::string contents; - int buffer_size = 128 * 1024; - auto buffer = absl::make_unique<uint8_t[]>(buffer_size); - int read_result = read(fd, buffer.get(), buffer_size); - while (read_result > 0) { - std::clog << "Read " << read_result << " bytes" << std::endl; - contents.append(reinterpret_cast<const char*>(buffer.get()), read_result); - read_result = read(fd, buffer.get(), buffer_size); - } - if (read_result < 0) { - std::clog << "Error reading file " << full_filename - << " error: " << errno << std::endl; - exit(1); - } - close(fd); - std::clog << "Read in total " << contents.length() << " bytes" << std::endl; - return contents; + std::stringstream buffer; + buffer << input_stream.rdbuf(); + return buffer.str(); } util::StatusOr<std::string> HexDecode(absl::string_view hex) { @@ -184,24 +124,25 @@ } std::string TmpDir() { - // The Bazel 'test' command sets TEST_TMPDIR. - const char* env = getenv("TEST_TMPDIR"); - if (env && env[0] != '\0') { - return env; + // Try the following environment variables in order: + // - TEST_TMPDIR: Set by `bazel test`. + // - TMPDIR: Set by some Tink tests. + // - TEMP, TMP: Set on Windows; they contain the tmp dir's path. + for (const std::string& tmp_env_variable : + {"TEST_TMPDIR", "TMPDIR", "TEMP", "TMP"}) { + const char* env = getenv(tmp_env_variable.c_str()); + if (env && env[0] != '\0') { + return env; + } } - env = getenv("TMPDIR"); - if (env && env[0] != '\0') { - return env; - } + // Tmp dir on Linux/macOS. return "/tmp"; } -void AddKeyData( - const google::crypto::tink::KeyData& key_data, - uint32_t key_id, - google::crypto::tink::OutputPrefixType output_prefix, - google::crypto::tink::KeyStatusType key_status, - google::crypto::tink::Keyset* keyset) { +void AddKeyData(const google::crypto::tink::KeyData& key_data, uint32_t key_id, + google::crypto::tink::OutputPrefixType output_prefix, + google::crypto::tink::KeyStatusType key_status, + google::crypto::tink::Keyset* keyset) { Keyset::Key* key = keyset->add_key(); key->set_output_prefix_type(output_prefix); key->set_key_id(key_id);
diff --git a/cc/util/test_util.h b/cc/util/test_util.h index 10de4e3..cba8433 100644 --- a/cc/util/test_util.h +++ b/cc/util/test_util.h
@@ -18,6 +18,8 @@ #define TINK_UTIL_TEST_UTIL_H_ #include <limits> +#include <memory> +#include <ostream> #include <string> #include <utility> @@ -62,22 +64,8 @@ // Various utilities for testing. /////////////////////////////////////////////////////////////////////////////// -// Creates a new test file with the specified 'filename', writes 'size' random -// bytes to the file, and returns a file descriptor for reading from the file. -// A copy of the bytes written to the file is returned in 'file_contents'. -int GetTestFileDescriptor(absl::string_view filename, int size, - std::string* file_contents); - -// Creates a new test file with the specified 'filename', with contents from -// 'file_contents', and returns a file descriptor for reading from the file. -int GetTestFileDescriptor(absl::string_view filename, - absl::string_view file_contents); - -// Creates a new test file with the specified 'filename', ready for writing. -int GetTestFileDescriptor(absl::string_view filename); - -// Reads the test file specified by 'filename', and returns its contents. -std::string ReadTestFile(std::string filename); +// Reads the test file specified by `filename`, and returns its contents. +std::string ReadTestFile(absl::string_view filename); // Converts a hexadecimal string into a string of bytes. // Returns a status if the size of the input is odd or if the input contains @@ -493,40 +481,35 @@ util::Status status_; }; // class DummyDecryptingStream - // Upon first call to PRead() tries to read from 'ct_source' a header - // that is expected to be equal to 'expected_header'. If this + // Upon first call to PRead() tries to read from `ct_source` a header + // that is expected to be equal to `expected_header`. If this // header matching succeeds, all subsequent method calls are forwarded - // to the corresponding methods of 'cd_source'. + // to `ct_source->PRead`. class DummyDecryptingRandomAccessStream : public crypto::tink::RandomAccessStream { public: DummyDecryptingRandomAccessStream( std::unique_ptr<crypto::tink::RandomAccessStream> ct_source, absl::string_view expected_header) - : ct_source_(std::move(ct_source)), - exp_header_(expected_header), - status_(util::Status(absl::StatusCode::kUnavailable, - "not initialized")) {} + : ct_source_(std::move(ct_source)), exp_header_(expected_header) {} crypto::tink::util::Status PRead( int64_t position, int count, crypto::tink::util::Buffer* dest_buffer) override { - { // Initialize, if not initialized yet. - absl::MutexLock lock(&status_mutex_); - if (status_.code() == absl::StatusCode::kUnavailable) Initialize(); - if (!status_.ok()) return status_; + util::Status status = CheckHeader(); + if (!status.ok()) { + return status; } - auto status = dest_buffer->set_size(0); + status = dest_buffer->set_size(0); if (!status.ok()) return status; return ct_source_->PRead(position + exp_header_.size(), count, dest_buffer); } util::StatusOr<int64_t> size() override { - { // Initialize, if not initialized yet. - absl::MutexLock lock(&status_mutex_); - if (status_.code() == absl::StatusCode::kUnavailable) Initialize(); - if (!status_.ok()) return status_; + util::Status status = CheckHeader(); + if (!status.ok()) { + return status; } auto ct_size_result = ct_source_->size(); if (!ct_size_result.ok()) return ct_size_result.status(); @@ -536,25 +519,39 @@ } private: - void Initialize() ABSL_EXCLUSIVE_LOCKS_REQUIRED(status_mutex_) { + util::Status CheckHeader() + ABSL_LOCKS_EXCLUDED(header_check_status_mutex_) { + absl::MutexLock lock(&header_check_status_mutex_); + if (header_check_status_.code() != absl::StatusCode::kUnavailable) { + return header_check_status_; + } auto buf = std::move(util::Buffer::New(exp_header_.size()).value()); - status_ = ct_source_->PRead(0, exp_header_.size(), buf.get()); - if (!status_.ok() && status_.code() != absl::StatusCode::kOutOfRange) - return; + header_check_status_ = + ct_source_->PRead(0, exp_header_.size(), buf.get()); + if (!header_check_status_.ok() && + header_check_status_.code() != absl::StatusCode::kOutOfRange) { + return header_check_status_; + } + // EOF or Ok indicate a valid read has happened. + header_check_status_ = util::OkStatus(); + // Invalid header. if (buf->size() < exp_header_.size()) { - status_ = util::Status(absl::StatusCode::kInvalidArgument, + header_check_status_ = util::Status(absl::StatusCode::kInvalidArgument, "Could not read header"); } else if (memcmp(buf->get_mem_block(), exp_header_.data(), static_cast<int>(exp_header_.size()))) { - status_ = util::Status(absl::StatusCode::kInvalidArgument, + header_check_status_ = util::Status(absl::StatusCode::kInvalidArgument, "Corrupted header"); } + return header_check_status_; } std::unique_ptr<crypto::tink::RandomAccessStream> ct_source_; std::string exp_header_; - mutable absl::Mutex status_mutex_; - util::Status status_ ABSL_GUARDED_BY(status_mutex_); + mutable absl::Mutex header_check_status_mutex_; + util::Status header_check_status_ + ABSL_GUARDED_BY(header_check_status_mutex_) = + util::Status(absl::StatusCode::kUnavailable, "Uninitialized"); }; // class DummyDecryptingRandomAccessStream private: @@ -733,7 +730,7 @@ return {absl::make_unique<DummyAead>(key_uri)}; } - ~DummyKmsClient() override {} + ~DummyKmsClient() override = default; private: std::string uri_prefix_;
diff --git a/cc/util/test_util_test.cc b/cc/util/test_util_test.cc index 4b8fdf9..34171c3 100644 --- a/cc/util/test_util_test.cc +++ b/cc/util/test_util_test.cc
@@ -15,9 +15,24 @@ /////////////////////////////////////////////////////////////////////////////// #include "tink/util/test_util.h" +#include <algorithm> +#include <cstdint> +#include <memory> +#include <sstream> +#include <string> +#include <utility> + #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "tink/internal/test_random_access_stream.h" +#include "tink/output_stream.h" +#include "tink/random_access_stream.h" #include "tink/subtle/random.h" +#include "tink/subtle/test_util.h" +#include "tink/util/buffer.h" +#include "tink/util/ostream_output_stream.h" +#include "tink/util/statusor.h" #include "tink/util/test_matchers.h" #include "proto/aes_gcm.pb.h" #include "proto/tink.pb.h" @@ -27,6 +42,8 @@ namespace test { namespace { +using ::crypto::tink::internal::TestRandomAccessStream; +using ::crypto::tink::test::StatusIs; using ::google::crypto::tink::AesGcmKey; using ::google::crypto::tink::KeyData; using ::testing::Eq; @@ -107,6 +124,170 @@ IsOk()); } +TEST(DummyStreamingAead, DummyDecryptingStreamPreadAllAtOnceSucceeds) { + const int stream_size = 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + + auto ostream = std::make_unique<std::ostringstream>(); + auto string_stream_buffer = ostream->rdbuf(); + auto output_stream = + std::make_unique<util::OstreamOutputStream>(std::move(ostream)); + + DummyStreamingAead streaming_aead("Some AEAD"); + util::StatusOr<std::unique_ptr<OutputStream>> encrypting_output_stream = + streaming_aead.NewEncryptingStream(std::move(output_stream), "Some AAD"); + ASSERT_THAT(encrypting_output_stream.status(), IsOk()); + ASSERT_THAT(subtle::test::WriteToStream( + encrypting_output_stream.value().get(), stream_content), + IsOk()); + + std::string ciphertext = string_stream_buffer->str(); + auto test_random_access_stream = + std::make_unique<TestRandomAccessStream>(ciphertext); + util::StatusOr<std::unique_ptr<RandomAccessStream>> + decrypting_random_access_stream = + streaming_aead.NewDecryptingRandomAccessStream( + std::move(test_random_access_stream), "Some AAD"); + ASSERT_THAT(decrypting_random_access_stream.status(), IsOk()); + + auto buffer = util::Buffer::New(ciphertext.size()); + EXPECT_THAT((*decrypting_random_access_stream) + ->PRead(/*position=*/0, ciphertext.size(), buffer->get()), + StatusIs(absl::StatusCode::kOutOfRange)); + EXPECT_EQ(stream_content, + std::string((*buffer)->get_mem_block(), (*buffer)->size())); +} + +TEST(DummyStreamingAead, DummyDecryptingStreamPreadInChunksSucceeds) { + const int stream_size = 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + + auto ostream = std::make_unique<std::ostringstream>(); + auto string_stream_buffer = ostream->rdbuf(); + auto output_stream = + std::make_unique<util::OstreamOutputStream>(std::move(ostream)); + + DummyStreamingAead streaming_aead("Some AEAD"); + util::StatusOr<std::unique_ptr<OutputStream>> encrypting_output_stream = + streaming_aead.NewEncryptingStream(std::move(output_stream), "Some AAD"); + ASSERT_THAT(encrypting_output_stream.status(), IsOk()); + ASSERT_THAT(subtle::test::WriteToStream( + encrypting_output_stream.value().get(), stream_content), + IsOk()); + + std::string ciphertext = string_stream_buffer->str(); + auto test_random_access_stream = + std::make_unique<TestRandomAccessStream>(ciphertext); + util::StatusOr<std::unique_ptr<RandomAccessStream>> + decrypting_random_access_stream = + streaming_aead.NewDecryptingRandomAccessStream( + std::move(test_random_access_stream), "Some AAD"); + ASSERT_THAT(decrypting_random_access_stream.status(), IsOk()); + + int chunk_size = 10; + auto buffer = util::Buffer::New(chunk_size); + std::string plaintext; + int64_t position = 0; + util::Status status = (*decrypting_random_access_stream) + ->PRead(position, chunk_size, buffer->get()); + while (status.ok()) { + plaintext.append((*buffer)->get_mem_block(), (*buffer)->size()); + position += (*buffer)->size(); + status = (*decrypting_random_access_stream) + ->PRead(position, chunk_size, buffer->get()); + } + EXPECT_THAT(status, StatusIs(absl::StatusCode::kOutOfRange)); + plaintext.append((*buffer)->get_mem_block(), (*buffer)->size()); + EXPECT_EQ(stream_content, plaintext); +} + +TEST(DummyStreamingAead, DummyDecryptingStreamPreadWithSmallerHeaderFails) { + const int stream_size = 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + + auto ostream = std::make_unique<std::ostringstream>(); + auto output_stream = + std::make_unique<util::OstreamOutputStream>(std::move(ostream)); + + constexpr absl::string_view kStreamingAeadName = "Some AEAD"; + constexpr absl::string_view kStreamingAeadAad = "Some associated data"; + + DummyStreamingAead streaming_aead(kStreamingAeadName); + util::StatusOr<std::unique_ptr<OutputStream>> encrypting_output_stream = + streaming_aead.NewEncryptingStream(std::move(output_stream), + kStreamingAeadAad); + ASSERT_THAT(encrypting_output_stream.status(), IsOk()); + ASSERT_THAT(subtle::test::WriteToStream( + encrypting_output_stream.value().get(), stream_content), + IsOk()); + // Stream content size is too small; DummyDecryptingStream expects + // absl::StrCat(kStreamingAeadName, kStreamingAeadAad). + std::string ciphertext = "Invalid header"; + auto test_random_access_stream = + std::make_unique<TestRandomAccessStream>(ciphertext); + util::StatusOr<std::unique_ptr<RandomAccessStream>> + decrypting_random_access_stream = + streaming_aead.NewDecryptingRandomAccessStream( + std::move(test_random_access_stream), kStreamingAeadAad); + ASSERT_THAT(decrypting_random_access_stream.status(), IsOk()); + + int chunk_size = 10; + auto buffer = util::Buffer::New(chunk_size); + EXPECT_THAT( + (*decrypting_random_access_stream) + ->PRead(/*position=*/0, chunk_size, buffer->get()), + StatusIs(absl::StatusCode::kInvalidArgument, "Could not read header")); + EXPECT_THAT( + (*decrypting_random_access_stream) + ->PRead(/*position=*/0, chunk_size, buffer->get()), + StatusIs(absl::StatusCode::kInvalidArgument, "Could not read header")); + EXPECT_THAT( + (*decrypting_random_access_stream)->size().status(), + StatusIs(absl::StatusCode::kInvalidArgument, "Could not read header")); +} + +TEST(DummyStreamingAead, DummyDecryptingStreamPreadWithCorruptedAadFails) { + const int stream_size = 1024; + std::string stream_content = subtle::Random::GetRandomBytes(stream_size); + + auto ostream = std::make_unique<std::ostringstream>(); + auto string_stream_buffer = ostream->rdbuf(); + auto output_stream = + std::make_unique<util::OstreamOutputStream>(std::move(ostream)); + + constexpr absl::string_view kStreamingAeadName = "Some AEAD"; + constexpr absl::string_view kStreamingAeadAad = "Some associated data"; + + DummyStreamingAead streaming_aead(kStreamingAeadName); + util::StatusOr<std::unique_ptr<OutputStream>> encrypting_output_stream = + streaming_aead.NewEncryptingStream(std::move(output_stream), + kStreamingAeadAad); + ASSERT_THAT(encrypting_output_stream.status(), IsOk()); + ASSERT_THAT(subtle::test::WriteToStream( + encrypting_output_stream.value().get(), stream_content), + IsOk()); + // Invalid associated data. + std::string ciphertext = string_stream_buffer->str(); + auto test_random_access_stream = + std::make_unique<TestRandomAccessStream>(ciphertext); + util::StatusOr<std::unique_ptr<RandomAccessStream>> + decrypting_random_access_stream = + streaming_aead.NewDecryptingRandomAccessStream( + std::move(test_random_access_stream), "Some wrong AAD"); + ASSERT_THAT(decrypting_random_access_stream.status(), IsOk()); + + int chunk_size = 10; + auto buffer = util::Buffer::New(chunk_size); + EXPECT_THAT((*decrypting_random_access_stream) + ->PRead(/*position=*/0, chunk_size, buffer->get()), + StatusIs(absl::StatusCode::kInvalidArgument, "Corrupted header")); + EXPECT_THAT((*decrypting_random_access_stream) + ->PRead(/*position=*/0, chunk_size, buffer->get()), + StatusIs(absl::StatusCode::kInvalidArgument, "Corrupted header")); + EXPECT_THAT((*decrypting_random_access_stream)->size().status(), + StatusIs(absl::StatusCode::kInvalidArgument, "Corrupted header")); +} + } // namespace } // namespace test } // namespace tink
diff --git a/cc/util/validation_test.cc b/cc/util/validation_test.cc index 1d5b01c..db7d2d2 100644 --- a/cc/util/validation_test.cc +++ b/cc/util/validation_test.cc
@@ -16,6 +16,8 @@ #include "tink/util/validation.h" +#include <limits> + #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/status/status.h"
diff --git a/cc/version_script.lds b/cc/version_script.lds index fbee2ab..6684cbf 100644 --- a/cc/version_script.lds +++ b/cc/version_script.lds
@@ -1,4 +1,4 @@ -VERS_1.7.0 { +VERS_2.0.0 { global: *tink*; *absl*;
diff --git a/cmake/HttpArchive.cmake b/cmake/HttpArchive.cmake index d13a103..75fd0d3 100644 --- a/cmake/HttpArchive.cmake +++ b/cmake/HttpArchive.cmake
@@ -12,21 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -include(ExternalProject) +include(FetchContent) include(CMakeParseArguments) -if (NOT DEFINED TINK_THIRD_PARTY_DIR) - set(TINK_THIRD_PARTY_DIR "${CMAKE_CURRENT_BINARY_DIR}/__third_party") -endif() - # Download, unpack and configure a dependency. # # The project is added as a subdirectory of Tink, unless DATA_ONLY is # specified. This makes all target defined by it available as dependencies. # -# This rule also defines a <NAME>_SOURCE_DIR variable, which points to the -# root directory of the downloaded package and can be used to reference data in -# tests, or append extra include/link paths in the Workspace file. +# This rule also defines two variables: +# - <NAME>_SOURCE_DIR points to the root directory of the downloaded package; +# it can be used to reference data in tests, or append extra include/link +# paths in the Workspace file. +# - <NAME>_BINARY_DIR points to the build directory. # # Parameters: # NAME name of the dependency. @@ -49,40 +47,29 @@ "NAME;URL;SHA256;CMAKE_SUBDIR" "CMAKE_ARGS" ) - + FetchContent_Declare( + ${http_archive_NAME} + URL ${http_archive_URL} + URL_HASH SHA256=${http_archive_SHA256} + ) message(STATUS "Fetching ${http_archive_NAME}") - - set(http_archive_PREFIX "${TINK_THIRD_PARTY_DIR}/${http_archive_NAME}") - set(http_archive_SOURCE_DIR "${http_archive_PREFIX}/src") - set(http_archive_BINARY_DIR "${http_archive_PREFIX}/build") - - configure_file( - cmake/HttpArchiveDownloader.cmake.in - "${http_archive_PREFIX}/CMakeLists.txt") - - execute_process( - COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE errors - WORKING_DIRECTORY "${http_archive_PREFIX}") - - if (errors) - message(FATAL_ERROR "While configuring ${http_archive_NAME}: ${errors}") - endif() - - set(${http_archive_NAME}_SOURCE_DIR "${http_archive_SOURCE_DIR}" PARENT_SCOPE) - - execute_process( - COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE errors - WORKING_DIRECTORY "${http_archive_PREFIX}") - - if (errors) - message(FATAL_ERROR "While fetching ${http_archive_NAME}: ${errors}") - endif() - - if (NOT http_archive_DATA_ONLY) - add_subdirectory( - "${http_archive_SOURCE_DIR}/${http_archive_CMAKE_SUBDIR}" - "${http_archive_BINARY_DIR}" EXCLUDE_FROM_ALL) + FetchContent_GetProperties(${http_archive_NAME}) + if(NOT ${http_archive_NAME}_POPULATED) + FetchContent_Populate(${http_archive_NAME}) + if (NOT http_archive_DATA_ONLY) + add_subdirectory( + ${${http_archive_NAME}_SOURCE_DIR}/${http_archive_CMAKE_SUBDIR} + ${${http_archive_NAME}_BINARY_DIR} + EXCLUDE_FROM_ALL) + endif() + # Expose these variables to the caller. + set( + "${http_archive_NAME}_SOURCE_DIR" + "${${http_archive_NAME}_SOURCE_DIR}" + PARENT_SCOPE) + set( + "${http_archive_NAME}_BINARY_DIR" + "${${http_archive_NAME}_BINARY_DIR}" + PARENT_SCOPE) endif() endfunction(http_archive)
diff --git a/cmake/HttpArchiveDownloader.cmake.in b/cmake/HttpArchiveDownloader.cmake.in deleted file mode 100644 index 3aac815..0000000 --- a/cmake/HttpArchiveDownloader.cmake.in +++ /dev/null
@@ -1,37 +0,0 @@ -# Copyright 2019 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. - -# This template is used by http_archive() to download, unpack and configure Tink -# dependencies. You shouldn't need to use it directly. - -cmake_minimum_required(VERSION 3.5) -project(http-archive-${http_archive_NAME}) - -include(ExternalProject) - -ExternalProject_Add(${http_archive_NAME} - URL "${http_archive_URL}" - URL_HASH SHA256=${http_archive_SHA256} - TLS_VERIFY true - SOURCE_DIR "${http_archive_SOURCE_DIR}" - BINARY_DIR "${http_archive_BINARY_DIR}" - SOURCE_SUBDIR "${http_archive_CMAKE_SUBDIR}" - CMAKE_ARGS ${http_archive_CMAKE_ARGS} - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - TEST_COMMAND "" - INSTALL_COMMAND "" - ${http_archive_EXTRA_OPTIONS} - EXCLUDE_FROM_ALL -)
diff --git a/cmake/TinkBuildRules.cmake b/cmake/TinkBuildRules.cmake index 2a6ef1f..9fd4328 100644 --- a/cmake/TinkBuildRules.cmake +++ b/cmake/TinkBuildRules.cmake
@@ -43,7 +43,7 @@ endif() if (NOT DEFINED TINK_CXX_STANDARD) - set(TINK_CXX_STANDARD 11) + set(TINK_CXX_STANDARD 14) if (DEFINED CMAKE_CXX_STANDARD_REQUIRED AND CMAKE_CXX_STANDARD_REQUIRED AND DEFINED CMAKE_CXX_STANDARD) set(TINK_CXX_STANDARD ${CMAKE_CXX_STANDARD}) endif() @@ -54,6 +54,7 @@ set(TINK_IDE_FOLDER "Tink") set(TINK_TARGET_EXCLUDE_IF_OPENSSL "exclude_if_openssl") +set(TINK_TARGET_EXCLUDE_IF_WINDOWS "exclude_if_windows") # Declare the beginning of a new Tink library namespace. # @@ -74,10 +75,12 @@ # a way to organise code and speed up compilation. # # Arguments: -# NAME base name of the target. See below for target naming conventions. -# SRCS list of source files, including headers. -# DEPS list of dependency targets. -# PUBLIC flag, signal that this target is intended for external use. +# NAME base name of the target. See below for target naming conventions. +# SRCS list of source files, including headers. +# DEPS list of dependency targets. +# PUBLIC flag, signals that this target is intended for external use. +# TESTONLY flag, signals that this target should be ignored if +# TINK_BUILD_TESTS=OFF. # # If SRCS contains only headers, an INTERFACE rule is created. This rule carries # include path and link library information, but is not directly buildable. @@ -94,22 +97,30 @@ # function(tink_cc_library) cmake_parse_arguments(PARSE_ARGV 0 tink_cc_library - "PUBLIC" + "PUBLIC;TESTONLY" "NAME" "SRCS;DEPS;TAGS" ) + if (tink_cc_library_TESTONLY AND NOT TINK_BUILD_TESTS) + return() + endif() + if (NOT DEFINED TINK_MODULE) message(FATAL_ERROR "TINK_MODULE not defined, perhaps you are missing a tink_module() statement?") endif() - # Check if this target must be skipped. Currently the only reason for this to - # happen is incompatibility with OpenSSL, when used. + # Check if this target must be skipped. foreach(_tink_cc_library_tag ${tink_cc_library_TAGS}) + # Exclude if we use OpenSSL. if (${_tink_cc_library_tag} STREQUAL ${TINK_TARGET_EXCLUDE_IF_OPENSSL} AND TINK_USE_SYSTEM_OPENSSL) return() endif() + # Exclude if building on Windows. + if (${_tink_cc_library_tag} STREQUAL ${TINK_TARGET_EXCLUDE_IF_WINDOWS} AND WIN32) + return() + endif() endforeach() # We replace :: with __ in targets, because :: may not appear in target names. @@ -157,10 +168,10 @@ # Declare a Tink test using googletest, with a syntax similar to Bazel. # # Parameters: -# NAME base name of the test. -# SRCS list of test source files, headers included. -# DEPS list of dependencies, see tink_cc_library above. -# DATA list of non-code dependencies, such as test vectors. +# NAME base name of the test. +# SRCS list of test source files, headers included. +# DEPS list of dependencies, see tink_cc_library above. +# DATA list of non-code dependencies, such as test vectors. # # Tests added with this macro are automatically registered. # Each test produces a build target named tink_test_<MODULE>_<NAME>. @@ -180,12 +191,16 @@ message(FATAL_ERROR "TINK_MODULE not defined") endif() - # Check if this target must be skipped. Currently the only reason for this to - # happen is incompatibility with OpenSSL, when used. + # Check if this target must be skipped. foreach(_tink_cc_test_tag ${tink_cc_test_TAGS}) + # Exclude if we use OpenSSL. if (${_tink_cc_test_tag} STREQUAL ${TINK_TARGET_EXCLUDE_IF_OPENSSL} AND TINK_USE_SYSTEM_OPENSSL) return() endif() + # Exclude if building on Windows. + if (${_tink_cc_test_tag} STREQUAL ${TINK_TARGET_EXCLUDE_IF_WINDOWS} AND WIN32) + return() + endif() endforeach() # We replace :: with __ in targets, because :: may not appear in target names. @@ -276,8 +291,8 @@ # to group dependencies that are logically related and give them a single name. # # Parameters: -# NAME base name of the target. -# DEPS list of dependencies to group. +# NAME base name of the target. +# DEPS list of dependencies to group. # # Each tink_target_group produces a target named tink_<MODULE>_<NAME>. function(tink_target_group)
diff --git a/cmake/TinkWorkspace.cmake b/cmake/TinkWorkspace.cmake index 2404a5d..f871893 100644 --- a/cmake/TinkWorkspace.cmake +++ b/cmake/TinkWorkspace.cmake
@@ -50,27 +50,40 @@ set(gtest_force_shared_crt ON CACHE BOOL "Tink dependency override" FORCE) -if (NOT TINK_USE_INSTALLED_GOOGLETEST) +if (TINK_BUILD_TESTS) + if (TINK_USE_INSTALLED_GOOGLETEST) + # This uses the CMake's FindGTest module; if successful, this call to + # find_package generates the targets GTest::gmock, GTest::gtest and + # GTest::gtest_main. + find_package(GTest CONFIG REQUIRED) + _create_interface_target(gmock GTest::gmock) + _create_interface_target(gtest_main GTest::gtest_main) + else() + http_archive( + NAME googletest + URL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz + SHA256 b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5 + ) + endif() + http_archive( - NAME com_google_googletest - URL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz - SHA256 b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5 + NAME wycheproof + URL https://github.com/google/wycheproof/archive/d8ed1ba95ac4c551db67f410c06131c3bc00a97c.zip + SHA256 eb1d558071acf1aa6d677d7f1cabec2328d1cf8381496c17185bd92b52ce7545 + DATA_ONLY ) -else() - # This uses the CMake's FindGTest module; if successful, this call to - # find_package generates the targets GTest::gmock, GTest::gtest and - # GTest::gtest_main. - find_package(GTest CONFIG REQUIRED) - _create_interface_target(gmock GTest::gmock) - _create_interface_target(gtest_main GTest::gtest_main) + # Symlink the Wycheproof test data. + # Tests expect Wycheproof test vectors to be in a local testvectors/ folder. + add_directory_alias("${wycheproof_SOURCE_DIR}/testvectors" + "${CMAKE_BINARY_DIR}/testvectors") endif() if (NOT TINK_USE_INSTALLED_ABSEIL) - # Commit from 2021-12-03 + # Release from 2023-05-04. http_archive( - NAME com_google_absl - URL https://github.com/abseil/abseil-cpp/archive/9336be04a242237cd41a525bedfcf3be1bb55377.zip - SHA256 368be019fc8d69a566ac2cf7a75262d5ba8f6409e3ef3cdbcf0106bdeb32e91c + NAME abseil + URL https://github.com/abseil/abseil-cpp/archive/refs/tags/20230125.3.zip + SHA256 51d676b6846440210da48899e4df618a357e6e44ecde7106f1e44ea16ae8adc7 ) else() # This is everything that needs to be done here. Abseil already defines its @@ -78,31 +91,32 @@ find_package(absl REQUIRED) endif() -http_archive( - NAME wycheproof - URL https://github.com/google/wycheproof/archive/d8ed1ba95ac4c551db67f410c06131c3bc00a97c.zip - SHA256 eb1d558071acf1aa6d677d7f1cabec2328d1cf8381496c17185bd92b52ce7545 - DATA_ONLY -) - -# Symlink the Wycheproof test data. -# Paths are hard-coded in tests, which expects wycheproof/ in this location. -add_directory_alias("${wycheproof_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/external/wycheproof") - -if (NOT TINK_USE_SYSTEM_OPENSSL) - http_archive( - NAME boringssl - URL https://github.com/google/boringssl/archive/88cdf7dd2dbce1ecb9057c183095103d83373abe.zip - SHA256 24092815136f956069fcfa5172166ad4e025166ce6fe500420c9e3e3c4f3da38 - CMAKE_SUBDIR src - ) - - # BoringSSL targets do not carry include directory info, this fixes it. - target_include_directories(crypto PUBLIC "${boringssl_SOURCE_DIR}/src/include") +# Don't fetch BoringSSL or look for OpenSSL if target `crypto` is already +# defined. +if (NOT TARGET crypto) + if (NOT TINK_USE_SYSTEM_OPENSSL) + # Commit from Feb 15, 2023. + # NOTE: This is one commit ahead of Bazel; the commit fixes a CMake issue, + # which made build fail on CMake 3.10. + # See https://github.com/google/boringssl/compare/5c22014...e27ff0e. + http_archive( + NAME boringssl + URL https://github.com/google/boringssl/archive/e27ff0e4312c91357778b36bbd8a7ec7bfc67be3.zip + SHA256 11d3c87906bed215a915b0db11cefd0fc7b939ddbec4952a29e343a83ce3bc50 + CMAKE_SUBDIR src + ) + # BoringSSL targets do not carry include directory info, this fixes it. + target_include_directories(crypto PUBLIC + "$<BUILD_INTERFACE:${boringssl_SOURCE_DIR}/src/include>") + else() + # Support for ED25519 was added from 1.1.1. + find_package(OpenSSL 1.1.1 REQUIRED) + _create_interface_target(crypto OpenSSL::Crypto) + endif() else() - # Support for ED25519 was added from 1.1.1. - find_package(OpenSSL 1.1.1 REQUIRED) - _create_interface_target(crypto OpenSSL::Crypto) + message(STATUS "Using an already declared `crypto` target") + get_target_property(crypto_INCLUDE_DIR crypto INTERFACE_INCLUDE_DIRECTORIES) + message(STATUS "crypto Include Dir: ${crypto_INCLUDE_DIR}") endif() set(RAPIDJSON_BUILD_DOC OFF CACHE BOOL "Tink dependency override" FORCE) @@ -114,17 +128,16 @@ URL https://github.com/Tencent/rapidjson/archive/v1.1.0.tar.gz SHA256 bf7ced29704a1e696fbccf2a2b4ea068e7774fa37f6d7dd4039d0787f8bed98e ) - # Rapidjson is a header-only library with no explicit target. Here we create one. add_library(rapidjson INTERFACE) target_include_directories(rapidjson INTERFACE "${rapidjson_SOURCE_DIR}") set(protobuf_BUILD_TESTS OFF CACHE BOOL "Tink dependency override" FORCE) set(protobuf_BUILD_EXAMPLES OFF CACHE BOOL "Tink dependency override" FORCE) - +## Use protobuf X.21.9. http_archive( NAME com_google_protobuf - URL https://github.com/protocolbuffers/protobuf/archive/v3.19.3.zip - SHA256 6b6bf5cd8d0cca442745c4c3c9f527c83ad6ef35a405f64db5215889ac779b42 + URL https://github.com/protocolbuffers/protobuf/archive/v21.9.zip + SHA256 5babb8571f1cceafe0c18e13ddb3be556e87e12ceea3463d6b0d0064e6cc1ac3 CMAKE_SUBDIR cmake )
diff --git a/docs/CMAKE-HOWTO.md b/docs/CMAKE-HOWTO.md index 91e30eb..95d5664 100644 --- a/docs/CMAKE-HOWTO.md +++ b/docs/CMAKE-HOWTO.md
@@ -13,10 +13,10 @@ source tree has been copied in the `third_party/tink` directory of your project, your top-level CMake script should look like this: - cmake_minimum_required(VERSION 3.5) + cmake_minimum_required(VERSION 3.13) project(YourProject CXX) set(CMAKE_CXX_STANDARD_REQUIRED ON) - set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD 14) add_subdirectory(third_party/tink) @@ -25,9 +25,9 @@ NOTES: -* You need at least CMake 3.5 to build Tink and its dependencies. +* You need at least CMake 3.13 to build Tink and its dependencies. * Tink defines the C++ standard to use via the `TINK_CXX_STANDARD` variable, - which is `11` by default. If you want to propagate to the value of + which is `14` by default. If you want to propagate to the value of `CMAKE_CXX_STANDARD` to Tink use `set(CMAKE_CXX_STANDARD_REQUIRED ON)`. Include Tink headers in `your_app.cc` as follows:
diff --git a/docs/CPP-HOWTO.md b/docs/CPP-HOWTO.md index 7e237c3..dac6f46 100644 --- a/docs/CPP-HOWTO.md +++ b/docs/CPP-HOWTO.md
@@ -11,9 +11,9 @@ ### Bazel -Using Tink in projects built with Bazel is straightforward and is the recommended -approach. For reference, see [the C++ -examples](https://github.com/google/tink/tree/master/examples/cc). +Using Tink in projects built with Bazel is straightforward and is the +recommended approach. For reference, see +[the C++ examples](https://github.com/google/tink/tree/master/cc/examples). ### CMake
diff --git a/docs/GOLANG-HOWTO.md b/docs/GOLANG-HOWTO.md index 8f25527..15b8935 100644 --- a/docs/GOLANG-HOWTO.md +++ b/docs/GOLANG-HOWTO.md
@@ -115,36 +115,37 @@ ) func main() { - // Generate a new key. - kh1, err := keyset.NewHandle(aead.AES128GCMKeyTemplate()) + // Generate a new keyset handle. + handle1, err := keyset.NewHandle(aead.AES128GCMKeyTemplate()) if err != nil { log.Fatal(err) } - // Fetch the master key from a KMS. + // Get the key encryption AEAD from a KMS. gcpClient, err := gcpkms.NewClientWithCredentials(keyURI, credentialsPath) if err != nil { log.Fatal(err) } registry.RegisterKMSClient(gcpClient) - masterKey, err := gcpClient.GetAEAD(keyURI) + keyEncryptionAEAD, err := gcpClient.GetAEAD(keyURI) if err != nil { log.Fatal(err) } - // An io.Reader and io.Writer implementation which simply writes to memory. - memKeyset := &keyset.MemReaderWriter{} - - // Write encrypts the keyset handle with the master key and writes to the - // io.Writer implementation (memKeyset). We recommend that you encrypt the - // keyset handle before persisting it. - if err := kh1.Write(memKeyset, masterKey); err != nil { + // Serialize and encrypt the keyset handle using the key encryption AEAD. + // We strongly recommend that you encrypt the keyset handle before persisting + // it. + buf := new(bytes.Buffer) + writer := keyset.NewBinaryWriter(buf) + err = handle1.Write(writer, keyEncryptionAEAD) + if err != nil { log.Fatal(err) } + encryptedHandle := buf.Bytes() - // Read reads the encrypted keyset handle back from the io.Reader - // implementation and decrypts it using the master key. - kh2, err := keyset.Read(memKeyset, masterKey) + // Decrypt and parse the encrypted keyset using the key encryption AEAD. + reader := keyset.NewBinaryReader(bytes.NewReader(encryptedHandle)) + handle2, err := keyset.Read(reader, keyEncryptionAEAD) if err != nil { log.Fatal(err) }
diff --git a/docs/JAVA-HOWTO.md b/docs/JAVA-HOWTO.md index b502318..21056a0 100644 --- a/docs/JAVA-HOWTO.md +++ b/docs/JAVA-HOWTO.md
@@ -1,134 +1,23 @@ # Tink for Java HOW-TO This document contains instructions and Java code snippets for common tasks in -[Tink](https://github.com/google/tink). +[Tink](https://github.com/tink-crypto/tink-java). If you want to contribute code to the Java implementation, please read the [Java hacking guide](JAVA-HACKING.md). ## Setup instructions -The most recent release is -[1.7.0](https://github.com/google/tink/releases/tag/v1.7.0), released -2022-08-09. - -In addition to the versioned releases, snapshots of Tink are regularly built -using the master branch of the Tink GitHub repository. - -Tink for Java has two primary build targets specified: - -- "tink": the default, for general purpose use -- "android": which is optimized for use in Android projects - -### Maven - -You can can include Tink in Java projects projects using -[Maven](https://maven.apache.org/). - -The Maven group ID is `com.google.crypto.tink`, and the artifact ID is `tink`. - -You can specify the current release of Tink as a project dependency using the -following configuration: - -```xml -<dependencies> - <dependency> - <groupId>com.google.crypto.tink</groupId> - <artifactId>tink</artifactId> - <version>1.7.0</version> - </dependency> -</dependencies> -``` - -You can specify the latest snapshot as a project dependency by using the version -`HEAD-SNAPSHOT`: - -```xml -<repositories> - <repository> - <id>sonatype-snapshots</id> - <name>sonatype-snapshots</name> - <url>https://oss.sonatype.org/content/repositories/snapshots/</url> - <snapshots> - <enabled>true</enabled> - <updatePolicy>always</updatePolicy> - </snapshots> - <releases> - <updatePolicy>always</updatePolicy> - </releases> - </repository> -</repositories> - -... - -<dependencies> - <dependency> - <groupId>com.google.crypto.tink</groupId> - <artifactId>tink</artifactId> - <version>HEAD-SNAPSHOT</version> - </dependency> -</dependencies> -``` - -### AWS/GCP integration - -Since 1.3.0 the support for AWS/GCP KMS has been moved to a separate package. To -use AWS KMS, one should also add dependency on `tink-awskms`, and similarly -`tink-gcpkms` for GCP KMS. - -```xml -<dependencies> - <dependency> - <groupId>com.google.crypto.tink</groupId> - <artifactId>tink-awskms</artifactId> - <version>1.7.0</version> - </dependency> -</dependencies> -``` - -```xml -<dependencies> - <dependency> - <groupId>com.google.crypto.tink</groupId> - <artifactId>tink-gcpkms</artifactId> - <version>1.7.0</version> - </dependency> -</dependencies> -``` - -### Gradle - -You can include Tink in Android projects using [Gradle](https://gradle.org). - -You can specify the current release of Tink as a project dependency using the -following configuration: - -``` -dependencies { - implementation 'com.google.crypto.tink:tink-android:1.7.0' -} -``` - -You can specify the latest snapshot as a project dependency using the following -configuration: - -``` -repositories { - maven { url "https://oss.sonatype.org/content/repositories/snapshots" } -} - -dependencies { - implementation 'com.google.crypto.tink:tink-android:HEAD-SNAPSHOT' -} -``` +See https://developers.devsite.corp.google.com/tink/tink-setup#java for setup +instructions. ## API documentation * Java: - * [1.7.0](https://google.github.io/tink/javadoc/tink/1.7.0) + * [1.9.0](https://google.github.io/tink/javadoc/tink/1.9.0) * [HEAD-SNAPSHOT](https://google.github.io/tink/javadoc/tink/HEAD-SNAPSHOT) * Android: - * [1.7.0](https://google.github.io/tink/javadoc/tink-android/1.7.0) + * [1.9.0](https://google.github.io/tink/javadoc/tink-android/1.9.0) * [HEAD-SNAPSHOT](https://google.github.io/tink/javadoc/tink-android/HEAD-SNAPSHOT) ## Important warnings @@ -188,97 +77,48 @@ Still, if there is a need to generate a KeysetHandle with fresh key material directly in Java code, you can use -[`KeysetHandle`](https://github.com/google/tink/blob/master/java_src/src/main/java/com/google/crypto/tink/KeysetHandle.java). +[`KeysetHandle`](https://github.com/tink-crypto/tink-java/blob/main/src/main/java/com/google/crypto/tink/KeysetHandle.java). For example, you can generate a keyset containing a randomly generated AES128-GCM key as follows. ```java - import com.google.crypto.tink.KeyTemplates; import com.google.crypto.tink.KeysetHandle; + import com.google.crypto.tink.aead.PredefinedAeadParameters; KeysetHandle keysetHandle = KeysetHandle.generateNew( - KeyTemplates.get("AES128_GCM")); + PredefinedAeadParameters.AES128_GCM); ``` -## Storing keysets +## Serializing keysets -After generating key material, you might want to persist it to a storage system, -e.g., writing to a file: +After generating key material, you might want to serialize it in order to +persist it to a storage system, e.g., writing to a file. ```java - import com.google.crypto.tink.CleartextKeysetHandle; + import com.google.crypto.tink.InsecureSecretKeyAccess; import com.google.crypto.tink.KeysetHandle; - import com.google.crypto.tink.KeyTemplates; - import com.google.crypto.tink.JsonKeysetWriter; - import java.io.File; + import com.google.crypto.tink.TinkJsonProtoKeysetFormat; + import com.google.crypto.tink.aead.PredefinedAeadParameters; + import java.nio.Files; // Generate the key material... KeysetHandle keysetHandle = KeysetHandle.generateNew( - KeyTemplates.get("AES128_GCM")); + PredefinedAeadParameters.AES128_GCM); - // and write it to a file. + // and serialize it to a string. String keysetFilename = "my_keyset.json"; - CleartextKeysetHandle.write(keysetHandle, JsonKeysetWriter.withFile( - new File(keysetFilename))); + String serializedKeyset = + TinkJsonProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get()); ``` -Storing cleartext keysets on disk is not recommended. Tink supports encrypting -keysets with master keys stored in remote [key management -systems](KEY-MANAGEMENT.md). +Parsing can be done with `TinkJsonProtoKeysetFormat.parseKeyset`. If the keyset +has no secret key material, the method `serializeKeysetWithoutSecret` can be +used (which does not require `InsecureSecretKeyAccess`). -For example, you can encrypt the key material with a key stored in Google Cloud -KMS key as follows: - -```java - import com.google.crypto.tink.JsonKeysetWriter; - import com.google.crypto.tink.KeysetHandle; - import com.google.crypto.tink.KeyTemplates; - import com.google.crypto.tink.integration.gcpkms.GcpKmsClient; - import java.io.File; - - // Generate the key material... - KeysetHandle keysetHandle = KeysetHandle.generateNew( - KeyTemplates.get("AES128_GCM")); - - // and write it to a file... - String keysetFilename = "my_keyset.json"; - // encrypted with the this key in GCP KMS - String masterKeyUri = "gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar"; - keysetHandle.write(JsonKeysetWriter.withFile(new File(keysetFilename)), - new GcpKmsClient().getAead(masterKeyUri)); -``` - -## Loading existing keysets - -To load encrypted keysets, use -[`KeysetHandle`](https://github.com/google/tink/blob/master/java_src/src/main/java/com/google/crypto/tink/KeysetHandle.java): - -```java - import com.google.crypto.tink.JsonKeysetReader; - import com.google.crypto.tink.KeysetHandle; - import com.google.crypto.tink.integration.awskms.AwsKmsClient; - import java.io.File; - - String keysetFilename = "my_keyset.json"; - // The keyset is encrypted with the this key in AWS KMS. - String masterKeyUri = "aws-kms://arn:aws:kms:us-east-1:007084425826:key/84a65985-f868-4bfc-83c2-366618acf147"; - KeysetHandle keysetHandle = KeysetHandle.read( - JsonKeysetReader.withFile(new File(keysetFilename)), - new AwsKmsClient().getAead(masterKeyUri)); -``` - -To load cleartext keysets, use -[`CleartextKeysetHandle`](https://github.com/google/tink/blob/master/java_src/src/main/java/com/google/crypto/tink/CleartextKeysetHandle.java): - -```java - import com.google.crypto.tink.CleartextKeysetHandle; - import com.google.crypto.tink.KeysetHandle; - import java.io.File; - - String keysetFilename = "my_keyset.json"; - KeysetHandle keysetHandle = CleartextKeysetHandle.read( - JsonKeysetReader.withFile(new File(keysetFilename))); -``` +Storing keysets unencrypted on disk is not recommended. Tink supports encrypting +keysets with master keys stored in remote key management systems, see for +example +https://developers.devsite.corp.google.com/tink/client-side-encryption#java. ## Obtaining and using primitives @@ -305,12 +145,11 @@ ```java import com.google.crypto.tink.Aead; - import com.google.crypto.tink.KeysetHandle; - import com.google.crypto.tink.KeyTemplates; + import com.google.crypto.tink.aead.PredefinedAeadParameters; // 1. Generate the key material. KeysetHandle keysetHandle = KeysetHandle.generateNew( - KeyTemplates.get("AES128_GCM")); + PredefinedAeadParameters.AES128_GCM); // 2. Get the primitive. Aead aead = keysetHandle.getPrimitive(Aead.class); @@ -330,13 +169,12 @@ encrypt or decrypt data: ```java - import com.google.crypto.tink.DeterministicAead; + import com.google.crypto.tink.daead.PredefinedDeterministicAeadParameters; import com.google.crypto.tink.KeysetHandle; - import com.google.crypto.tink.KeyTemplates; // 1. Generate the key material. KeysetHandle keysetHandle = KeysetHandle.generateNew( - KeyTemplates.get("AES256_SIV")); + PredefinedDeterministicAeadParameters.AES256_SIV); // 2. Get the primitive. DeterministicAead daead = @@ -351,160 +189,21 @@ ### Symmetric key encryption of streaming data -You can obtain and use a -[Streaming AEAD](PRIMITIVES.md#streaming-authenticated-encryption-with-associated-data) -(Streaming Authenticated Encryption with Associated Data) primitive to encrypt -or decrypt data streams: - -```java - import com.google.crypto.tink.StreamingAead; - import com.google.crypto.tink.KeysetHandle; - import com.google.crypto.tink.KeyTemplates; - import java.nio.ByteBuffer; - import java.nio.channels.FileChannel; - import java.nio.channels.SeekableByteChannel; - import java.nio.channels.WritableByteChannel; - - // 1. Generate the key material. - KeysetHandle keysetHandle = KeysetHandle.generateNew( - KeyTemplates.get("AES128_GCM_HKDF_1MB")); - - // 2. Get the primitive. - StreamingAead streamingAead = keysetHandle.getPrimitive(StreamingAead.class); - - // 3. Use the primitive to encrypt some data and write the ciphertext to a file, - FileChannel ciphertextDestination = - new FileOutputStream(ciphertextFileName).getChannel(); - byte[] aad = ... - WritableByteChannel encryptingChannel = - streamingAead.newEncryptingChannel(ciphertextDestination, aad); - ByteBuffer buffer = ByteBuffer.allocate(chunkSize); - while ( bufferContainsDataToEncrypt ) { - int r = encryptingChannel.write(buffer); - // Try to get into buffer more data for encryption. - } - // Complete the encryption (process the remaining plaintext, if any, and close the channel). - encryptingChannel.close(); - - // ... or to decrypt an existing ciphertext stream. - FileChannel ciphertextSource = - new FileInputStream(ciphertextFileName).getChannel(); - byte[] aad = ... - ReadableByteChannel decryptingChannel = - s.newDecryptingChannel(ciphertextSource, aad); - ByteBuffer buffer = ByteBuffer.allocate(chunkSize); - do { - buffer.clear(); - int cnt = decryptingChannel.read(buffer); - if (cnt > 0) { - // Process cnt bytes of plaintext. - } else if (read == -1) { - // End of plaintext detected. - break; - } else if (read == 0) { - // No ciphertext is available at the moment. - } - } -``` +See +https://developers.devsite.corp.google.com/tink/encrypt-large-files-or-data-streams#java ### Message Authentication Code -You can compute or verify a [MAC](PRIMITIVES.md#message-authentication-code) -(Message Authentication Code): - -```java - import com.google.crypto.tink.KeysetHandle; - import com.google.crypto.tink.KeyTemplates; - import com.google.crypto.tink.Mac; - - // 1. Generate the key material. - KeysetHandle keysetHandle = KeysetHandle.generateNew( - KeyTemplates.get("HMAC_SHA256_128BITTAG")); - - // 2. Get the primitive. - Mac mac = keysetHandle.getPrimitive(Mac.class); - - // 3. Use the primitive to compute a tag, - byte[] tag = mac.computeMac(data); - - // ... or to verify a tag. - mac.verifyMac(tag, data); -``` +See +https://developers.devsite.corp.google.com/tink/protect-data-from-tampering#java ### Digital signatures -You can sign or verify a [digital -signature](PRIMITIVES.md#digital-signatures): - -```java - import com.google.crypto.tink.KeysetHandle; - import com.google.crypto.tink.KeyTemplates; - import com.google.crypto.tink.PublicKeySign; - import com.google.crypto.tink.PublicKeyVerify; - - // SIGNING - - // 1. Generate the private key material. - KeysetHandle privateKeysetHandle = KeysetHandle.generateNew( - KeyTemplates.get("ECDSA_P256")); - - // 2. Get the primitive. - PublicKeySign signer = privateKeysetHandle.getPrimitive(PublicKeySign.class); - - // 3. Use the primitive to sign. - byte[] signature = signer.sign(data); - - // VERIFYING - - // 1. Obtain a handle for the public key material. - KeysetHandle publicKeysetHandle = - privateKeysetHandle.getPublicKeysetHandle(); - - // 2. Get the primitive. - PublicKeyVerify verifier = publicKeysetHandle.getPrimitive(PublicKeyVerify.class); - - // 4. Use the primitive to verify. - verifier.verify(signature, data); -``` +See https://developers.devsite.corp.google.com/tink/digitally-sign-data ### Hybrid encryption -To encrypt or decrypt using [a combination of public key encryption and -symmetric key encryption](PRIMITIVES.md#hybrid-encryption) one can -use the following: - -```java - import com.google.crypto.tink.HybridDecrypt; - import com.google.crypto.tink.HybridEncrypt; - import com.google.crypto.tink.KeysetHandle; - import com.google.crypto.tink.KeyTemplates; - - // 1. Generate the private key material. - KeysetHandle privateKeysetHandle = KeysetHandle.generateNew( - KeyTemplates.get("ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_GCM")); - - // Obtain the public key material. - KeysetHandle publicKeysetHandle = - privateKeysetHandle.getPublicKeysetHandle(); - - // ENCRYPTING - - // 2. Get the primitive. - HybridEncrypt hybridEncrypt = - publicKeysetHandle.getPrimitive(HybridEncrypt.class); - - // 3. Use the primitive. - byte[] ciphertext = hybridEncrypt.encrypt(plaintext, contextInfo); - - // DECRYPTING - - // 2. Get the primitive. - HybridDecrypt hybridDecrypt = privateKeysetHandle.getPrimitive( - HybridDecrypt.class); - - // 3. Use the primitive. - byte[] plaintext = hybridDecrypt.decrypt(ciphertext, contextInfo); -``` +See https://developers.devsite.corp.google.com/tink/exchange-data#java ### Envelope encryption @@ -545,33 +244,29 @@ ## Key rotation Support for key rotation in Tink is provided via the -[`KeysetManager`](https://github.com/google/tink/blob/master/java_src/src/main/java/com/google/crypto/tink/KeysetManager.java) +[`KeysetHandle.Builder`](https://github.com/tink-crypto/tink-java/blob/main/src/main/java/com/google/crypto/tink/KeysetHandle.java) class. You have to provide a `KeysetHandle`-object that contains the keyset that should be rotated, and a specification of the new key via a -[`KeyTemplate`](https://github.com/google/tink/blob/master/proto/tink.proto#L50) -message. +[`Parameters`](https://github.com/tink-crypto/tink-java/blob/main/src/main/java/com/google/crypto/tink/Parameters.java) +object. ```java - import com.google.crypto.tink.KeyTemplate; - import com.google.crypto.tink.KeyTemplates; import com.google.crypto.tink.KeysetHandle; import com.google.crypto.tink.KeysetManager; KeysetHandle keysetHandle = ...; // existing keyset - KeyTemplate keyTemplate = KeyTemplates.get("AES256_GCM"); // template for the new key - - KeysetHandle rotatedKeysetHandle = KeysetManager - .withKeysetHandle(keysetHandle) - .rotate(keyTemplate) - .getKeysetHandle(); + KeysetHandle.Builder builder = KeysetHandle.newBuilder(keysetHandle); + builder.addEntry(KeysetHandle.generateEntryFromParameters( + ChaCha20Poly1305Parameters.create()).withRandomId()); + KeysetHandle keysetHandleWithAdditionalEntry = builder.build(); ``` After a successful rotation, the resulting keyset contains a new key generated -according to the specification in `keyTemplate`, and the new key becomes the -_primary key_ of the keyset. For the rotation to succeed the `Registry` must -contain a key manager for the key type specified in `keyTemplate`. +according to the specification in the parameters object. For the rotation to +succeed the `Registry` must contain a key manager for the key type specified in +`keyTemplate`. Alternatively, you can use [Tinkey](TINKEY.md) to rotate or manage a keyset.
diff --git a/docs/JWT-HOWTO.md b/docs/JWT-HOWTO.md index fdb947e..417c0ec 100644 --- a/docs/JWT-HOWTO.md +++ b/docs/JWT-HOWTO.md
@@ -170,7 +170,6 @@ Here are some small examples on how to use Tink's JWT library: -* [examples/cc/jwt](https://github.com/google/tink/tree/master/examples/cc/jwt) -* [examples/java_src/jwt](https://github.com/google/tink/tree/master/examples/java_src/jwt) -* [examples/python/jwt](https://github.com/google/tink/tree/master/examples/python/jwt) - +* [examples/cc/jwt](https://github.com/google/tink/tree/master/cc/examples/jwt) +* [examples/java_src/jwt](https://github.com/google/tink/tree/master/java_src/examples/jwt) +* [examples/python/jwt](https://github.com/google/tink/tree/master/python/examples/jwt)
diff --git a/docs/KNOWN-ISSUES.md b/docs/KNOWN-ISSUES.md index 7378857..3bbe819 100644 --- a/docs/KNOWN-ISSUES.md +++ b/docs/KNOWN-ISSUES.md
@@ -1,88 +1,3 @@ # Known Issues in Tink -This doc lists known issues in Tink. Please report new issues by opening new -tickets or emailing the maintainers at `tink-users@googlegroups.com`. - -## C++ - -* Before 1.4.0, AES-CTR-HMAC-AEAD keys and the - [EncryptThenAuthenticate](https://github.com/google/tink/blob/master/cc/subtle/encrypt_then_authenticate.cc) - subtle implementation may be vulnerable to chosen-ciphertext attacks. An - attacker can generate ciphertexts that bypass the HMAC verification if and - only if all of the following conditions are true: - - - Tink C++ is used on systems where `size_t` is a 32-bit integer. This is - usually the case on 32-bit machines. - - The attacker can specify long (>= 2^29 bytes ~ 536MB) associated data. - - This issue was reported by Quan Nguyen of Snap security team. - -## Java - -* Tink supports Java 8 or newer. Java 7 support was removed since 1.4.0. - -* Tink is built on top of Java security providers, but, via - [Project Wycheproof](https://github.com/google/wycheproof), we found many - security issues in popular providers. Tink provides countermeasures for most - problems, and we've also helped upstream fix many issues. Still, there are - some issues in old providers that we cannot fix. We recommend using Tink - with the latest version of Conscrypt, Oracle JDK, OpenJDK or Bouncy Castle. - If you cannot use the latest version, you might want to avoid using ECDSA - (alternative: ED25519) or AES-GCM (alternatives: AES-EAX, AES-CTR-HMAC-AEAD - or XChaCha20-Poly1305). - -## Android - -* The minimum API level that Tink supports is 19 (Android KitKat). This covers - more than 90% of all Android phones. Tink hasn't been tested on older - versions. It might or might not work. Drop us a line if you really need to - support ancient Android phones. - -* On Android Marshmallow (API level 23) or older, the - `newSeekableDecryptingChannel` method in implementations of `StreamingAead` - doesn't work. It depends on - [SeekableByteChannel](https://developer.android.com/reference/java/nio/channels/SeekableByteChannel.html), - which is only available on API level 24 or newer. Users should use - `newEncryptingStream` instead. - -* On Android Lollipop (API level 21) or older, `AndroidKeysetManager` does not - support wrapping keysets with Android Keystore, but it'd store keysets in - cleartext in private preference. This is secure enough for most - applications. - -* On Android KitKat (API level 19) without [Google Play - Services](https://developers.google.com/android/guides/overview), `AES-GCM` - does not work properly because KitKat uses Bouncy Castle 1.48 which doesn't - support updateAAD. If Google Play Services is present, `AES-GCM` should work - well. If you want to support all Android versions, without depending on - Google Play Services, please use `CHACHA20-POLY1305`, `AES-EAX`, or - `AES-CTR-HMAC-AEAD`. - -## Signature malleability - -* ECDSA signatures are malleable. You probably can ignore this issue, unless - you're working on Bitcoin or cryptocurrencies and have to worry about - [transaction - malleability](https://en.bitcoin.it/wiki/Transaction_malleability). In that - case you want to use ED25519 signatures which are non-malleable. - -## Envelope encryption - Benign malleability - -* Envelope encryption uses a third-party provider (e.g. GCP, AWS) to encrypt - the *data encryption key (DEK)*. It is possible to modify certain parts of - the *encrypted DEK* without detection when using *KmsEnvelopeAead* with - *AwsKmsAead* or *GcpKmsAead* as the remote provider. This is due to some - metadata being included (for instance version numbers) which is not - authenticated and modifications are not detected by the provider. Note that - this violates the CCA2 property for this interface, although the ciphertext - will still decrypt to the correct DEK. When using this interface one should - not rely on that for each DEK there only exists a single *encrypted DEK*. - -## Streaming AEAD - potential integer overflow issues - -* Streaming AEAD implementations encrypt the plaintext in segments. Tink uses - a 4-byte segment counter. When encrypting a stream consisting of more than - 2^32 segments, the segment counter might overflow and lead to leakage of key - material or plaintext. This problem was found in the Java and Go - implementations of the AES-GCM-HKDF-Streaming key type, and has been fixed - since 1.4.0. +See https://developers.google.com/tink/known-issues.
diff --git a/docs/PRIMITIVES.md b/docs/PRIMITIVES.md index 8fb0e23..841562d 100644 --- a/docs/PRIMITIVES.md +++ b/docs/PRIMITIVES.md
@@ -27,45 +27,44 @@ ### Primitives supported by language -**Primitive** | **Java** | **C++** | **Objective-C** | **Go** | **Python** ------------------- | :------: | :-----: | :-------------: | :----: | :--------: -AEAD | yes | yes | yes | yes | yes -Streaming AEAD | yes | yes | **no** | yes | yes -Deterministic AEAD | yes | yes | yes | yes | yes -MAC | yes | yes | yes | yes | yes -PRF | yes | yes | **no** | yes | yes -Digital signatures | yes | yes | yes | yes | yes -Hybrid encryption | yes | yes | yes | yes | yes +**Primitive** | **Java** | **C++** | **Objective-C** | **Go** | **Python** | **TypeScript** +------------------ | :------: | :-----: | :-------------: | :----: | :--------: | :------------: +AEAD | yes | yes | yes | yes | yes | yes +Streaming AEAD | yes | yes | **no** | yes | yes | **no** +Deterministic AEAD | yes | yes | yes | yes | yes | **no** +MAC | yes | yes | yes | yes | yes | **no** +PRF | yes | yes | **no** | yes | yes | **no** +Digital signatures | yes | yes | yes | yes | yes | yes +Hybrid encryption | yes | yes | yes | yes | yes | yes -JavaScript is currently under development. +TypeScript is still under development. ### Primitive implementations supported by language -| **Primitive** | **Implementation** | **Java** | **C++ (BoringSSL)** | **C++ (OpenSSL)** | **Objective-C** | **Go** | **Python** | -| ------------------ | --------------------------------- | :------: | :-----------------: | :---------------: | :-------------: | :--------: | :--------: | -| AEAD | AES-GCM | yes | yes | yes | yes | yes | yes | -| | AES-GCM-SIV | yes | yes | **no** | **no** | **no** | **no** | -| | AES-CTR-HMAC | yes | yes | yes | yes | yes | yes | -| | AES-EAX | yes | yes | yes | yes | **no** | yes | -| | KMS Envelope | yes | yes | yes | **no** | yes | yes | -| | CHACHA20-POLY1305 | yes | **no** | **no** | **no** | yes | **no** | -| | XCHACHA20-POLY1305 | yes | yes | **no** | yes | yes | yes | -| Streaming AEAD | AES-GCM-HKDF-STREAMING | yes | yes | yes | **no** | yes | yes | -| | AES-CTR-HMAC-STREAMING | yes | yes | yes | **no** | yes | yes | -| Deterministic AEAD | AES-SIV | yes | yes | yes | yes | yes | yes | -| MAC | HMAC-SHA2 | yes | yes | yes | yes | yes | yes | -| | AES-CMAC | yes | yes | yes | yes | yes | yes | -| PRF | HKDF-SHA2 | yes | yes | yes | **no** | yes | yes | -| | HMAC-SHA2 | yes | yes | yes | **no** | yes | yes | -| | AES-CMAC | yes | yes | yes | **no** | yes | yes | -| Digital Signatures | ECDSA over NIST curves | yes | yes | yes \* | yes | yes | yes | -| | Ed25519 | yes | yes | yes | yes | yes | yes | -| | RSA-SSA-PKCS1 | yes | yes | yes | yes | **no** | yes | -| | RSA-SSA-PSS | yes | yes | yes | yes | **no** | yes | -| Hybrid Encryption | HPKE | yes | yes | **no** | **no** | yes | yes | -| | ECIES with AEAD | yes | yes | yes | yes | yes | yes | -| | ECIES with DeterministicAEAD | yes | yes | yes | **no** | yes | yes | -| | HKDF | yes | yes | yes | yes | yes | yes | +| **Primitive** | **Implementation** | **Java** | **C++ (BoringSSL)** | **C++ (OpenSSL)** | **Objective-C** | **Go** | **Python** | **TypeScript** +| ------------------- | ------------------------------------- | :------: | :-----------------: | :---------------: | :-------------: | :----: | :--------: | :------------: +| AEAD | AES-GCM | yes | yes | yes | yes | yes | yes | yes +| | AES-GCM-SIV | yes | yes | **no** | **no** | **no** | **no** | **no** +| | AES-CTR-HMAC | yes | yes | yes | yes | yes | yes | yes +| | AES-EAX | yes | yes | yes | yes | **no** | yes | **no** +| | KMS Envelope | yes | yes | yes | **no** | yes | yes | **no** +| | CHACHA20-POLY1305 | yes | **no** | **no** | **no** | yes | **no** | **no** +| | XCHACHA20-POLY1305 | yes | yes | **no** | yes | yes | yes | **no** +| Streaming AEAD | AES-GCM-HKDF-STREAMING | yes | yes | yes | **no** | yes | yes | **no** +| | AES-CTR-HMAC-STREAMING | yes | yes | yes | **no** | yes | yes | **no** +| Deterministic AEAD | AES-SIV | yes | yes | yes | yes | yes | yes | **no** +| MAC | HMAC-SHA2 | yes | yes | yes | yes | yes | yes | **no** +| | AES-CMAC | yes | yes | yes | yes | yes | yes | **no** +| PRF | HKDF-SHA2 | yes | yes | yes | **no** | yes | yes | **no** +| | HMAC-SHA2 | yes | yes | yes | **no** | yes | yes | **no** +| | AES-CMAC | yes | yes | yes | **no** | yes | yes | **no** +| Digital Signatures | ECDSA over NIST curves | yes | yes | yes \* | yes | yes | yes | yes +| | Ed25519 | yes | yes | yes | yes | yes | yes | **no** +| | RSA-SSA-PKCS1 | yes | yes | yes | yes | yes | yes | **no** +| | RSA-SSA-PSS | yes | yes | yes | yes | yes | yes | **no** +| Hybrid Encryption | HPKE | yes | yes | **no** | **no** | yes | yes | **no** +| | ECIES with AEAD and HKDF | yes | yes | yes | yes | yes | yes | yes +| | ECIES with DeterministicAEAD and HKDF | yes | yes | yes | **no** | yes | yes | **no** \* EC key creation from seed (`DeriveKey`) is unsupported.
diff --git a/docs/TINKEY.md b/docs/TINKEY.md index 9c4804f..1fc9f17 100644 --- a/docs/TINKEY.md +++ b/docs/TINKEY.md
@@ -45,17 +45,20 @@ Available commands: +* `help`: Prints a help message for all available commands. * `add-key`: Generates and adds a new key to a keyset. * `convert-keyset`: Changes format, encrypts, decrypts a keyset. * `create-keyset`: Creates a new keyset. * `create-public-keyset`: Creates a public keyset from a private keyset. * `list-key-templates`: Lists all supported key templates. * `delete-key`: Deletes a specified key in a keyset. +* `destroy-key`: Destroys the key material of a specified key in a keyset. * `disable-key`: Disables a specified key in a keyset. * `enable-key`: Enables a specified key in a keyset. * `list-keyset`: Lists keys in a keyset. * `promote-key`: Promotes a specified key to primary. -* `rotate-keyset`: Performs a key rotation in a keyset. +* `rotate-keyset`: *Deprecated.* Rotate keysets in two steps using the commands + `add-key` and later `promote-key`. To obtain info about arguments available/required for a command, run `tinkey <command>` without further arguments. @@ -66,18 +69,11 @@ tinkey create-keyset --key-template ECDSA_P256 --out private-keyset.cfg ``` -- Add a new key to a keyset +- Add a new key to a keyset. This does not change the primary key. ```shell tinkey add-key --key-template ECDSA_P512 --in private-keyset.cfg \ ---out private-keyset.cfg -``` - -- Rotate a keyset by adding a primary key - -```shell -tinkey rotate-keyset --key-template ED25519 --in private-keyset.cfg \ ---out private-keyset.cfg + --out new-private-keyset.cfg ``` - List metadata of keys in a keyset: @@ -86,12 +82,25 @@ tinkey list-keyset --in private-keyset.cfg ``` +- Make the key with key ID 1234567890 the primary key. (You can get the key ID + using the `list-keyset` command.) + +```shell +tinkey promote-key --in private-keyset.cfg --key-id 1234567890 \ + --out new-private-keyset.cfg +``` + - Create a public keyset from a private keyset ```shell tinkey create-public-keyset --in private-keyset.cfg --out public-keyset.cfg ``` +Note: tinkey does not allow you to overwrite the keyset to prevent accidental +loss of existing keys. We recommend you always create a new keyset and +only delete the old keyset when you are certain that the new keyset works. + + ## Work with Key Management System (KMS) Tinkey can encrypt or decrypt keysets with master keys residing in remote KMSes. @@ -176,18 +185,20 @@ --new-master-key-uri gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar ``` -- Add a new key to an encrypted keyset, using default credentials +- Add a new key to an encrypted keyset, using default credentials. This does + not change the primary key. ```shell tinkey add-key --key-template AES256_GCM --in encrypted-keyset.cfg \ ---out encrypted-keyset.cfg \ + --out new-encrypted-keyset.cfg \ --master-key-uri gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar ``` -- Rotate an encrypted keyset by adding a primary key +- Make the key with key ID 1234567890 the primary key in an encrypted keyset. + (You can get the key ID using the `list-keyset` command.) ```shell -tinkey rotate-keyset --key-template AES256_GCM --in encrypted-keyset.cfg \ ---out encrypted-keyset.cfg \ +tinkey promote-key--in encrypted-keyset.cfg --key-id 1234567890 \ + --out new-encrypted-keyset.cfg \ --master-key-uri gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar ```
diff --git a/docs/TYPESCRIPT-HOWTO.md b/docs/TYPESCRIPT-HOWTO.md new file mode 100644 index 0000000..1fbd9ad --- /dev/null +++ b/docs/TYPESCRIPT-HOWTO.md
@@ -0,0 +1,143 @@ +# Tink for TypeScript HOW-TO + +This document presents instructions and TypeScript code snippets for common +tasks in [Tink](https://github.com/google/tink). + +Depending on the specifics of your build setup, you may need to alter these +snippets to use a different import syntax. Both ES modules and UMD are +supported. + +WARNING: Tink for TypeScript/JavaScript is still in an alpha state! Breaking +changes are likely. + +## Setup instructions + +To add Tink to a TypeScript/JavaScript project, just run: + +```sh +npm install tink-crypto +``` + +Or, if you're using Yarn: + +```sh +yarn add tink-crypto +``` + +## Generating new keys and keysets + +To take advantage of key rotation and other key management features, you usually +do not work with single keys, but with keysets, which you can use via a wrapper +called `KeysetHandle`. Keysets are just sets of keys with some additional +parameters and metadata. You can generate a new keyset and obtain its handle +using a `KeyTemplate` (example in below code snippet). + +To avoid accidental leakage of sensitive key material, you should usually avoid +mixing keyset generation and usage in code. To support the separation of these +activities Tink provides a command-line tool, [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 TypeScript code, you can use +`generateNewKeysetHandle`: + +```javascript +import {aead, generateNewKeysetHandle} from 'tink-crypto'; +const {aes256GcmKeyTemplate} = aead; + +(async () => { + const keyTemplate = aes256GcmKeyTemplate() + const keysetHandle = await generateNewKeysetHandle(keyTemplate); + // use the keyset... +})(); +``` + +Currently, key templates are only available for AEAD encryption, digital +signatures, and hybrid encryption. + +| Key | Key Template | +: Template : : +: Type : : +| --------- | ---------------------------------------------------------------- | +| AEAD | `aead.aes128GcmKeyTemplate()` | +| AEAD | `aead.aes256GcmKeyTemplate()` | +| AEAD | `aead.aes256GcmNoPrefixKeyTemplate()` | +| Signature | `signature.ecdsaP256KeyTemplate()` | +| Signature | `signature.ecdsaP256IeeeEncodingKeyTemplate()` | +| Signature | `signature.ecdsaP384KeyTemplate()` | +| Signature | `signature.ecdsaP384IeeeEncodingKeyTemplate()` | +| Signature | `signature.ecdsaP521KeyTemplate()` | +| Signature | `signature.ecdsaP521IeeeEncodingKeyTemplate()` | +| Hybrid | `hybrid.eciesP256HkdfHmacSha256Aes128GcmKeyTemplate()` | +| Hybrid | `hybrid.eciesP256HkdfHmacSha256Aes128CtrHmacSha256KeyTemplate()` | + +### Storing and loading existing keysets + +After generating key material, you might want to persist it to LocalStorage or +IndexedDB, or send it to a server to be stored there. The `binary` and +`binaryInsecure` subpackages can be used to serialize and deserialize keysets to +and from `UInt8Array`. `binary` handles only public keys; `binaryInsecure` can +additionally handle private and symmetric keys. With these, you must be careful +not to leak the raw key material. + +```javascript +import {aead, binaryInsecure, generateNewKeysetHandle} from 'tink-crypto'; + +const {Aead, register, aes256GcmKeyTemplate} = aead; +const {deserializeKeyset, serializeKeyset} = binaryInsecure; + +register(); + +(async () => { + const keysetHandle = await generateNewKeysetHandle(aes256GcmKeyTemplate()); + // Serialize keyset to send/store + const serializedKeyset = serializeKeyset(keysetHandle); + + const deserializedKeyset = deserializeKeyset(serializedKeyset) + const aead = await deserializedKeyset.getPrimitive(Aead); + // Use deserialization... (i.e. to decrypt a ciphertext) +})(); +``` + +## 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 you choose a desired implementation by using +a key of corresponding type (see the +[this section](KEY-MANAGEMENT.md#key-keyset-and-keysethandle) for details). + +A list of primitives and their implementations currently supported by Tink in +TypeScript/JavaScript can be found [here](PRIMITIVES.md#typescriptjavascript). +Note that there are currently a few additional limitations: + +* MAC is supported only via the subtle API, not the keyset API. +* It's not possible to generate a fresh new asymmetric keyset using the keyset + API and then use it without going through the subtle API. (The public key is + not directly accessible yet) + +### AEAD + +AEAD encryption assures the confidentiality and authenticity of the data. This +primitive is CPA secure. + +```javascript +// See live on StackBlitz: https://stackblitz.com/edit/tink-typescript?file=index.ts + +import {aead, generateNewKeysetHandle} from 'tink-crypto'; + +const {Aead, register, aes256GcmKeyTemplate} = aead; + +register(); + +(async () => { + const keysetHandle = await generateNewKeysetHandle(aes256GcmKeyTemplate()); + const aead = await keysetHandle.getPrimitive(Aead); + const ciphertext = await aead.encrypt( + new TextEncoder().encode('this data needs to be encrypted'), + new TextEncoder().encode('associated data')); + const plaintext = new TextDecoder().decode(await aead.decrypt( + ciphertext, new TextEncoder().encode('associated data'))); + console.log('Ciphertext:', ciphertext); + console.log('Plaintext:', plaintext); +})(); +```
diff --git a/docs/WIRE-FORMAT.md b/docs/WIRE-FORMAT.md index 5b171d1..6765d2c 100644 --- a/docs/WIRE-FORMAT.md +++ b/docs/WIRE-FORMAT.md
@@ -1,10 +1,5 @@ # Tink Wire Format -<!--* -# Document freshness: For more information, see go/fresh-source. -freshness: { owner: 'tink-dev' reviewed: '2022-04-12' } -*--> - Please see the [developer documentation](https://developers.google.com/tink/wire-format) for details on Tink's wire format for keys and primitive output.
diff --git a/examples/.bazelignore b/examples/.bazelignore deleted file mode 100644 index 70ff329..0000000 --- a/examples/.bazelignore +++ /dev/null
@@ -1,2 +0,0 @@ -cc -java_src
diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 5da3b38..0000000 --- a/examples/README.md +++ /dev/null
@@ -1,9 +0,0 @@ -# tink-examples - -These examples show how to use [Tink](https://github.com/google/tink) -to perform common crypto tasks. They also show how to add a dependency -on Tink using Maven, Gradle or Bazel. - -Subscribe to our -[mailing list](https://groups.google.com/forum/#!forum/tink-users) -if you have any questions.
diff --git a/examples/android/helloworld/app/build.gradle b/examples/android/helloworld/app/build.gradle deleted file mode 100644 index 6cc4b8e..0000000 --- a/examples/android/helloworld/app/build.gradle +++ /dev/null
@@ -1,45 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 26 - defaultConfig { - applicationId "com.helloworld" - minSdkVersion 19 - targetSdkVersion 26 - versionCode 1 - versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility 1.8 - targetCompatibility 1.8 - } -} - -apply from: "maven_${mavenLocation}.gradle" - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - // This is already included in protobuf-lite that Tink depends on. - exclude group: 'com.google.code.findbugs' - }) - implementation 'com.android.support:appcompat-v7:26.+' - implementation 'com.android.support:design:26.+' - testImplementation 'junit:junit:4.12' - - // Tink HEAD-SNAPSHOT for Android. - // In production apps, please use a named version, e.g., 1.4.0. - implementation 'com.google.crypto.tink:tink-android:HEAD-SNAPSHOT' - - // An artificial dependency to test whether Tink can co-exist with other - // protobuf dependencies. Please remove from production apps. - implementation 'com.google.protobuf:protobuf-lite:3.0.1' -}
diff --git a/examples/android/helloworld/app/src/main/java/com/helloworld/TinkApplication.java b/examples/android/helloworld/app/src/main/java/com/helloworld/TinkApplication.java deleted file mode 100644 index daefd70..0000000 --- a/examples/android/helloworld/app/src/main/java/com/helloworld/TinkApplication.java +++ /dev/null
@@ -1,56 +0,0 @@ -// Copyright 2017 Google Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////////// - -package com.helloworld; - -import android.app.Application; -import com.google.crypto.tink.Aead; -import com.google.crypto.tink.Config; -import com.google.crypto.tink.KeysetHandle; -import com.google.crypto.tink.aead.AeadKeyTemplates; -import com.google.crypto.tink.config.TinkConfig; -import com.google.crypto.tink.integration.android.AndroidKeysetManager; -import java.io.IOException; -import java.security.GeneralSecurityException; - -/** A custom application that initializes the Tink runtime at application startup. */ -public class TinkApplication extends Application { - private static final String TAG = TinkApplication.class.toString(); - private static final String PREF_FILE_NAME = "hello_world_pref"; - private static final String TINK_KEYSET_NAME = "hello_world_keyset"; - private static final String MASTER_KEY_URI = "android-keystore://hello_world_master_key"; - public Aead aead; - - @Override - public final void onCreate() { - super.onCreate(); - try { - Config.register(TinkConfig.TINK_1_0_0); - aead = getOrGenerateNewKeysetHandle().getPrimitive(Aead.class); - } catch (GeneralSecurityException | IOException e) { - throw new RuntimeException(e); - } - } - - private KeysetHandle getOrGenerateNewKeysetHandle() throws IOException, GeneralSecurityException { - return new AndroidKeysetManager.Builder() - .withSharedPref(getApplicationContext(), TINK_KEYSET_NAME, PREF_FILE_NAME) - .withKeyTemplate(AeadKeyTemplates.AES256_GCM) - .withMasterKeyUri(MASTER_KEY_URI) - .build() - .getKeysetHandle(); - } -}
diff --git a/examples/android/helloworld/build.gradle b/examples/android/helloworld/build.gradle deleted file mode 100644 index 4959c1c..0000000 --- a/examples/android/helloworld/build.gradle +++ /dev/null
@@ -1,25 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:3.6.0' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -}
diff --git a/examples/android/helloworld/gradle.properties b/examples/android/helloworld/gradle.properties deleted file mode 100644 index f874b2f..0000000 --- a/examples/android/helloworld/gradle.properties +++ /dev/null
@@ -1,23 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - -# Preferred source for Maven packages: -# "local": Local Maven repository (i.e. for testing without publishing) -# "snapshot": Maven Central snapshot repository (i.e. for testing published -# snapshots) -mavenLocation=snapshot
diff --git a/examples/android/helloworld/gradle/wrapper/gradle-wrapper.jar b/examples/android/helloworld/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 7a3265e..0000000 --- a/examples/android/helloworld/gradle/wrapper/gradle-wrapper.jar +++ /dev/null Binary files differ
diff --git a/examples/android/helloworld/gradle/wrapper/gradle-wrapper.properties b/examples/android/helloworld/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 8205549..0000000 --- a/examples/android/helloworld/gradle/wrapper/gradle-wrapper.properties +++ /dev/null
@@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
diff --git a/examples/android/helloworld/gradlew b/examples/android/helloworld/gradlew deleted file mode 100755 index cccdd3d..0000000 --- a/examples/android/helloworld/gradlew +++ /dev/null
@@ -1,172 +0,0 @@ -#!/usr/bin/env sh - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@"
diff --git a/examples/android/helloworld/gradlew.bat b/examples/android/helloworld/gradlew.bat deleted file mode 100644 index e95643d..0000000 --- a/examples/android/helloworld/gradlew.bat +++ /dev/null
@@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega
diff --git a/examples/objc/helloworld/README.md b/examples/objc/helloworld/README.md deleted file mode 100644 index 901ed51..0000000 --- a/examples/objc/helloworld/README.md +++ /dev/null
@@ -1,22 +0,0 @@ -# Obj-C Hello World - -This is an example iOS application that can encrypt and decrypt text using [AEAD -(Authenticated Encryption with Associated -Data)](../../../docs/PRIMITIVES.md#authenticated-encryption-with-associated-data). - -It demonstrates the basic steps of using Tink, namely generating key material, -obtaining a primitive, and using the primitive to do crypto. - -The example comes with a Podfile that demonstrates how to install Tink from Cocoapods. - -## Build and Run - -### Cocoapods - -```shell -git clone https://github.com/google/tink -cd tink/examples/objc/helloworld -pod install -open TinkExampleApp.xcworkspace -``` -
diff --git a/go/.bazelversion b/go/.bazelversion index ac14c3d..09b254e 100644 --- a/go/.bazelversion +++ b/go/.bazelversion
@@ -1 +1 @@ -5.1.1 +6.0.0
diff --git a/go/README.md b/go/README.md new file mode 100644 index 0000000..5d8c259 --- /dev/null +++ b/go/README.md
@@ -0,0 +1,7 @@ +# Tink Go + +This is the Go implementation of Tink v1.7.0. + +> **NOTE**: **This library has migrated to +> https://github.com/tink-crypto/tink-go +> ([godoc](https://pkg.go.dev/github.com/tink-crypto/tink-go/tink))**
diff --git a/go/WORKSPACE b/go/WORKSPACE index 060119d..526c81c 100644 --- a/go/WORKSPACE +++ b/go/WORKSPACE
@@ -10,6 +10,25 @@ ) # ------------------------------------------------------------------------- +# Bazel Skylib. +# ------------------------------------------------------------------------- +# Release from 2023-02-09 +# Protobuf vX.21.9 imports a version of bazel-skylib [1] that is incompatible +# with the one required by bazel-gazelle, so we make sure we have a newer +# version [2]. +# +# [1] https://github.com/protocolbuffers/protobuf/blob/90b73ac3f0b10320315c2ca0d03a5a9b095d2f66/protobuf_deps.bzl#L28 +# [2] https://github.com/bazelbuild/bazel-gazelle/issues/1290#issuecomment-1312809060 +http_archive( + name = "bazel_skylib", + sha256 = "b8a1527901774180afc798aeb28c4634bdccf19c4d98e7bdd1ce79d1fe9aaad7", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.1/bazel-skylib-1.4.1.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.1/bazel-skylib-1.4.1.tar.gz", + ], +) + +# ------------------------------------------------------------------------- # Wycheproof. # ------------------------------------------------------------------------- # Commit from 2019-12-17 @@ -29,12 +48,12 @@ # * @com_google_protobuf//:cc_toolchain # * @com_google_protobuf//:java_toolchain # This statement defines the @com_google_protobuf repo. -# Release from 2021-06-08. +# Release X.21.9 from 2022-10-26. http_archive( name = "com_google_protobuf", - sha256 = "6b6bf5cd8d0cca442745c4c3c9f527c83ad6ef35a405f64db5215889ac779b42", - strip_prefix = "protobuf-3.19.3", - urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.3.zip"], + strip_prefix = "protobuf-21.9", + urls = ["https://github.com/protocolbuffers/protobuf/archive/refs/tags/v21.9.zip"], + sha256 = "5babb8571f1cceafe0c18e13ddb3be556e87e12ceea3463d6b0d0064e6cc1ac3", ) load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") @@ -42,37 +61,32 @@ protobuf_deps() # ------------------------------------------------------------------------- -# Remote Build Execution (RBE). +# Bazel rules for Go. # ------------------------------------------------------------------------- -# Latest bazel_toolchains package on 2021-10-13. +# Release from 2023-04-20 http_archive( - name = "bazel_toolchains", - sha256 = "179ec02f809e86abf56356d8898c8bd74069f1bd7c56044050c2cd3d79d0e024", - strip_prefix = "bazel-toolchains-4.1.0", + name = "io_bazel_rules_go", + sha256 = "6dc2da7ab4cf5d7bfc7c949776b1b7c733f05e56edc4bcd9022bb249d2e2a996", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/4.1.0/bazel-toolchains-4.1.0.tar.gz", - "https://github.com/bazelbuild/bazel-toolchains/releases/download/4.1.0/bazel-toolchains-4.1.0.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.39.1/rules_go-v0.39.1.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.39.1/rules_go-v0.39.1.zip", ], ) -load("@bazel_toolchains//rules:rbe_repo.bzl", "rbe_autoconfig") - -rbe_autoconfig(name = "rbe_default") - # ------------------------------------------------------------------------- # Bazel Gazelle. # ------------------------------------------------------------------------- -# Release from 2021-10-11. +# Release from 2023-01-14 http_archive( name = "bazel_gazelle", - sha256 = "de69a09dc70417580aabf20a28619bb3ef60d038470c7cf8442fafcf627c21cb", + sha256 = "ecba0f04f96b4960a5b250c8e8eeec42281035970aa8852dda73098274d14a1d", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz", - "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.29.0/bazel-gazelle-v0.29.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.29.0/bazel-gazelle-v0.29.0.tar.gz", ], ) -load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") # ------------------------------------------------------------------------- # Tink Go Deps. @@ -82,34 +96,13 @@ # gazelle:repository_macro deps.bzl%go_dependencies go_dependencies() -# ------------------------------------------------------------------------- -# Bazel rules for Go. -# ------------------------------------------------------------------------- -# Release from 2022-03-21 -http_archive( - name = "io_bazel_rules_go", - sha256 = "f2dcd210c7095febe54b804bb1cd3a58fe8435a909db2ec04e31542631cf715c", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.31.0/rules_go-v0.31.0.zip", - "https://github.com/bazelbuild/rules_go/releases/download/v0.31.0/rules_go-v0.31.0.zip", - ], -) - load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") -# TODO(b/213404399): Remove after Gazelle issue is fixed. -go_repository( - name = "com_google_cloud_go_compute", - importpath = "cloud.google.com/go/compute", - sum = "h1:rSUBvAyVwNJ5uQCKNJFMwPtTvJkfN38b6Pvb9zZoqJ8=", - version = "v0.1.0", -) - go_rules_dependencies() go_register_toolchains( nogo = "@//:tink_nogo", - version = "1.17.6", + version = "1.19.9", ) gazelle_dependencies()
diff --git a/go/aead/BUILD.bazel b/go/aead/BUILD.bazel index 80e9e0b..33ef479 100644 --- a/go/aead/BUILD.bazel +++ b/go/aead/BUILD.bazel
@@ -27,6 +27,7 @@ "//core/registry", "//internal/internalregistry", "//internal/monitoringutil", + "//internal/tinkerror", "//keyset", "//mac/subtle", "//monitoring", @@ -51,12 +52,15 @@ name = "aead_test", srcs = [ "aead_factory_test.go", + "aead_init_test.go", "aead_key_templates_test.go", "aead_test.go", "aes_ctr_hmac_aead_key_manager_test.go", "aes_gcm_key_manager_test.go", "aes_gcm_siv_key_manager_test.go", "chacha20poly1305_key_manager_test.go", + "kms_envelope_aead_example_test.go", + "kms_envelope_aead_key_manager_test.go", "kms_envelope_aead_test.go", "xchacha20poly1305_key_manager_test.go", ], @@ -65,13 +69,18 @@ "//aead/subtle", "//core/cryptofmt", "//core/registry", + "//insecurecleartextkeyset", "//internal/internalregistry", + "//internal/testing/stubkeymanager", + "//internal/tinkerror/tinkerrortest", "//keyset", + "//mac", "//monitoring", "//proto/aes_ctr_hmac_aead_go_proto", "//proto/aes_gcm_go_proto", "//proto/aes_gcm_siv_go_proto", "//proto/chacha20_poly1305_go_proto", + "//proto/kms_envelope_go_proto", "//proto/tink_go_proto", "//proto/xchacha20_poly1305_go_proto", "//signature",
diff --git a/go/aead/aead.go b/go/aead/aead.go index 45cd93e..4c313a0 100644 --- a/go/aead/aead.go +++ b/go/aead/aead.go
@@ -23,6 +23,7 @@ "fmt" "github.com/google/tink/go/core/registry" + "github.com/google/tink/go/internal/internalregistry" ) func init() { @@ -33,6 +34,9 @@ if err := registry.RegisterKeyManager(new(aesGCMKeyManager)); err != nil { panic(fmt.Sprintf("aead.init() failed: %v", err)) } + if err := internalregistry.AllowKeyDerivation(aesGCMTypeURL); err != nil { + panic(fmt.Sprintf("aead.init() failed: %v", err)) + } if err := registry.RegisterKeyManager(new(chaCha20Poly1305KeyManager)); err != nil { panic(fmt.Sprintf("aead.init() failed: %v", err)) @@ -41,6 +45,9 @@ if err := registry.RegisterKeyManager(new(xChaCha20Poly1305KeyManager)); err != nil { panic(fmt.Sprintf("aead.init() failed: %v", err)) } + if err := internalregistry.AllowKeyDerivation(xChaCha20Poly1305TypeURL); err != nil { + panic(fmt.Sprintf("aead.init() failed: %v", err)) + } if err := registry.RegisterKeyManager(new(kmsEnvelopeAEADKeyManager)); err != nil { panic(fmt.Sprintf("aead.init() failed: %v", err))
diff --git a/go/aead/aead_factory.go b/go/aead/aead_factory.go index 55bbdb1..bfe8879 100644 --- a/go/aead/aead_factory.go +++ b/go/aead/aead_factory.go
@@ -21,7 +21,6 @@ "github.com/google/tink/go/core/cryptofmt" "github.com/google/tink/go/core/primitiveset" - "github.com/google/tink/go/core/registry" "github.com/google/tink/go/internal/internalregistry" "github.com/google/tink/go/internal/monitoringutil" "github.com/google/tink/go/keyset" @@ -30,19 +29,11 @@ ) // New returns an AEAD primitive from the given keyset handle. -func New(h *keyset.Handle) (tink.AEAD, error) { - return NewWithKeyManager(h, nil /*keyManager*/) -} - -// NewWithKeyManager returns an AEAD primitive from the given keyset handle and custom key manager. -// -// Deprecated: Use [New]. -func NewWithKeyManager(h *keyset.Handle, km registry.KeyManager) (tink.AEAD, error) { - ps, err := h.PrimitivesWithKeyManager(km) +func New(handle *keyset.Handle) (tink.AEAD, error) { + ps, err := handle.Primitives() if err != nil { return nil, fmt.Errorf("aead_factory: cannot obtain primitive set: %s", err) } - return newWrappedAead(ps) } @@ -66,32 +57,43 @@ } } } - wa := &wrappedAead{ps: ps} - client := internalregistry.GetMonitoringClient() - if client == nil { - return wa, nil - } - keysetInfo, err := monitoringutil.KeysetInfoFromPrimitiveSet(ps) + encLogger, decLogger, err := createLoggers(ps) if err != nil { return nil, err } - wa.encLogger, err = client.NewLogger(&monitoring.Context{ + return &wrappedAead{ + ps: ps, + encLogger: encLogger, + decLogger: decLogger, + }, nil +} + +func createLoggers(ps *primitiveset.PrimitiveSet) (monitoring.Logger, monitoring.Logger, error) { + if len(ps.Annotations) == 0 { + return &monitoringutil.DoNothingLogger{}, &monitoringutil.DoNothingLogger{}, nil + } + client := internalregistry.GetMonitoringClient() + keysetInfo, err := monitoringutil.KeysetInfoFromPrimitiveSet(ps) + if err != nil { + return nil, nil, err + } + encLogger, err := client.NewLogger(&monitoring.Context{ Primitive: "aead", APIFunction: "encrypt", KeysetInfo: keysetInfo, }) if err != nil { - return nil, err + return nil, nil, err } - wa.decLogger, err = client.NewLogger(&monitoring.Context{ + decLogger, err := client.NewLogger(&monitoring.Context{ Primitive: "aead", APIFunction: "decrypt", KeysetInfo: keysetInfo, }) if err != nil { - return nil, err + return nil, nil, err } - return wa, nil + return encLogger, decLogger, nil } // Encrypt encrypts the given plaintext with the given associatedData. @@ -108,7 +110,13 @@ return nil, err } a.encLogger.Log(primary.KeyID, len(plaintext)) - return append([]byte(primary.Prefix), ct...), nil + if len(primary.Prefix) == 0 { + return ct, nil + } + output := make([]byte, 0, len(primary.Prefix)+len(ct)) + output = append(output, primary.Prefix...) + output = append(output, ct...) + return output, nil } // Decrypt decrypts the given ciphertext and authenticates it with the given
diff --git a/go/aead/aead_factory_test.go b/go/aead/aead_factory_test.go index 9716142..7f8f8c3 100644 --- a/go/aead/aead_factory_test.go +++ b/go/aead/aead_factory_test.go
@@ -18,6 +18,7 @@ import ( "bytes" + "errors" "fmt" "strings" "testing" @@ -26,7 +27,10 @@ "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/tink/go/aead" "github.com/google/tink/go/core/cryptofmt" + "github.com/google/tink/go/core/registry" + "github.com/google/tink/go/insecurecleartextkeyset" "github.com/google/tink/go/internal/internalregistry" + "github.com/google/tink/go/internal/testing/stubkeymanager" "github.com/google/tink/go/keyset" "github.com/google/tink/go/monitoring" "github.com/google/tink/go/signature" @@ -37,6 +41,7 @@ "github.com/google/tink/go/tink" "github.com/google/tink/go/aead/subtle" + agpb "github.com/google/tink/go/proto/aes_gcm_go_proto" tinkpb "github.com/google/tink/go/proto/tink_go_proto" ) @@ -103,9 +108,7 @@ } } -func validateAEADFactoryCipher(encryptCipher tink.AEAD, - decryptCipher tink.AEAD, - expectedPrefix string) error { +func validateAEADFactoryCipher(encryptCipher, decryptCipher tink.AEAD, expectedPrefix string) error { prefixSize := len(expectedPrefix) // regular plaintext pt := random.GetRandomBytes(20) @@ -170,26 +173,37 @@ } } -func TestFactoryWithMonitoringPrimitiveLogsEncryptionDecryptionWithPrefix(t *testing.T) { +func TestPrimitiveFactoryWithMonitoringAnnotationsLogsEncryptionDecryptionWithPrefix(t *testing.T) { defer internalregistry.ClearMonitoringClient() client := fakemonitoring.NewClient("fake-client") if err := internalregistry.RegisterMonitoringClient(client); err != nil { - t.Fatalf("registry.RegisterMonitoringClient() err = %v, want nil", err) + t.Fatalf("internalregistry.RegisterMonitoringClient() err = %v, want nil", err) } kh, err := keyset.NewHandle(aead.AES128GCMKeyTemplate()) if err != nil { t.Fatalf("keyset.NewHandle() err = %v, want nil", err) } - p, err := aead.New(kh) + // Annotations are only supported throught the `insecurecleartextkeyset` API. + buff := &bytes.Buffer{} + if err := insecurecleartextkeyset.Write(kh, keyset.NewBinaryWriter(buff)); err != nil { + t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err) + } + annotations := map[string]string{"foo": "bar"} + mh, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations)) + if err != nil { + t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err) + } + p, err := aead.New(mh) if err != nil { t.Fatalf("aead.New() err = %v, want nil", err) } data := []byte("HELLO_WORLD") - ct, err := p.Encrypt(data, nil) + ad := []byte("_!") + ct, err := p.Encrypt(data, ad) if err != nil { t.Fatalf("p.Encrypt() err = %v, want nil", err) } - if _, err := p.Decrypt(ct, nil); err != nil { + if _, err := p.Decrypt(ct, ad); err != nil { t.Fatalf("p.Decrypt() err = %v, want nil", err) } failures := client.Failures() @@ -198,46 +212,57 @@ } got := client.Events() wantKeysetInfo := monitoring.NewKeysetInfo( - make(map[string]string), + annotations, kh.KeysetInfo().GetPrimaryKeyId(), []*monitoring.Entry{ { - KeyID: kh.KeysetInfo().GetPrimaryKeyId(), - Status: monitoring.Enabled, - FormatAsString: "type.googleapis.com/google.crypto.tink.AesGcmKey", + KeyID: kh.KeysetInfo().GetPrimaryKeyId(), + Status: monitoring.Enabled, + KeyType: "tink.AesGcmKey", + KeyPrefix: "TINK", }, }, ) want := []*fakemonitoring.LogEvent{ { - KeyID: kh.KeysetInfo().GetPrimaryKeyId(), + KeyID: mh.KeysetInfo().GetPrimaryKeyId(), NumBytes: len(data), Context: monitoring.NewContext("aead", "encrypt", wantKeysetInfo), }, { - KeyID: kh.KeysetInfo().GetPrimaryKeyId(), + KeyID: mh.KeysetInfo().GetPrimaryKeyId(), // ciphertext was encrypted with a key that has TINK ouput prefix. This adds a 5 bytes prefix // to the ciphertext. This prefix is not included in `Log` call. NumBytes: len(ct) - cryptofmt.NonRawPrefixSize, Context: monitoring.NewContext("aead", "decrypt", wantKeysetInfo), }, } - if !cmp.Equal(got, want) { - t.Errorf("got = %v, want = %v", got, want) + if cmp.Diff(got, want) != "" { + t.Errorf("%v", cmp.Diff(got, want)) } } -func TestFactoryWithMonitoringPrimitiveLogsEncryptionDecryptionWithoutPrefix(t *testing.T) { +func TestPrimitiveFactoryWithMonitoringAnnotationsLogsEncryptionDecryptionWithoutPrefix(t *testing.T) { defer internalregistry.ClearMonitoringClient() client := fakemonitoring.NewClient("fake-client") if err := internalregistry.RegisterMonitoringClient(client); err != nil { - t.Fatalf("registry.RegisterMonitoringClient() err = %v, want nil", err) + t.Fatalf("internalregistry.RegisterMonitoringClient() err = %v, want nil", err) } kh, err := keyset.NewHandle(aead.AES256GCMNoPrefixKeyTemplate()) if err != nil { t.Fatalf("keyset.NewHandle() err = %v, want nil", err) } - p, err := aead.New(kh) + // Annotations are only supported throught the `insecurecleartextkeyset` API. + buff := &bytes.Buffer{} + if err := insecurecleartextkeyset.Write(kh, keyset.NewBinaryWriter(buff)); err != nil { + t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err) + } + annotations := map[string]string{"foo": "bar"} + mh, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations)) + if err != nil { + t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err) + } + p, err := aead.New(mh) if err != nil { t.Fatalf("aead.New() err = %v, want nil", err) } @@ -255,34 +280,35 @@ } got := client.Events() wantKeysetInfo := monitoring.NewKeysetInfo( - make(map[string]string), + annotations, kh.KeysetInfo().GetPrimaryKeyId(), []*monitoring.Entry{ { - KeyID: kh.KeysetInfo().GetPrimaryKeyId(), - Status: monitoring.Enabled, - FormatAsString: "type.googleapis.com/google.crypto.tink.AesGcmKey", + KeyID: kh.KeysetInfo().GetPrimaryKeyId(), + Status: monitoring.Enabled, + KeyType: "tink.AesGcmKey", + KeyPrefix: "RAW", }, }, ) want := []*fakemonitoring.LogEvent{ { - KeyID: kh.KeysetInfo().GetPrimaryKeyId(), + KeyID: mh.KeysetInfo().GetPrimaryKeyId(), NumBytes: len(data), Context: monitoring.NewContext("aead", "encrypt", wantKeysetInfo), }, { - KeyID: kh.KeysetInfo().GetPrimaryKeyId(), + KeyID: mh.KeysetInfo().GetPrimaryKeyId(), NumBytes: len(ct), Context: monitoring.NewContext("aead", "decrypt", wantKeysetInfo), }, } - if !cmp.Equal(got, want) { - t.Errorf("got = %v, want = %v", got, want) + if cmp.Diff(got, want) != "" { + t.Errorf("%v", cmp.Diff(got, want)) } } -func TestFactoryWithMonitoringPrimitiveWithMultipleKeysLogsEncryptionDecryption(t *testing.T) { +func TestPrimitiveFactoryMonitoringWithAnnotatiosMultipleKeysLogsEncryptionDecryption(t *testing.T) { defer internalregistry.ClearMonitoringClient() client := fakemonitoring.NewClient("fake-client") if err := internalregistry.RegisterMonitoringClient(client); err != nil { @@ -313,7 +339,17 @@ if err != nil { t.Fatalf("manager.Handle() err = %v, want nil", err) } - p, err := aead.New(kh) + // Annotations are only supported throught the `insecurecleartextkeyset` API. + buff := &bytes.Buffer{} + if err := insecurecleartextkeyset.Write(kh, keyset.NewBinaryWriter(buff)); err != nil { + t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err) + } + annotations := map[string]string{"foo": "bar"} + mh, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations)) + if err != nil { + t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err) + } + p, err := aead.New(mh) if err != nil { t.Fatalf("aead.New() err = %v, want nil", err) } @@ -330,21 +366,24 @@ t.Fatalf("p.Decrypt() err = %v, want nil", err) } got := client.Events() - wantKeysetInfo := monitoring.NewKeysetInfo(make(map[string]string), keyIDs[1], []*monitoring.Entry{ + wantKeysetInfo := monitoring.NewKeysetInfo(annotations, keyIDs[1], []*monitoring.Entry{ { - KeyID: keyIDs[1], - Status: monitoring.Enabled, - FormatAsString: "type.googleapis.com/google.crypto.tink.AesGcmKey", + KeyID: keyIDs[1], + Status: monitoring.Enabled, + KeyType: "tink.AesGcmKey", + KeyPrefix: "RAW", }, { - KeyID: keyIDs[2], - Status: monitoring.Enabled, - FormatAsString: "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey", + KeyID: keyIDs[2], + Status: monitoring.Enabled, + KeyType: "tink.AesCtrHmacAeadKey", + KeyPrefix: "TINK", }, { - KeyID: keyIDs[3], - Status: monitoring.Enabled, - FormatAsString: "type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key", + KeyID: keyIDs[3], + Status: monitoring.Enabled, + KeyType: "tink.XChaCha20Poly1305Key", + KeyPrefix: "TINK", }, }) want := []*fakemonitoring.LogEvent{ @@ -376,25 +415,47 @@ } } -func TestFactoryWithMonitoringPrimitiveEncryptionFailureIsLogged(t *testing.T) { +func TestPrimitiveFactoryWithMonitoringAnnotationsEncryptionFailureIsLogged(t *testing.T) { defer internalregistry.ClearMonitoringClient() client := &fakemonitoring.Client{Name: ""} if err := internalregistry.RegisterMonitoringClient(client); err != nil { t.Fatalf("internalregistry.RegisterMonitoringClient() err = %v, want nil", err) } - kh, err := keyset.NewHandle(aead.AES128GCMKeyTemplate()) + typeURL := "TestFactoryWithMonitoringPrimitiveEncryptionFailureIsLogged" + template := &tinkpb.KeyTemplate{ + TypeUrl: typeURL, + OutputPrefixType: tinkpb.OutputPrefixType_LEGACY, + } + km := &stubkeymanager.StubKeyManager{ + URL: typeURL, + Key: &agpb.AesGcmKey{}, + Prim: &testutil.AlwaysFailingAead{Error: errors.New("failed")}, + KeyData: &tinkpb.KeyData{ + TypeUrl: typeURL, + KeyMaterialType: tinkpb.KeyData_SYMMETRIC, + Value: []byte("serialized_key"), + }, + } + if err := registry.RegisterKeyManager(km); err != nil { + t.Fatalf("registry.RegisterKeyManager() err = %v, want nil", err) + } + kh, err := keyset.NewHandle(template) if err != nil { t.Fatalf("keyset.NewHandle() err = %v, want nil", err) } - km := testutil.NewTestKeyManager( - &testutil.AlwaysFailingAead{ - Error: fmt.Errorf("failed"), - }, - testutil.AESGCMTypeURL, - ) - p, err := aead.NewWithKeyManager(kh, km) + // Annotations are only supported throught the `insecurecleartextkeyset` API. + buff := &bytes.Buffer{} + if err := insecurecleartextkeyset.Write(kh, keyset.NewBinaryWriter(buff)); err != nil { + t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err) + } + annotations := map[string]string{"foo": "bar"} + mh, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations)) if err != nil { - t.Fatalf("aead.NewWithKeyManager() err = %v, want nil", err) + t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err) + } + p, err := aead.New(mh) + if err != nil { + t.Fatalf("aead.New() err = %v, want nil", err) } if _, err := p.Encrypt(nil, nil); err == nil { t.Fatalf("Encrypt() err = nil, want error") @@ -406,27 +467,28 @@ "aead", "encrypt", monitoring.NewKeysetInfo( - make(map[string]string), + annotations, kh.KeysetInfo().GetPrimaryKeyId(), []*monitoring.Entry{ { - KeyID: kh.KeysetInfo().GetPrimaryKeyId(), - Status: monitoring.Enabled, - FormatAsString: "type.googleapis.com/google.crypto.tink.AesGcmKey", + KeyID: kh.KeysetInfo().GetPrimaryKeyId(), + Status: monitoring.Enabled, + KeyType: typeURL, + KeyPrefix: "LEGACY", }, }, ), ), }, } - if !cmp.Equal(got, want) { - t.Errorf("got = %v, want = %v", got, want) + if cmp.Diff(got, want) != "" { + t.Errorf("%v", cmp.Diff(got, want)) } } -func TestFactoryWithMonitoringPrimitiveDecryptionFailureIsLogged(t *testing.T) { +func TestPrimitiveFactoryWithMonitoringAnnotationsDecryptionFailureIsLogged(t *testing.T) { defer internalregistry.ClearMonitoringClient() - client := &fakemonitoring.Client{Name: ""} + client := fakemonitoring.NewClient("fake-client") if err := internalregistry.RegisterMonitoringClient(client); err != nil { t.Fatalf("internalregistry.RegisterMonitoringClient() err = %v, want nil", err) } @@ -434,7 +496,17 @@ if err != nil { t.Fatalf("keyset.NewHandle() err = %v, want nil", err) } - p, err := aead.New(kh) + // Annotations are only supported throught the `insecurecleartextkeyset` API. + buff := &bytes.Buffer{} + if err := insecurecleartextkeyset.Write(kh, keyset.NewBinaryWriter(buff)); err != nil { + t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err) + } + annotations := map[string]string{"foo": "bar"} + mh, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations)) + if err != nil { + t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err) + } + p, err := aead.New(mh) if err != nil { t.Fatalf("aead.New() err = %v, want nil", err) } @@ -448,21 +520,22 @@ "aead", "decrypt", monitoring.NewKeysetInfo( - make(map[string]string), + annotations, kh.KeysetInfo().GetPrimaryKeyId(), []*monitoring.Entry{ { - KeyID: kh.KeysetInfo().GetPrimaryKeyId(), - Status: monitoring.Enabled, - FormatAsString: "type.googleapis.com/google.crypto.tink.AesGcmKey", + KeyID: kh.KeysetInfo().GetPrimaryKeyId(), + Status: monitoring.Enabled, + KeyType: "tink.AesGcmKey", + KeyPrefix: "TINK", }, }, ), ), }, } - if !cmp.Equal(got, want) { - t.Errorf("got = %v, want = %v", got, want) + if cmp.Diff(got, want) != "" { + t.Errorf("%v", cmp.Diff(got, want)) } } @@ -476,7 +549,17 @@ if err != nil { t.Fatalf("keyset.NewHandle() err = %v, want nil", err) } - p1, err := aead.New(kh1) + // Annotations are only supported throught the `insecurecleartextkeyset` API. + buff := &bytes.Buffer{} + if err := insecurecleartextkeyset.Write(kh1, keyset.NewBinaryWriter(buff)); err != nil { + t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err) + } + annotations := map[string]string{"foo": "bar"} + mh1, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations)) + if err != nil { + t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err) + } + p1, err := aead.New(mh1) if err != nil { t.Fatalf("aead.New() err = %v, want nil", err) } @@ -484,7 +567,15 @@ if err != nil { t.Fatalf("keyset.NewHandle() err = %v, want nil", err) } - p2, err := aead.New(kh2) + buff.Reset() + if err := insecurecleartextkeyset.Write(kh2, keyset.NewBinaryWriter(buff)); err != nil { + t.Fatalf("insecurecleartextkeyset.Write() err = %v, want nil", err) + } + mh2, err := insecurecleartextkeyset.Read(keyset.NewBinaryReader(buff), keyset.WithAnnotations(annotations)) + if err != nil { + t.Fatalf("insecurecleartextkeyset.Read() err = %v, want nil", err) + } + p2, err := aead.New(mh2) if err != nil { t.Fatalf("aead.New() err = %v, want nil", err) } @@ -505,13 +596,14 @@ "aead", "encrypt", monitoring.NewKeysetInfo( - make(map[string]string), + annotations, kh1.KeysetInfo().GetPrimaryKeyId(), []*monitoring.Entry{ { - KeyID: kh1.KeysetInfo().GetPrimaryKeyId(), - Status: monitoring.Enabled, - FormatAsString: "type.googleapis.com/google.crypto.tink.AesGcmKey", + KeyID: kh1.KeysetInfo().GetPrimaryKeyId(), + Status: monitoring.Enabled, + KeyType: "tink.AesGcmKey", + KeyPrefix: "TINK", }, }, ), @@ -524,13 +616,14 @@ "aead", "encrypt", monitoring.NewKeysetInfo( - make(map[string]string), + annotations, kh2.KeysetInfo().GetPrimaryKeyId(), []*monitoring.Entry{ { - KeyID: kh2.KeysetInfo().GetPrimaryKeyId(), - Status: monitoring.Enabled, - FormatAsString: "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey", + KeyID: kh2.KeysetInfo().GetPrimaryKeyId(), + Status: monitoring.Enabled, + KeyType: "tink.AesCtrHmacAeadKey", + KeyPrefix: "TINK", }, }, ), @@ -541,3 +634,35 @@ t.Errorf("got = %v, want = %v, with diff: %v", got, want, cmp.Diff(got, want)) } } + +func TestPrimitiveFactoryEncryptDecryptWithoutAnnotationsDoesNothing(t *testing.T) { + defer internalregistry.ClearMonitoringClient() + client := fakemonitoring.NewClient("fake-client") + if err := internalregistry.RegisterMonitoringClient(client); err != nil { + t.Fatalf("internalregistry.RegisterMonitoringClient() err = %v, want nil", err) + } + kh, err := keyset.NewHandle(aead.AES128GCMKeyTemplate()) + if err != nil { + t.Fatalf("keyset.NewHandle() err = %v, want nil", err) + } + p, err := aead.New(kh) + if err != nil { + t.Fatalf("aead.New() err = %v, want nil", err) + } + data := []byte("YELLOW_ORANGE") + ct, err := p.Encrypt(data, nil) + if err != nil { + t.Fatalf("p.Encrypt() err = %v, want nil", err) + } + if _, err := p.Decrypt(ct, nil); err != nil { + t.Fatalf("p.Decrypt() err = %v, want nil", err) + } + got := client.Events() + if len(got) != 0 { + t.Errorf("len(client.Events()) = %d, want 0", len(got)) + } + failures := len(client.Failures()) + if failures != 0 { + t.Errorf("len(client.Failures()) = %d, want 0", failures) + } +}
diff --git a/go/aead/aead_init_test.go b/go/aead/aead_init_test.go new file mode 100644 index 0000000..664ef4b --- /dev/null +++ b/go/aead/aead_init_test.go
@@ -0,0 +1,44 @@ +// Copyright 2023 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. +// +//////////////////////////////////////////////////////////////////////////////// + +package aead_test + +import ( + "testing" + + "github.com/google/tink/go/core/registry" + "github.com/google/tink/go/testutil" +) + +func TestAEADInit(t *testing.T) { + // Check that the AES-GCM key manager is in the global registry. + _, err := registry.GetKeyManager(testutil.AESGCMTypeURL) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + + // Check that the ChaCha20Poly1305 key manager is in the global registry. + _, err = registry.GetKeyManager(testutil.ChaCha20Poly1305TypeURL) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + + // Check that the XChaCha20Poly1305 key manager is in the global registry. + _, err = registry.GetKeyManager(testutil.XChaCha20Poly1305TypeURL) + if err != nil { + t.Errorf("unexpected error: %s", err) + } +}
diff --git a/go/aead/aead_key_templates.go b/go/aead/aead_key_templates.go index b536279..1cbb001 100644 --- a/go/aead/aead_key_templates.go +++ b/go/aead/aead_key_templates.go
@@ -17,10 +17,14 @@ package aead import ( + "fmt" + "google.golang.org/protobuf/proto" + "github.com/google/tink/go/internal/tinkerror" ctrpb "github.com/google/tink/go/proto/aes_ctr_go_proto" ctrhmacpb "github.com/google/tink/go/proto/aes_ctr_hmac_aead_go_proto" gcmpb "github.com/google/tink/go/proto/aes_gcm_go_proto" + gcmsivpb "github.com/google/tink/go/proto/aes_gcm_siv_go_proto" commonpb "github.com/google/tink/go/proto/common_go_proto" hmacpb "github.com/google/tink/go/proto/hmac_go_proto" kmsenvpb "github.com/google/tink/go/proto/kms_envelope_go_proto" @@ -51,22 +55,43 @@ return createAESGCMKeyTemplate(32, tinkpb.OutputPrefixType_RAW) } +// AES128GCMSIVKeyTemplate is a KeyTemplate that generates an AES-GCM-SIV key with the following parameters: +// - Key size: 16 bytes +// - Output prefix type: TINK +func AES128GCMSIVKeyTemplate() *tinkpb.KeyTemplate { + return createAESGCMSIVKeyTemplate(16, tinkpb.OutputPrefixType_TINK) +} + +// AES256GCMSIVKeyTemplate is a KeyTemplate that generates an AES-GCM-SIV key with the following parameters: +// - Key size: 32 bytes +// - Output prefix type: TINK +func AES256GCMSIVKeyTemplate() *tinkpb.KeyTemplate { + return createAESGCMSIVKeyTemplate(32, tinkpb.OutputPrefixType_TINK) +} + +// AES256GCMSIVNoPrefixKeyTemplate is a KeyTemplate that generates an AES-GCM key with the following parameters: +// - Key size: 32 bytes +// - Output prefix type: RAW +func AES256GCMSIVNoPrefixKeyTemplate() *tinkpb.KeyTemplate { + return createAESGCMSIVKeyTemplate(32, tinkpb.OutputPrefixType_RAW) +} + // AES128CTRHMACSHA256KeyTemplate is a KeyTemplate that generates an AES-CTR-HMAC-AEAD key with the following parameters: -// - AES key size: 16 bytes -// - AES CTR IV size: 16 bytes -// - HMAC key size: 32 bytes -// - HMAC tag size: 16 bytes -// - HMAC hash function: SHA256 +// - AES key size: 16 bytes +// - AES CTR IV size: 16 bytes +// - HMAC key size: 32 bytes +// - HMAC tag size: 16 bytes +// - HMAC hash function: SHA256 func AES128CTRHMACSHA256KeyTemplate() *tinkpb.KeyTemplate { return createAESCTRHMACAEADKeyTemplate(16, 16, 32, 16, commonpb.HashType_SHA256) } // AES256CTRHMACSHA256KeyTemplate is a KeyTemplate that generates an AES-CTR-HMAC-AEAD key with the following parameters: -// - AES key size: 32 bytes -// - AES CTR IV size: 16 bytes -// - HMAC key size: 32 bytes -// - HMAC tag size: 32 bytes -// - HMAC hash function: SHA256 +// - AES key size: 32 bytes +// - AES CTR IV size: 16 bytes +// - HMAC key size: 32 bytes +// - HMAC tag size: 32 bytes +// - HMAC hash function: SHA256 func AES256CTRHMACSHA256KeyTemplate() *tinkpb.KeyTemplate { return createAESCTRHMACAEADKeyTemplate(32, 16, 32, 32, commonpb.HashType_SHA256) } @@ -89,22 +114,65 @@ } } -// KMSEnvelopeAEADKeyTemplate is a KeyTemplate that generates a KMSEnvelopeAEAD key for -// a given KEK in remote KMS. Keys generated by this key template uses RAW output prefix -// to make them compatible with the remote KMS' encrypt/decrypt operations. -// Unlike other templates, when you generate new keys with this template, Tink does not -// generate new key material, but only creates a reference to the remote KEK. -func KMSEnvelopeAEADKeyTemplate(uri string, dekT *tinkpb.KeyTemplate) *tinkpb.KeyTemplate { +// CreateKMSEnvelopeAEADKeyTemplate returns a key template that generates a +// KMSEnvelopeAEAD key for a given key encryption key (KEK) in a remote key +// management service (KMS). +// +// When performing encrypt operations, a data encryption key (DEK) is generated +// for each ciphertext. The DEK is wrapped by the remote KMS using the KEK and +// stored alongside the ciphertext. +// +// dekTemplate must be a KeyTemplate for any of these Tink AEAD key types (any +// other key template will be rejected): +// - AesCtrHmacAeadKey +// - AesGcmKey +// - ChaCha20Poly1305Key +// - XChaCha20Poly1305 +// - AesGcmSivKey +// +// DEKs generated by this key template use the RAW output prefix to make them +// compatible with remote KMS encrypt/decrypt operations. +// +// Unlike other templates, when you generate new keys with this template, Tink +// does not generate new key material, but only creates a reference to the +// remote KEK. +// +// If either uri or dekTemplate contain invalid input, an error is returned. +func CreateKMSEnvelopeAEADKeyTemplate(uri string, dekTemplate *tinkpb.KeyTemplate) (*tinkpb.KeyTemplate, error) { + if !isSupporedKMSEnvelopeDEK(dekTemplate.GetTypeUrl()) { + return nil, fmt.Errorf("unsupported DEK key type %s. Only Tink AEAD key types are supported", dekTemplate.GetTypeUrl()) + } + f := &kmsenvpb.KmsEnvelopeAeadKeyFormat{ KekUri: uri, - DekTemplate: dekT, + DekTemplate: dekTemplate, } - serializedFormat, _ := proto.Marshal(f) + serializedFormat, err := proto.Marshal(f) + if err != nil { + return nil, fmt.Errorf("failed to marshal key format: %s", err) + } return &tinkpb.KeyTemplate{ Value: serializedFormat, TypeUrl: kmsEnvelopeAEADTypeURL, OutputPrefixType: tinkpb.OutputPrefixType_RAW, + }, nil +} + +// KMSEnvelopeAEADKeyTemplate returns a KeyTemplate that generates a +// KMSEnvelopeAEAD key for a given key encryption key (KEK) in a remote key +// management service (KMS). +// +// If either uri or dekTemplate contain invalid input, program execution will +// be interrupted. +// +// Deprecated: Use [CreateKMSEnvelopeAEADKeyTemplate], which returns an error +// value instead of interrupting the program. +func KMSEnvelopeAEADKeyTemplate(uri string, dekTemplate *tinkpb.KeyTemplate) *tinkpb.KeyTemplate { + t, err := CreateKMSEnvelopeAEADKeyTemplate(uri, dekTemplate) + if err != nil { + tinkerror.Fail(err.Error()) } + return t } // createAESGCMKeyTemplate creates a new AES-GCM key template with the given key @@ -113,7 +181,10 @@ format := &gcmpb.AesGcmKeyFormat{ KeySize: keySize, } - serializedFormat, _ := proto.Marshal(format) + serializedFormat, err := proto.Marshal(format) + if err != nil { + tinkerror.Fail(fmt.Sprintf("failed to marshal key format: %s", err)) + } return &tinkpb.KeyTemplate{ TypeUrl: aesGCMTypeURL, Value: serializedFormat, @@ -121,6 +192,23 @@ } } +// createAESGCMSIVKeyTemplate creates a new AES-GCM-SIV key template with the given key +// size in bytes. +func createAESGCMSIVKeyTemplate(keySize uint32, outputPrefixType tinkpb.OutputPrefixType) *tinkpb.KeyTemplate { + format := &gcmsivpb.AesGcmSivKeyFormat{ + KeySize: keySize, + } + serializedFormat, err := proto.Marshal(format) + if err != nil { + tinkerror.Fail(fmt.Sprintf("failed to marshal key format: %s", err)) + } + return &tinkpb.KeyTemplate{ + TypeUrl: aesGCMSIVTypeURL, + Value: serializedFormat, + OutputPrefixType: outputPrefixType, + } +} + func createAESCTRHMACAEADKeyTemplate(aesKeySize, ivSize, hmacKeySize, tagSize uint32, hash commonpb.HashType) *tinkpb.KeyTemplate { format := &ctrhmacpb.AesCtrHmacAeadKeyFormat{ AesCtrKeyFormat: &ctrpb.AesCtrKeyFormat{ @@ -132,7 +220,10 @@ KeySize: hmacKeySize, }, } - serializedFormat, _ := proto.Marshal(format) + serializedFormat, err := proto.Marshal(format) + if err != nil { + tinkerror.Fail(fmt.Sprintf("failed to marshal key format: %s", err)) + } return &tinkpb.KeyTemplate{ Value: serializedFormat, TypeUrl: aesCTRHMACAEADTypeURL,
diff --git a/go/aead/aead_key_templates_test.go b/go/aead/aead_key_templates_test.go index 3b13523..cf6d6c8 100644 --- a/go/aead/aead_key_templates_test.go +++ b/go/aead/aead_key_templates_test.go
@@ -23,7 +23,9 @@ "github.com/google/tink/go/aead" "github.com/google/tink/go/core/registry" + "github.com/google/tink/go/internal/tinkerror/tinkerrortest" "github.com/google/tink/go/keyset" + "github.com/google/tink/go/mac" "github.com/google/tink/go/testing/fakekms" tinkpb "github.com/google/tink/go/proto/tink_go_proto" ) @@ -43,6 +45,15 @@ name: "AES256_GCM_NO_PREFIX", template: aead.AES256GCMNoPrefixKeyTemplate(), }, { + name: "AES128_GCM_SIV", + template: aead.AES128GCMSIVKeyTemplate(), + }, { + name: "AES256_GCM_SIV", + template: aead.AES256GCMSIVKeyTemplate(), + }, { + name: "AES256_GCM_SIV_NO_PREFIX", + template: aead.AES256GCMSIVNoPrefixKeyTemplate(), + }, { name: "AES128_CTR_HMAC_SHA256", template: aead.AES128CTRHMACSHA256KeyTemplate(), }, { @@ -77,16 +88,25 @@ if err != nil { t.Fatalf("fakekms.NewKeyURI() failed: %v", err) } + fixedKeyTemplate, err := aead.CreateKMSEnvelopeAEADKeyTemplate(fixedKeyURI, aead.AES128GCMKeyTemplate()) + if err != nil { + t.Fatalf("CreateKMSEnvelopeAEADKeyTemplate() err = %v", err) + } + newKeyTemplate, err := aead.CreateKMSEnvelopeAEADKeyTemplate(newKeyURI, aead.AES128GCMKeyTemplate()) + if err != nil { + t.Fatalf("CreateKMSEnvelopeAEADKeyTemplate() err = %v", err) + } + var testCases = []struct { name string template *tinkpb.KeyTemplate }{ { name: "Fixed Fake KMS Envelope AEAD Key with AES128_GCM", - template: aead.KMSEnvelopeAEADKeyTemplate(fixedKeyURI, aead.AES128GCMKeyTemplate()), + template: fixedKeyTemplate, }, { name: "New Fake KMS Envelope AEAD Key with AES128_GCM", - template: aead.KMSEnvelopeAEADKeyTemplate(newKeyURI, aead.AES128GCMKeyTemplate()), + template: newKeyTemplate, }, } for _, tc := range testCases { @@ -101,8 +121,8 @@ } } -// Tests that two KMSEnvelopeAEAD keys that use the same KEK and DEK template should be able to -// decrypt each other's ciphertexts. +// Tests that two KMSEnvelopeAEAD keys that use the same KEK and DEK template +// should be able to decrypt each other's ciphertexts. func TestKMSEnvelopeAEADKeyTemplateMultipleKeysSameKEK(t *testing.T) { fakeKmsClient, err := fakekms.NewClient("fake-kms://") if err != nil { @@ -111,7 +131,62 @@ registry.RegisterKMSClient(fakeKmsClient) fixedKeyURI := "fake-kms://CM2b3_MDElQKSAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EhIaEIK75t5L-adlUwVhWvRuWUwYARABGM2b3_MDIAE" - template1 := aead.KMSEnvelopeAEADKeyTemplate(fixedKeyURI, aead.AES128GCMKeyTemplate()) + template1, err := aead.CreateKMSEnvelopeAEADKeyTemplate(fixedKeyURI, aead.AES128GCMKeyTemplate()) + if err != nil { + t.Fatalf("CreateKMSEnvelopeAEADKeyTemplate() err = %v", err) + } + template2, err := aead.CreateKMSEnvelopeAEADKeyTemplate(fixedKeyURI, aead.AES128GCMKeyTemplate()) + if err != nil { + t.Fatalf("CreateKMSEnvelopeAEADKeyTemplate() err = %v", err) + } + + handle1, err := keyset.NewHandle(template1) + if err != nil { + t.Fatalf("keyset.NewHandle(template1) failed: %v", err) + } + aead1, err := aead.New(handle1) + if err != nil { + t.Fatalf("aead.New(handle) failed: %v", err) + } + + handle2, err := keyset.NewHandle(template2) + if err != nil { + t.Fatalf("keyset.NewHandle(template2) failed: %v", err) + } + aead2, err := aead.New(handle2) + if err != nil { + t.Fatalf("aead.New(handle) failed: %v", err) + } + + plaintext := []byte("some data to encrypt") + aad := []byte("extra data to authenticate") + + ciphertext, err := aead1.Encrypt(plaintext, aad) + if err != nil { + t.Fatalf("encryption failed, error: %v", err) + } + decrypted, err := aead2.Decrypt(ciphertext, aad) + if err != nil { + t.Fatalf("decryption failed, error: %v", err) + } + if !bytes.Equal(plaintext, decrypted) { + t.Fatalf("decrypted data doesn't match plaintext, got: %q, want: %q", decrypted, plaintext) + } +} + +// Testing deprecated function, ignoring GoDeprecated. +func TestCreateKMSEnvelopeAEADKeyTemplateCompatibleWithKMSEnevelopeAEADKeyTemplate(t *testing.T) { + fakeKmsClient, err := fakekms.NewClient("fake-kms://") + if err != nil { + t.Fatalf("fakekms.NewClient('fake-kms://') failed: %v", err) + } + registry.RegisterKMSClient(fakeKmsClient) + + fixedKeyURI := "fake-kms://CM2b3_MDElQKSAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EhIaEIK75t5L-adlUwVhWvRuWUwYARABGM2b3_MDIAE" + template1, err := aead.CreateKMSEnvelopeAEADKeyTemplate(fixedKeyURI, aead.AES128GCMKeyTemplate()) + if err != nil { + t.Fatalf("CreateKMSEnvelopeAEADKeyTemplate() err = %v", err) + } template2 := aead.KMSEnvelopeAEADKeyTemplate(fixedKeyURI, aead.AES128GCMKeyTemplate()) handle1, err := keyset.NewHandle(template1) @@ -148,6 +223,51 @@ } } +// Testing deprecated function, ignoring GoDeprecated. +func TestKMSEnvelopeAEADKeyTemplateFails(t *testing.T) { + keyURI, err := fakekms.NewKeyURI() + if err != nil { + t.Fatalf("fakekms.NewKeyURI() err = %v", err) + } + invalidTemplate := &tinkpb.KeyTemplate{ + // String fields cannot contain invalid UTF-8 characters. + TypeUrl: "\xff", + } + var template *tinkpb.KeyTemplate + err = tinkerrortest.RecoverFromFail(func() { + template = aead.KMSEnvelopeAEADKeyTemplate(keyURI, invalidTemplate) + }) + if err == nil { + t.Errorf("aead.KMSEnvelopAEADKeyTemplate() err = nil, want non-nil") + } + t.Logf("template: %+v", template) +} + +func TestCreateKMSEnvelopeAEADKeyTemplateFails(t *testing.T) { + keyURI, err := fakekms.NewKeyURI() + if err != nil { + t.Fatalf("fakekms.NewKeyURI() err = %v", err) + } + invalidTemplate := &tinkpb.KeyTemplate{ + // String fields cannot contain invalid UTF-8 characters. + TypeUrl: "\xff", + } + if _, err := aead.CreateKMSEnvelopeAEADKeyTemplate(keyURI, invalidTemplate); err == nil { + t.Errorf("aead.CreateKMSEnvelopAEADKeyTemplate(keyURI, invalidTemplate) err = nil, want non-nil") + } +} + +func TestCreateKMSEnvelopeAEADKeyTemplateWithUnsupportedTemplateFails(t *testing.T) { + keyURI, err := fakekms.NewKeyURI() + if err != nil { + t.Fatalf("fakekms.NewKeyURI() err = %v", err) + } + unsupportedTemplate := mac.HMACSHA256Tag128KeyTemplate() + if _, err := aead.CreateKMSEnvelopeAEADKeyTemplate(keyURI, unsupportedTemplate); err == nil { + t.Errorf("aead.CreateKMSEnvelopAEADKeyTemplate(keyURI, unsupportedTemplate) err = nil, want non-nil") + } +} + func testEncryptDecrypt(template *tinkpb.KeyTemplate) error { handle, err := keyset.NewHandle(template) if err != nil {
diff --git a/go/aead/aead_test.go b/go/aead/aead_test.go index 456991a..b0583cc 100644 --- a/go/aead/aead_test.go +++ b/go/aead/aead_test.go
@@ -16,66 +16,76 @@ package aead_test +// [START aead-example] + import ( - "encoding/base64" + "bytes" "fmt" "log" - "testing" "github.com/google/tink/go/aead" - "github.com/google/tink/go/core/registry" + "github.com/google/tink/go/insecurecleartextkeyset" "github.com/google/tink/go/keyset" - "github.com/google/tink/go/testutil" ) func Example() { - kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) - if err != nil { - log.Fatal(err) - } + // A keyset created with "tinkey create-keyset --key-template=AES256_GCM". Note + // that this keyset has the secret key information in cleartext. + jsonKeyset := `{ + "key": [{ + "keyData": { + "keyMaterialType": + "SYMMETRIC", + "typeUrl": + "type.googleapis.com/google.crypto.tink.AesGcmKey", + "value": + "GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg==" + }, + "keyId": 294406504, + "outputPrefixType": "TINK", + "status": "ENABLED" + }], + "primaryKeyId": 294406504 + }` - // TODO: save the keyset to a safe location. DO NOT hardcode it in source code. + // Create a keyset handle from the cleartext keyset in the previous + // step. The keyset handle provides abstract access to the underlying keyset to + // limit the exposure of accessing the raw key material. WARNING: In practice, + // it is unlikely you will want to use a insecurecleartextkeyset, as it implies + // that your key material is passed in cleartext, which is a security risk. // Consider encrypting it with a remote key in Cloud KMS, AWS KMS or HashiCorp Vault. // See https://github.com/google/tink/blob/master/docs/GOLANG-HOWTO.md#storing-and-loading-existing-keysets. - - a, err := aead.New(kh) + keysetHandle, err := insecurecleartextkeyset.Read( + keyset.NewJSONReader(bytes.NewBufferString(jsonKeyset))) if err != nil { log.Fatal(err) } - msg := []byte("this message needs to be encrypted") - aad := []byte("this data needs to be authenticated, but not encrypted") - ct, err := a.Encrypt(msg, aad) + // Retrieve the AEAD primitive we want to use from the keyset handle. + primitive, err := aead.New(keysetHandle) if err != nil { log.Fatal(err) } - pt, err := a.Decrypt(ct, aad) + // Use the primitive to encrypt a message. In this case the primary key of the + // keyset will be used (which is also the only key in this example). + plaintext := []byte("message") + associatedData := []byte("associated data") + ciphertext, err := primitive.Encrypt(plaintext, associatedData) if err != nil { log.Fatal(err) } - fmt.Printf("Ciphertext: %s\n", base64.StdEncoding.EncodeToString(ct)) - fmt.Printf("Original plaintext: %s\n", msg) - fmt.Printf("Decrypted Plaintext: %s\n", pt) + // Use the primitive to decrypt the message. Decrypt finds the correct key in + // the keyset and decrypts the ciphertext. If no key is found or decryption + // fails, it returns an error. + decrypted, err := primitive.Decrypt(ciphertext, associatedData) + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(decrypted)) + // Output: message } -func TestAEADInit(t *testing.T) { - // Check for AES-GCM key manager. - _, err := registry.GetKeyManager(testutil.AESGCMTypeURL) - if err != nil { - t.Errorf("unexpected error: %s", err) - } - - // Check for ChaCha20Poly1305 key manager. - _, err = registry.GetKeyManager(testutil.ChaCha20Poly1305TypeURL) - if err != nil { - t.Errorf("unexpected error: %s", err) - } - - // Check for XChaCha20Poly1305 key manager. - _, err = registry.GetKeyManager(testutil.XChaCha20Poly1305TypeURL) - if err != nil { - t.Errorf("unexpected error: %s", err) - } -} +// [END aead-example]
diff --git a/go/aead/aes_gcm_key_manager.go b/go/aead/aes_gcm_key_manager.go index c74da12..f5142b9 100644 --- a/go/aead/aes_gcm_key_manager.go +++ b/go/aead/aes_gcm_key_manager.go
@@ -18,6 +18,7 @@ import ( "fmt" + "io" "google.golang.org/protobuf/proto" "github.com/google/tink/go/aead/subtle" @@ -97,7 +98,7 @@ return &tinkpb.KeyData{ TypeUrl: aesGCMTypeURL, Value: serializedKey, - KeyMaterialType: tinkpb.KeyData_SYMMETRIC, + KeyMaterialType: km.KeyMaterialType(), }, nil } @@ -111,10 +112,41 @@ return aesGCMTypeURL } +// KeyMaterialType returns the key material type of the key manager. +func (km *aesGCMKeyManager) KeyMaterialType() tinkpb.KeyData_KeyMaterialType { + return tinkpb.KeyData_SYMMETRIC +} + +// DeriveKey derives a new key from serializedKeyFormat and pseudorandomness. +func (km *aesGCMKeyManager) DeriveKey(serializedKeyFormat []byte, pseudorandomness io.Reader) (proto.Message, error) { + if len(serializedKeyFormat) == 0 { + return nil, errInvalidAESGCMKeyFormat + } + keyFormat := new(gcmpb.AesGcmKeyFormat) + if err := proto.Unmarshal(serializedKeyFormat, keyFormat); err != nil { + return nil, errInvalidAESGCMKeyFormat + } + if err := km.validateKeyFormat(keyFormat); err != nil { + return nil, fmt.Errorf("aes_gcm_key_manager: invalid key format: %s", err) + } + if err := keyset.ValidateKeyVersion(keyFormat.GetVersion(), aesGCMKeyVersion); err != nil { + return nil, fmt.Errorf("aes_gcm_key_manager: invalid key version: %s", err) + } + + keyValue := make([]byte, keyFormat.GetKeySize()) + if _, err := io.ReadFull(pseudorandomness, keyValue); err != nil { + return nil, fmt.Errorf("aes_gcm_key_manager: not enough pseudorandomness given") + } + + return &gcmpb.AesGcmKey{ + Version: aesGCMKeyVersion, + KeyValue: keyValue, + }, nil +} + // validateKey validates the given AESGCMKey. func (km *aesGCMKeyManager) validateKey(key *gcmpb.AesGcmKey) error { - err := keyset.ValidateKeyVersion(key.Version, aesGCMKeyVersion) - if err != nil { + if err := keyset.ValidateKeyVersion(key.Version, aesGCMKeyVersion); err != nil { return fmt.Errorf("aes_gcm_key_manager: %s", err) } keySize := uint32(len(key.KeyValue))
diff --git a/go/aead/aes_gcm_key_manager_test.go b/go/aead/aes_gcm_key_manager_test.go index 6ea647e..156b98a 100644 --- a/go/aead/aes_gcm_key_manager_test.go +++ b/go/aead/aes_gcm_key_manager_test.go
@@ -21,9 +21,11 @@ "fmt" "testing"