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/crypto/commutative_elgamal.h"
17 
18 #include <memory>
19 #include <string>
20 #include <utility>
21 
22 #include "absl/strings/string_view.h"
23 #include "private_join_and_compute/crypto/big_num.h"
24 #include "private_join_and_compute/crypto/ec_group.h"
25 #include "private_join_and_compute/crypto/ec_point.h"
26 #include "private_join_and_compute/crypto/elgamal.h"
27 #include "private_join_and_compute/util/status.inc"
28 
29 namespace private_join_and_compute {
30 
CommutativeElGamal(std::unique_ptr<Context> ctx,ECGroup group,std::unique_ptr<elgamal::PublicKey> elgamal_public_key,std::unique_ptr<elgamal::PrivateKey> elgamal_private_key)31 CommutativeElGamal::CommutativeElGamal(
32     std::unique_ptr<Context> ctx, ECGroup group,
33     std::unique_ptr<elgamal::PublicKey> elgamal_public_key,
34     std::unique_ptr<elgamal::PrivateKey> elgamal_private_key)
35     : context_(std::move(ctx)),
36       group_(std::move(group)),
37       encrypter_(new ElGamalEncrypter(&group_, std::move(elgamal_public_key))),
38       decrypter_(new ElGamalDecrypter(std::move(elgamal_private_key))) {}
39 
CommutativeElGamal(std::unique_ptr<Context> ctx,ECGroup group,std::unique_ptr<elgamal::PublicKey> elgamal_public_key)40 CommutativeElGamal::CommutativeElGamal(
41     std::unique_ptr<Context> ctx, ECGroup group,
42     std::unique_ptr<elgamal::PublicKey> elgamal_public_key)
43     : context_(std::move(ctx)),
44       group_(std::move(group)),
45       encrypter_(new ElGamalEncrypter(&group_, std::move(elgamal_public_key))),
46       decrypter_(nullptr) {}
47 
48 StatusOr<std::unique_ptr<CommutativeElGamal>>
CreateWithNewKeyPair(int curve_id)49 CommutativeElGamal::CreateWithNewKeyPair(int curve_id) {
50   std::unique_ptr<Context> context(new Context);
51   ASSIGN_OR_RETURN(ECGroup group, ECGroup::Create(curve_id, context.get()));
52   ASSIGN_OR_RETURN(auto key_pair, elgamal::GenerateKeyPair(group));
53   std::unique_ptr<CommutativeElGamal> result(new CommutativeElGamal(
54       std::move(context), std::move(group), std::move(key_pair.first),
55       std::move(key_pair.second)));
56   return {std::move(result)};
57 }
58 
59 StatusOr<std::unique_ptr<CommutativeElGamal>>
CreateFromPublicKey(int curve_id,const std::pair<std::string,std::string> & public_key_bytes)60 CommutativeElGamal::CreateFromPublicKey(
61     int curve_id, const std::pair<std::string, std::string>& public_key_bytes) {
62   std::unique_ptr<Context> context(new Context);
63   ASSIGN_OR_RETURN(ECGroup group, ECGroup::Create(curve_id, context.get()));
64 
65   ASSIGN_OR_RETURN(ECPoint g, group.CreateECPoint(public_key_bytes.first));
66   ASSIGN_OR_RETURN(ECPoint y, group.CreateECPoint(public_key_bytes.second));
67 
68   std::unique_ptr<elgamal::PublicKey> public_key(
69       new elgamal::PublicKey({std::move(g), std::move(y)}));
70   std::unique_ptr<CommutativeElGamal> result(new CommutativeElGamal(
71       std::move(context), std::move(group), std::move(public_key)));
72   return {std::move(result)};
73 }
74 
75 StatusOr<std::unique_ptr<CommutativeElGamal>>
CreateFromPublicAndPrivateKeys(int curve_id,const std::pair<std::string,std::string> & public_key_bytes,absl::string_view private_key_bytes)76 CommutativeElGamal::CreateFromPublicAndPrivateKeys(
77     int curve_id, const std::pair<std::string, std::string>& public_key_bytes,
78     absl::string_view private_key_bytes) {
79   std::unique_ptr<Context> context(new Context);
80   ASSIGN_OR_RETURN(ECGroup group, ECGroup::Create(curve_id, context.get()));
81 
82   ASSIGN_OR_RETURN(ECPoint g, group.CreateECPoint(public_key_bytes.first));
83   ASSIGN_OR_RETURN(ECPoint y, group.CreateECPoint(public_key_bytes.second));
84 
85   BigNum x = context->CreateBigNum(private_key_bytes);
86 
87   ASSIGN_OR_RETURN(ECPoint expected_y, g.Mul(x));
88 
89   if (y != expected_y) {
90     return InvalidArgumentError(
91         "CommutativeElGamal::CreateFromPublicAndPrivateKeys : Public key is "
92         "not consistent with private key");
93   }
94 
95   std::unique_ptr<elgamal::PublicKey> public_key(
96       new elgamal::PublicKey({std::move(g), std::move(y)}));
97   std::unique_ptr<elgamal::PrivateKey> private_key(
98       new elgamal::PrivateKey({std::move(x)}));
99   std::unique_ptr<CommutativeElGamal> result(
100       new CommutativeElGamal(std::move(context), std::move(group),
101                              std::move(public_key), std::move(private_key)));
102   return {std::move(result)};
103 }
104 
Encrypt(absl::string_view plaintext) const105 StatusOr<std::pair<std::string, std::string>> CommutativeElGamal::Encrypt(
106     absl::string_view plaintext) const {
107   ASSIGN_OR_RETURN(ECPoint plaintext_point, group_.CreateECPoint(plaintext));
108 
109   ASSIGN_OR_RETURN(elgamal::Ciphertext ciphertext,
110                    encrypter_->Encrypt(plaintext_point));
111 
112   ASSIGN_OR_RETURN(std::string u_string, ciphertext.u.ToBytesCompressed());
113   ASSIGN_OR_RETURN(std::string e_string, ciphertext.e.ToBytesCompressed());
114 
115   return {std::make_pair(std::move(u_string), std::move(e_string))};
116 }
117 
118 StatusOr<std::pair<std::string, std::string>>
EncryptIdentityElement() const119 CommutativeElGamal::EncryptIdentityElement() const {
120   ASSIGN_OR_RETURN(ECPoint plaintext_point, group_.GetPointAtInfinity());
121 
122   ASSIGN_OR_RETURN(elgamal::Ciphertext ciphertext,
123                    encrypter_->Encrypt(plaintext_point));
124 
125   ASSIGN_OR_RETURN(std::string u_string, ciphertext.u.ToBytesCompressed());
126   ASSIGN_OR_RETURN(std::string e_string, ciphertext.e.ToBytesCompressed());
127 
128   return {std::make_pair(std::move(u_string), std::move(e_string))};
129 }
130 
Decrypt(const std::pair<std::string,std::string> & ciphertext) const131 StatusOr<std::string> CommutativeElGamal::Decrypt(
132     const std::pair<std::string, std::string>& ciphertext) const {
133   if (nullptr == decrypter_) {
134     return InvalidArgumentError(
135         "CommutativeElGamal::Decrypt: cannot decrypt without the private key.");
136   }
137 
138   ASSIGN_OR_RETURN(ECPoint u_point, group_.CreateECPoint(ciphertext.first));
139   ASSIGN_OR_RETURN(ECPoint e_point, group_.CreateECPoint(ciphertext.second));
140   elgamal::Ciphertext decoded_ciphertext(
141       {std::move(u_point), std::move(e_point)});
142 
143   ASSIGN_OR_RETURN(ECPoint plaintext_point,
144                    decrypter_->Decrypt(decoded_ciphertext));
145 
146   ASSIGN_OR_RETURN(std::string plaintext, plaintext_point.ToBytesCompressed());
147 
148   return {std::move(plaintext)};
149 }
150 
151 StatusOr<std::pair<std::string, std::string>>
GetPublicKeyBytes() const152 CommutativeElGamal::GetPublicKeyBytes() const {
153   const elgamal::PublicKey* public_key = encrypter_->getPublicKey();
154   ASSIGN_OR_RETURN(std::string g_string, public_key->g.ToBytesCompressed());
155   ASSIGN_OR_RETURN(std::string y_string, public_key->y.ToBytesCompressed());
156 
157   return {std::make_pair(std::move(g_string), std::move(y_string))};
158 }
159 
GetPrivateKeyBytes() const160 StatusOr<std::string> CommutativeElGamal::GetPrivateKeyBytes() const {
161   if (nullptr == decrypter_) {
162     return InvalidArgumentError(
163         "CommutativeElGamal::GetPrivateKeyBytes: private key is not known.");
164   }
165   return {decrypter_->getPrivateKey()->x.ToBytes()};
166 }
167 
168 }  // namespace private_join_and_compute
169