1 /*
2  * Copyright 2019 Google LLC.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     https://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "private_join_and_compute/util/elgamal_key_util.h"
17 
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 
21 #include <filesystem>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
27 #include "private_join_and_compute/crypto/context.h"
28 #include "private_join_and_compute/crypto/ec_group.h"
29 #include "private_join_and_compute/crypto/elgamal.h"
30 #include "private_join_and_compute/crypto/elgamal.pb.h"
31 #include "private_join_and_compute/crypto/openssl.inc"
32 #include "private_join_and_compute/util/elgamal_proto_util.h"
33 #include "private_join_and_compute/util/proto_util.h"
34 #include "private_join_and_compute/util/status_testing.inc"
35 
36 namespace private_join_and_compute::elgamal_key_util {
37 namespace {
38 
39 using elgamal::PublicKey;
40 using elgamal_proto_util::DeserializePrivateKey;
41 using elgamal_proto_util::DeserializePublicKey;
42 using private_join_and_compute::ElGamalPublicKey;
43 using private_join_and_compute::ElGamalSecretKey;
44 using private_join_and_compute::ProtoUtils;
45 using ::testing::HasSubstr;
46 using ::testing::Test;
47 
48 const int kTestCurveId = NID_X9_62_prime256v1;
49 
TEST(ElGamalKeyUtilTest,GenerateKeyPair)50 TEST(ElGamalKeyUtilTest, GenerateKeyPair) {
51   std::filesystem::path temp_dir(::testing::TempDir());
52   std::string pub_key_filename = (temp_dir / "elgamal_pub.key").string();
53   std::string prv_key_filename = (temp_dir / "elgamal_prv.key").string();
54   ASSERT_OK(
55       GenerateElGamalKeyPair(kTestCurveId, pub_key_filename, prv_key_filename));
56   ASSERT_TRUE(std::filesystem::exists(pub_key_filename));
57   ASSERT_TRUE(std::filesystem::exists(prv_key_filename));
58 
59   // Verify the keys written to files are correct.
60   Context context;
61   ASSERT_OK_AND_ASSIGN(auto ec_group, ECGroup::Create(kTestCurveId, &context));
62   ASSERT_OK_AND_ASSIGN(
63       auto public_key_proto,
64       ProtoUtils::ReadProtoFromFile<ElGamalPublicKey>(pub_key_filename));
65   ASSERT_OK_AND_ASSIGN(
66       auto private_key_proto,
67       ProtoUtils::ReadProtoFromFile<ElGamalSecretKey>(prv_key_filename));
68   ASSERT_OK_AND_ASSIGN(auto public_key,
69                        DeserializePublicKey(&ec_group, public_key_proto));
70   ASSERT_OK_AND_ASSIGN(auto private_key,
71                        DeserializePrivateKey(&context, private_key_proto));
72   ASSERT_OK_AND_ASSIGN(auto product, public_key->g.Mul(private_key->x));
73   EXPECT_EQ(product, public_key->y);
74 }
75 
TEST(ElGamalKeyUtilTest,ComputeJointElGamalPublicKey)76 TEST(ElGamalKeyUtilTest, ComputeJointElGamalPublicKey) {
77   std::filesystem::path temp_dir(::testing::TempDir());
78   std::string pub_key_filename_1 = (temp_dir / "elgamal_pub1.key").string();
79   std::string prv_key_filename_1 = (temp_dir / "elgamal_prv1.key").string();
80   ASSERT_OK(GenerateElGamalKeyPair(kTestCurveId, pub_key_filename_1,
81                                    prv_key_filename_1));
82   std::string pub_key_filename_2 = (temp_dir / "elgamal_pub2.key").string();
83   std::string prv_key_filename_2 = (temp_dir / "elgamal_prv2.key").string();
84   ASSERT_OK(GenerateElGamalKeyPair(kTestCurveId, pub_key_filename_2,
85                                    prv_key_filename_2));
86   std::string joint_pub_key_filename =
87       (temp_dir / "joint_elgamal_pub.key").string();
88   std::vector<std::string> pub_key_shares{pub_key_filename_1,
89                                           pub_key_filename_2};
90   ASSERT_OK(ComputeJointElGamalPublicKey(kTestCurveId, pub_key_shares,
91                                          joint_pub_key_filename));
92   ASSERT_TRUE(std::filesystem::exists(joint_pub_key_filename));
93 
94   // Verify the joint key written to file is correct.
95   Context context;
96   ASSERT_OK_AND_ASSIGN(auto ec_group, ECGroup::Create(kTestCurveId, &context));
97   ASSERT_OK_AND_ASSIGN(
98       auto joint_public_key_proto,
99       ProtoUtils::ReadProtoFromFile<ElGamalPublicKey>(joint_pub_key_filename));
100   ASSERT_OK_AND_ASSIGN(auto joint_public_key,
101                        DeserializePublicKey(&ec_group, joint_public_key_proto));
102   ASSERT_OK_AND_ASSIGN(
103       auto share_1_proto,
104       ProtoUtils::ReadProtoFromFile<ElGamalPublicKey>(pub_key_filename_1));
105   ASSERT_OK_AND_ASSIGN(auto share_1,
106                        DeserializePublicKey(&ec_group, share_1_proto));
107   ASSERT_OK_AND_ASSIGN(
108       auto share_2_proto,
109       ProtoUtils::ReadProtoFromFile<ElGamalPublicKey>(pub_key_filename_2));
110   ASSERT_OK_AND_ASSIGN(auto share_2,
111                        DeserializePublicKey(&ec_group, share_2_proto));
112   std::vector<std::unique_ptr<elgamal::PublicKey>> key_shares;
113   key_shares.reserve(2);
114   key_shares.push_back(std::move(share_1));
115   key_shares.push_back(std::move(share_2));
116   ASSERT_OK_AND_ASSIGN(auto expected_joint_public_key,
117                        elgamal::GeneratePublicKeyFromShares(key_shares));
118   EXPECT_EQ(joint_public_key->g, expected_joint_public_key->g);
119   EXPECT_EQ(joint_public_key->y, expected_joint_public_key->y);
120 }
121 
TEST(ElGamalKeyUtilTest,TestEmptyKeyShares)122 TEST(ElGamalKeyUtilTest, TestEmptyKeyShares) {
123   std::vector<std::string> empty_key_shares;
124   std::filesystem::path temp_dir(::testing::TempDir());
125   std::string joint_pub_key_filename =
126       (temp_dir / "joint_elgamal_pub.key").string();
127   auto outcome = ComputeJointElGamalPublicKey(kTestCurveId, empty_key_shares,
128                                               joint_pub_key_filename);
129   EXPECT_TRUE(IsInvalidArgument(outcome));
130 }
131 
TEST(ElGamalKeyUtilTest,TestKeyReadWrite)132 TEST(ElGamalKeyUtilTest, TestKeyReadWrite) {
133   std::unique_ptr<Context> context(new Context);
134   ASSERT_OK_AND_ASSIGN(ECGroup group,
135                        ECGroup::Create(kTestCurveId, context.get()));
136   ASSERT_OK_AND_ASSIGN(
137       auto key_pair, private_join_and_compute::elgamal::GenerateKeyPair(group));
138   ASSERT_OK_AND_ASSIGN(
139       auto public_key_proto,
140       elgamal_proto_util::SerializePublicKey(*key_pair.first.get()));
141   ASSERT_OK_AND_ASSIGN(
142       auto private_key_proto,
143       elgamal_proto_util::SerializePrivateKey(*key_pair.second.get()));
144 
145   std::filesystem::path temp_dir(::testing::TempDir());
146   std::string pub_key_filename = (temp_dir / "elgamal_pub.key").string();
147   std::string prv_key_filename = (temp_dir / "elgamal_prv.key").string();
148 
149   // Verify write and read public key to file returns the expected key.
150   ASSERT_OK(ProtoUtils::WriteProtoToFile(public_key_proto, pub_key_filename));
151   ASSERT_OK_AND_ASSIGN(
152       auto public_key_proto_2,
153       ProtoUtils::ReadProtoFromFile<ElGamalPublicKey>(pub_key_filename));
154   EXPECT_EQ(public_key_proto.g(), public_key_proto_2.g());
155   EXPECT_EQ(public_key_proto.y(), public_key_proto_2.y());
156 
157   // Verify write and read private key to file returns the expected key.
158   ASSERT_OK(ProtoUtils::WriteProtoToFile(private_key_proto, prv_key_filename));
159   ASSERT_OK_AND_ASSIGN(
160       auto private_key_proto_2,
161       ProtoUtils::ReadProtoFromFile<ElGamalSecretKey>(prv_key_filename));
162   EXPECT_EQ(private_key_proto.x(), private_key_proto_2.x());
163 }
164 
165 }  // namespace
166 }  // namespace private_join_and_compute::elgamal_key_util
167