Skip to content

Commit

Permalink
Adding generation of new keys to C++ EciesAeadHkdf hybrid encryption.
Browse files Browse the repository at this point in the history
Extending validation of key/format parameters.

PiperOrigin-RevId: 205088914
GitOrigin-RevId: 4d5e1e82a4b4bd7045b8c1de8798273d91c53934
  • Loading branch information
przydatek authored and Tink Team committed Jul 20, 2018
1 parent 691490f commit 13c0829
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 24 deletions.
7 changes: 7 additions & 0 deletions cc/hybrid/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ cc_library(
"//cc:hybrid_decrypt",
"//cc:key_manager",
"//cc:registry",
"//cc/subtle:subtle_util_boringssl",
"//cc/util:enums",
"//cc/util:errors",
"//cc/util:protobuf_helper",
"//cc/util:status",
"//cc/util:statusor",
Expand Down Expand Up @@ -374,6 +377,7 @@ cc_test(
copts = ["-Iexternal/gtest/include"],
deps = [
":ecies_aead_hkdf_private_key_manager",
":hybrid_config",
":hybrid_key_templates",
"//cc/aead:aead_key_templates",
"//proto:common_cc_proto",
Expand Down Expand Up @@ -437,7 +441,10 @@ cc_test(
deps = [
":ecies_aead_hkdf_private_key_manager",
":ecies_aead_hkdf_public_key_manager",
":hybrid_key_templates",
"//cc:hybrid_decrypt",
"//cc/aead:aead_key_templates",
"//cc/aead:aes_ctr_hmac_aead_key_manager",
"//cc/aead:aes_gcm_key_manager",
"//cc/util:status",
"//cc/util:statusor",
Expand Down
90 changes: 86 additions & 4 deletions cc/hybrid/ecies_aead_hkdf_private_key_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@
#include "absl/strings/string_view.h"
#include "tink/hybrid_decrypt.h"
#include "tink/key_manager.h"
#include "tink/registry.h"
#include "tink/hybrid/ecies_aead_hkdf_hybrid_decrypt.h"
#include "tink/subtle/subtle_util_boringssl.h"
#include "tink/util/enums.h"
#include "tink/util/errors.h"
#include "tink/util/protobuf_helper.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "tink/util/validation.h"
#include "proto/ecies_aead_hkdf.pb.h"
#include "proto/common.pb.h"
#include "proto/tink.pb.h"

namespace crypto {
Expand All @@ -34,6 +38,11 @@ namespace tink {
using google::crypto::tink::EciesAeadHkdfPrivateKey;
using google::crypto::tink::EciesAeadHkdfKeyFormat;
using google::crypto::tink::EciesAeadHkdfParams;
using google::crypto::tink::EciesAeadDemParams;
using google::crypto::tink::EciesHkdfKemParams;
using google::crypto::tink::EcPointFormat;
using google::crypto::tink::EllipticCurveType;
using google::crypto::tink::HashType;
using google::crypto::tink::KeyData;
using google::crypto::tink::KeyTemplate;
using portable_proto::MessageLite;
Expand Down Expand Up @@ -64,19 +73,66 @@ class EciesAeadHkdfPrivateKeyFactory : public KeyFactory {

StatusOr<std::unique_ptr<MessageLite>> EciesAeadHkdfPrivateKeyFactory::NewKey(
const portable_proto::MessageLite& key_format) const {
return util::Status(util::error::UNIMPLEMENTED, "not implemented yet");
std::string key_format_url =
std::string(EciesAeadHkdfPrivateKeyManager::kKeyTypePrefix) +
key_format.GetTypeName();
if (key_format_url != EciesAeadHkdfPrivateKeyManager::kKeyFormatUrl) {
return ToStatusF(util::error::INVALID_ARGUMENT,
"Key format proto '%s' is not supported by this manager.",
key_format_url.c_str());
}
const EciesAeadHkdfKeyFormat& ecies_key_format =
reinterpret_cast<const EciesAeadHkdfKeyFormat&>(key_format);
Status status = EciesAeadHkdfPrivateKeyManager::Validate(ecies_key_format);
if (!status.ok()) return status;

// Generate new EC key.
const EciesHkdfKemParams& kem_params = ecies_key_format.params().kem_params();
auto ec_key_result = subtle::SubtleUtilBoringSSL::GetNewEcKey(
util::Enums::ProtoToSubtle(kem_params.curve_type()));
if (!ec_key_result.ok()) return ec_key_result.status();
auto ec_key = ec_key_result.ValueOrDie();

// Build EciesAeadHkdfPrivateKey.
std::unique_ptr<EciesAeadHkdfPrivateKey> ecies_private_key(
new EciesAeadHkdfPrivateKey());
ecies_private_key->set_version(EciesAeadHkdfPrivateKeyManager::kVersion);
ecies_private_key->set_key_value(ec_key.priv);
auto ecies_public_key = ecies_private_key->mutable_public_key();
ecies_public_key->set_version(EciesAeadHkdfPrivateKeyManager::kVersion);
ecies_public_key->set_x(ec_key.pub_x);
ecies_public_key->set_y(ec_key.pub_y);
*(ecies_public_key->mutable_params()) = ecies_key_format.params();

std::unique_ptr<MessageLite> key = std::move(ecies_private_key);
return std::move(key);
}

StatusOr<std::unique_ptr<MessageLite>> EciesAeadHkdfPrivateKeyFactory::NewKey(
absl::string_view serialized_key_format) const {
return util::Status(util::error::UNIMPLEMENTED, "not implemented yet");
EciesAeadHkdfKeyFormat key_format;
if (!key_format.ParseFromString(std::string(serialized_key_format))) {
return ToStatusF(util::error::INVALID_ARGUMENT,
"Could not parse the passed string as proto '%s'.",
EciesAeadHkdfPrivateKeyManager::kKeyFormatUrl);
}
return NewKey(key_format);
}

StatusOr<std::unique_ptr<KeyData>> EciesAeadHkdfPrivateKeyFactory::NewKeyData(
absl::string_view serialized_key_format) const {
return util::Status(util::error::UNIMPLEMENTED, "not implemented yet");
auto new_key_result = NewKey(serialized_key_format);
if (!new_key_result.ok()) return new_key_result.status();
auto new_key = reinterpret_cast<const EciesAeadHkdfPrivateKey&>(
*(new_key_result.ValueOrDie()));
std::unique_ptr<KeyData> key_data(new KeyData());
key_data->set_type_url(EciesAeadHkdfPrivateKeyManager::kKeyType);
key_data->set_value(new_key.SerializeAsString());
key_data->set_key_material_type(KeyData::ASYMMETRIC_PRIVATE);
return std::move(key_data);
}

constexpr char EciesAeadHkdfPrivateKeyManager::kKeyFormatUrl[];
constexpr char EciesAeadHkdfPrivateKeyManager::kKeyTypePrefix[];
constexpr char EciesAeadHkdfPrivateKeyManager::kKeyType[];
constexpr uint32_t EciesAeadHkdfPrivateKeyManager::kVersion;
Expand Down Expand Up @@ -141,6 +197,29 @@ EciesAeadHkdfPrivateKeyManager::GetPrimitiveImpl(
// static
Status EciesAeadHkdfPrivateKeyManager::Validate(
const EciesAeadHkdfParams& params) {
// Validate KEM params.
if (!params.has_kem_params()) {
return Status(util::error::INVALID_ARGUMENT, "Missing kem_params.");
}
if (params.kem_params().curve_type() == EllipticCurveType::UNKNOWN_CURVE ||
params.kem_params().hkdf_hash_type() == HashType::UNKNOWN_HASH) {
return Status(util::error::INVALID_ARGUMENT, "Invalid kem_params.");
}

// Validate DEM params.
if (!params.has_dem_params()) {
return Status(util::error::INVALID_ARGUMENT, "Missing dem_params.");
}
if (!params.dem_params().has_aead_dem()) {
return Status(util::error::INVALID_ARGUMENT, "Invalid dem_params.");
}
auto result = Registry::NewKeyData(params.dem_params().aead_dem());
if (!result.ok()) return result.status();

// Validate EC point format.
if (params.ec_point_format() == EcPointFormat::UNKNOWN_FORMAT) {
return Status(util::error::INVALID_ARGUMENT, "Unknown EC point format.");
}
return Status::OK;
}

Expand All @@ -155,7 +234,10 @@ Status EciesAeadHkdfPrivateKeyManager::Validate(
// static
Status EciesAeadHkdfPrivateKeyManager::Validate(
const EciesAeadHkdfKeyFormat& key_format) {
return Status::OK;
if (!key_format.has_params()) {
return Status(util::error::INVALID_ARGUMENT, "Missing params.");
}
return Validate(key_format.params());
}

} // namespace tink
Expand Down
2 changes: 2 additions & 0 deletions cc/hybrid/ecies_aead_hkdf_private_key_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class EciesAeadHkdfPrivateKeyManager : public KeyManager<HybridDecrypt> {
virtual ~EciesAeadHkdfPrivateKeyManager() {}

private:
friend class EciesAeadHkdfPrivateKeyFactory;

static constexpr char kKeyTypePrefix[] = "type.googleapis.com/";
static constexpr char kKeyFormatUrl[] =
"type.googleapis.com/google.crypto.tink.EciesAeadHkdfKeyFormat";
Expand Down
134 changes: 123 additions & 11 deletions cc/hybrid/ecies_aead_hkdf_private_key_manager_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@

#include "tink/hybrid_decrypt.h"
#include "tink/registry.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/hybrid/ecies_aead_hkdf_public_key_manager.h"
#include "tink/hybrid/hybrid_key_templates.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "tink/util/test_util.h"
Expand Down Expand Up @@ -48,13 +51,31 @@ class EciesAeadHkdfPrivateKeyManagerTest : public ::testing::Test {
static void SetUpTestCase() {
auto aes_gcm_key_manager = new AesGcmKeyManager();
ASSERT_TRUE(Registry::RegisterKeyManager(aes_gcm_key_manager).ok());
auto aes_ctr_hmac_key_manager = new AesCtrHmacAeadKeyManager();
ASSERT_TRUE(Registry::RegisterKeyManager(aes_ctr_hmac_key_manager).ok());
}

std::string key_type_prefix = "type.googleapis.com/";
std::string ecies_private_key_type =
"type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey";
};

// Checks whether given key is compatible with the given format.
void CheckNewKey(const EciesAeadHkdfPrivateKey& ecies_key,
const EciesAeadHkdfKeyFormat& key_format) {
EciesAeadHkdfPrivateKeyManager key_manager;
EXPECT_EQ(0, ecies_key.version());
EXPECT_TRUE(ecies_key.has_public_key());
EXPECT_GT(ecies_key.key_value().length(), 0);
EXPECT_EQ(0, ecies_key.public_key().version());
EXPECT_GT(ecies_key.public_key().x().length(), 0);
EXPECT_GT(ecies_key.public_key().y().length(), 0);
EXPECT_EQ(ecies_key.public_key().params().SerializeAsString(),
key_format.params().SerializeAsString());
auto primitive_result = key_manager.GetPrimitive(ecies_key);
EXPECT_TRUE(primitive_result.ok()) << primitive_result.status();
}

TEST_F(EciesAeadHkdfPrivateKeyManagerTest, testBasic) {
EciesAeadHkdfPrivateKeyManager key_manager;

Expand Down Expand Up @@ -154,34 +175,125 @@ TEST_F(EciesAeadHkdfPrivateKeyManagerTest, testPrimitives) {
}
}

TEST_F(EciesAeadHkdfPrivateKeyManagerTest, testNewKeyError) {
TEST_F(EciesAeadHkdfPrivateKeyManagerTest, testNewKeyCreation) {
EciesAeadHkdfPrivateKeyManager key_manager;
const KeyFactory& key_factory = key_manager.get_key_factory();

{ // Via NewKey(format_proto).
EciesAeadHkdfKeyFormat key_format;
ASSERT_TRUE(key_format.ParseFromString(
HybridKeyTemplates::EciesP256HkdfHmacSha256Aes128Gcm().value()));
auto result = key_factory.NewKey(key_format);
EXPECT_FALSE(result.ok());
EXPECT_EQ(util::error::UNIMPLEMENTED, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "not implemented yet",
result.status().error_message());
EXPECT_TRUE(result.ok()) << result.status();
auto key = std::move(result.ValueOrDie());
ASSERT_EQ(ecies_private_key_type, key_type_prefix + key->GetTypeName());
std::unique_ptr<EciesAeadHkdfPrivateKey> ecies_key(
reinterpret_cast<EciesAeadHkdfPrivateKey*>(key.release()));
CheckNewKey(*ecies_key, key_format);
}

{ // Via NewKey(serialized_format_proto).
EciesAeadHkdfKeyFormat key_format;
ASSERT_TRUE(key_format.ParseFromString(
HybridKeyTemplates::EciesP256HkdfHmacSha256Aes128CtrHmacSha256()
.value()));
auto result = key_factory.NewKey(key_format.SerializeAsString());
EXPECT_FALSE(result.ok());
EXPECT_EQ(util::error::UNIMPLEMENTED, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "not implemented yet",
result.status().error_message());
EXPECT_TRUE(result.ok()) << result.status();
auto key = std::move(result.ValueOrDie());
ASSERT_EQ(ecies_private_key_type, key_type_prefix + key->GetTypeName());
std::unique_ptr<EciesAeadHkdfPrivateKey> ecies_key(
reinterpret_cast<EciesAeadHkdfPrivateKey*>(key.release()));
CheckNewKey(*ecies_key, key_format);
}

{ // Via NewKeyData(serialized_format_proto).
EciesAeadHkdfKeyFormat key_format;
ASSERT_TRUE(key_format.ParseFromString(
HybridKeyTemplates::EciesP256HkdfHmacSha256Aes128CtrHmacSha256()
.value()));
auto result = key_factory.NewKeyData(key_format.SerializeAsString());
EXPECT_TRUE(result.ok()) << result.status();
auto key_data = std::move(result.ValueOrDie());
EXPECT_EQ(ecies_private_key_type, key_data->type_url());
EXPECT_EQ(KeyData::ASYMMETRIC_PRIVATE, key_data->key_material_type());
EciesAeadHkdfPrivateKey ecies_key;
ASSERT_TRUE(ecies_key.ParseFromString(key_data->value()));
CheckNewKey(ecies_key, key_format);
}
}

TEST_F(EciesAeadHkdfPrivateKeyManagerTest, testNewKeyErrors) {
EciesAeadHkdfPrivateKeyManager key_manager;
const KeyFactory& key_factory = key_manager.get_key_factory();

// Empty key format.
EciesAeadHkdfKeyFormat key_format;
{
auto result = key_factory.NewKey(key_format);
EXPECT_FALSE(result.ok());
EXPECT_EQ(util::error::UNIMPLEMENTED, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "not implemented yet",
EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "Missing params",
result.status().error_message());
}

// Missing kem_params.
auto params = key_format.mutable_params();
{
auto result = key_factory.NewKey(key_format);
EXPECT_FALSE(result.ok());
EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "Missing kem_params",
result.status().error_message());
}

// Invalid kem_params.
auto kem_params = params->mutable_kem_params();
{
auto result = key_factory.NewKey(key_format);
EXPECT_FALSE(result.ok());
EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "Invalid kem_params",
result.status().error_message());
}

// Missing dem_params.
kem_params->set_curve_type(EllipticCurveType::NIST_P256);
kem_params->set_hkdf_hash_type(HashType::SHA256);
{
auto result = key_factory.NewKey(key_format);
EXPECT_FALSE(result.ok());
EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "Missing dem_params",
result.status().error_message());
}

// Invalid dem_params.
auto dem_params = params->mutable_dem_params();
{
auto result = key_factory.NewKey(key_format);
EXPECT_FALSE(result.ok());
EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "Invalid dem_params",
result.status().error_message());
}

// Unsupported AEAD DEM (i.e. not present in the registry).
*(dem_params->mutable_aead_dem()) = AeadKeyTemplates::Aes128Eax();
{
auto result = key_factory.NewKey(key_format);
EXPECT_FALSE(result.ok());
EXPECT_EQ(util::error::NOT_FOUND, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "No manager",
result.status().error_message());
}

// Invalid EC point format.
*(dem_params->mutable_aead_dem()) = AeadKeyTemplates::Aes128Gcm();
{
auto result = key_factory.NewKey(key_format);
EXPECT_FALSE(result.ok());
EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "Unknown EC point format",
result.status().error_message());
}
}
Expand Down
Loading

0 comments on commit 13c0829

Please sign in to comment.