blob: 13387125b078f714df15d454b90a9c421426c8df [file] [log] [blame]
// 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/hybrid/internal/hpke_context.h"
#include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "tink/hybrid/internal/hpke_test_util.h"
#include "tink/hybrid/internal/hpke_util.h"
#include "tink/util/secret_data.h"
#include "tink/util/statusor.h"
#include "tink/util/test_matchers.h"
namespace crypto {
namespace tink {
namespace internal {
namespace {
using ::crypto::tink::internal::CreateHpkeTestParams;
using ::crypto::tink::internal::DefaultHpkeTestParams;
using ::crypto::tink::internal::HpkeTestParams;
using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::StatusIs;
using ::testing::Eq;
using ::testing::TestWithParam;
using ::testing::Values;
class HpkeContextTest : public TestWithParam<HpkeParams> {};
INSTANTIATE_TEST_SUITE_P(
HpkeContextTestSuite, HpkeContextTest,
Values(HpkeParams{HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
HpkeAead::kAes128Gcm},
HpkeParams{HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
HpkeAead::kChaCha20Poly1305}));
TEST_P(HpkeContextTest, SealAndOpen) {
HpkeParams hpke_params = GetParam();
util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
ASSERT_THAT(params, IsOk());
util::StatusOr<std::unique_ptr<HpkeContext>> sender_hpke_context =
HpkeContext::SetupSender(hpke_params, params->recipient_public_key,
params->application_info);
ASSERT_THAT(sender_hpke_context, IsOk());
util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
HpkeContext::SetupRecipient(
hpke_params,
util::SecretDataFromStringView(params->recipient_private_key),
(*sender_hpke_context)->EncapsulatedKey(), params->application_info);
ASSERT_THAT(recipient_hpke_context, IsOk());
std::vector<std::string> inputs = {"", params->plaintext};
std::vector<std::string> context_infos = {"", params->application_info};
for (const std::string& input : inputs) {
for (const std::string& context_info : context_infos) {
SCOPED_TRACE(absl::StrCat("plaintext: '", input, "', context_info: '",
context_info, "'"));
util::StatusOr<std::string> ciphertext =
(*sender_hpke_context)->Seal(input, context_info);
ASSERT_THAT(ciphertext, IsOk());
util::StatusOr<std::string> plaintext =
(*recipient_hpke_context)->Open(*ciphertext, context_info);
ASSERT_THAT(plaintext, IsOk());
EXPECT_THAT(*plaintext, Eq(input));
}
}
}
TEST_P(HpkeContextTest, Export) {
HpkeParams hpke_params = GetParam();
util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
ASSERT_THAT(params, IsOk());
util::StatusOr<std::unique_ptr<HpkeContext>> sender_hpke_context =
HpkeContext::SetupSender(hpke_params, params->recipient_public_key,
params->application_info);
ASSERT_THAT(sender_hpke_context, IsOk());
util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
HpkeContext::SetupRecipient(
hpke_params,
util::SecretDataFromStringView(params->recipient_private_key),
(*sender_hpke_context)->EncapsulatedKey(), params->application_info);
ASSERT_THAT(recipient_hpke_context, IsOk());
std::vector<std::string> exporter_contexts = {"", "c", "context"};
std::vector<int> secret_lengths = {0, 8, 16, 32, 64};
for (const std::string& exporter_context : exporter_contexts) {
for (int secret_length : secret_lengths) {
SCOPED_TRACE(absl::StrCat("exporter_context: '", exporter_context,
"', secret_length: '", secret_length, "'"));
util::StatusOr<util::SecretData> sender_secret =
(*sender_hpke_context)->Export(exporter_context, secret_length);
ASSERT_THAT(sender_secret, IsOk());
util::StatusOr<util::SecretData> recipient_secret =
(*recipient_hpke_context)->Export(exporter_context, secret_length);
ASSERT_THAT(recipient_secret, IsOk());
EXPECT_THAT(*sender_secret, Eq(*recipient_secret));
}
}
}
TEST_P(HpkeContextTest, OpenTruncatedCiphertextFails) {
HpkeParams hpke_params = GetParam();
util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
ASSERT_THAT(params, IsOk());
util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
HpkeContext::SetupRecipient(
hpke_params,
util::SecretDataFromStringView(params->recipient_private_key),
params->encapsulated_key, params->application_info);
ASSERT_THAT(recipient_hpke_context, IsOk());
util::StatusOr<std::string> plaintext =
(*recipient_hpke_context)
->Open(params->ciphertext, params->associated_data);
ASSERT_THAT(plaintext, IsOk());
const std::string truncated_ciphertext =
params->ciphertext.substr(params->ciphertext.length() - 1);
util::StatusOr<std::string> bad_plaintext =
(*recipient_hpke_context)
->Open(truncated_ciphertext, params->associated_data);
EXPECT_THAT(bad_plaintext.status(), StatusIs(absl::StatusCode::kUnknown));
}
TEST_P(HpkeContextTest, OpenModifiedCiphertextFails) {
HpkeParams hpke_params = GetParam();
util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
ASSERT_THAT(params, IsOk());
util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
HpkeContext::SetupRecipient(
hpke_params,
util::SecretDataFromStringView(params->recipient_private_key),
params->encapsulated_key, params->application_info);
ASSERT_THAT(recipient_hpke_context, IsOk());
util::StatusOr<std::string> plaintext =
(*recipient_hpke_context)
->Open(params->ciphertext, params->associated_data);
ASSERT_THAT(plaintext, IsOk());
const std::string modified_ciphertext =
absl::StrCat(params->ciphertext, "modification");
util::StatusOr<std::string> bad_plaintext =
(*recipient_hpke_context)
->Open(modified_ciphertext, params->associated_data);
EXPECT_THAT(bad_plaintext.status(), StatusIs(absl::StatusCode::kUnknown));
}
TEST_P(HpkeContextTest, OpenModifiedAssociatedDataFails) {
HpkeParams hpke_params = GetParam();
util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
ASSERT_THAT(params, IsOk());
util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
HpkeContext::SetupRecipient(
hpke_params,
util::SecretDataFromStringView(params->recipient_private_key),
params->encapsulated_key, params->application_info);
ASSERT_THAT(recipient_hpke_context, IsOk());
util::StatusOr<std::string> plaintext =
(*recipient_hpke_context)
->Open(params->ciphertext, params->associated_data);
ASSERT_THAT(plaintext, IsOk());
const std::string modified_associated_data =
absl::StrCat(params->associated_data, "modification");
util::StatusOr<std::string> bad_plaintext =
(*recipient_hpke_context)
->Open(params->ciphertext, modified_associated_data);
EXPECT_THAT(bad_plaintext.status(), StatusIs(absl::StatusCode::kUnknown));
}
class HpkeContextWithBadHpkeParamTest : public TestWithParam<HpkeParams> {};
INSTANTIATE_TEST_SUITE_P(
HpkeContextWithBadHpkeParamTestTestSuite, HpkeContextWithBadHpkeParamTest,
Values(HpkeParams{HpkeKem::kUnknownKem, HpkeKdf::kHkdfSha256,
HpkeAead::kAes128Gcm},
HpkeParams{HpkeKem::kX25519HkdfSha256, HpkeKdf::kUnknownKdf,
HpkeAead::kAes256Gcm},
HpkeParams{HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
HpkeAead::kUnknownAead}));
TEST_P(HpkeContextWithBadHpkeParamTest, SenderBadHpkeParamFails) {
HpkeParams hpke_params = GetParam();
HpkeTestParams params = DefaultHpkeTestParams();
util::StatusOr<std::unique_ptr<HpkeContext>> sender_hpke_context =
HpkeContext::SetupSender(hpke_params, params.recipient_public_key,
params.application_info);
EXPECT_THAT(sender_hpke_context.status(),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST_P(HpkeContextWithBadHpkeParamTest, RecipientBadHpkeParamFails) {
HpkeParams hpke_params = GetParam();
HpkeTestParams params = DefaultHpkeTestParams();
util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
HpkeContext::SetupRecipient(
hpke_params,
util::SecretDataFromStringView(params.recipient_private_key),
params.encapsulated_key, params.application_info);
EXPECT_THAT(recipient_hpke_context.status(),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(HpkeContextZeroLengthKeyTest, ZeroLengthPublicKeyFails) {
HpkeParams hpke_params = {HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
HpkeAead::kAes256Gcm};
HpkeTestParams params = DefaultHpkeTestParams();
util::StatusOr<std::unique_ptr<HpkeContext>> sender_hpke_context =
HpkeContext::SetupSender(hpke_params, /*recipient_public_key=*/"",
params.application_info);
EXPECT_THAT(sender_hpke_context.status(),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(HpkeContextZeroLengthKeyTest, ZeroLengthPrivateKeyFails) {
HpkeParams hpke_params = {HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
HpkeAead::kAes256Gcm};
HpkeTestParams params = DefaultHpkeTestParams();
util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
HpkeContext::SetupRecipient(
hpke_params,
/*recipient_private_key=*/util::SecretDataFromStringView(""),
params.encapsulated_key, params.application_info);
EXPECT_THAT(recipient_hpke_context.status(),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(HpkeContextZeroLengthKeyTest, ZeroLengthEncapsulatedKeyFails) {
HpkeParams hpke_params = {HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
HpkeAead::kAes256Gcm};
HpkeTestParams params = DefaultHpkeTestParams();
util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
HpkeContext::SetupRecipient(
hpke_params,
util::SecretDataFromStringView(params.recipient_private_key),
/*encapsulated_key=*/"", params.application_info);
EXPECT_THAT(recipient_hpke_context.status(),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(ConcatenatePayloadTest, ConcatenatePayloadSucceeds) {
HpkeTestParams params = DefaultHpkeTestParams();
EXPECT_THAT(ConcatenatePayload(params.encapsulated_key, params.ciphertext),
Eq(absl::StrCat(params.encapsulated_key, params.ciphertext)));
}
TEST(SplitPayloadTest, SplitPayloadSucceeds) {
HpkeTestParams params = DefaultHpkeTestParams();
const std::string payload =
absl::StrCat(params.encapsulated_key, params.ciphertext);
util::StatusOr<HpkePayloadView> hpke_payload =
SplitPayload(HpkeKem::kX25519HkdfSha256, payload);
ASSERT_THAT(hpke_payload, IsOk());
EXPECT_THAT(hpke_payload->encapsulated_key, Eq(params.encapsulated_key));
EXPECT_THAT(hpke_payload->ciphertext, Eq(params.ciphertext));
}
TEST(SplitPayloadTest, InvalidKemFails) {
HpkeTestParams params = DefaultHpkeTestParams();
util::StatusOr<HpkePayloadView> payload =
SplitPayload(HpkeKem::kUnknownKem,
absl::StrCat(params.encapsulated_key, params.ciphertext));
EXPECT_THAT(payload.status(), StatusIs(absl::StatusCode::kInvalidArgument));
}
} // namespace
} // namespace internal
} // namespace tink
} // namespace crypto