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/elgamal.h"
17
18 #include <memory>
19 #include <utility>
20 #include <vector>
21
22 #include "absl/log/check.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/util/status.inc"
27
28 namespace private_join_and_compute {
29
30 namespace elgamal {
31
32 StatusOr<std::pair<std::unique_ptr<PublicKey>, std::unique_ptr<PrivateKey>>>
GenerateKeyPair(const ECGroup & ec_group)33 GenerateKeyPair(const ECGroup& ec_group) {
34 ASSIGN_OR_RETURN(ECPoint g, ec_group.GetFixedGenerator());
35 BigNum x = ec_group.GeneratePrivateKey();
36 ASSIGN_OR_RETURN(ECPoint y, g.Mul(x));
37
38 std::unique_ptr<PublicKey> public_key(
39 new PublicKey({std::move(g), std::move(y)}));
40 std::unique_ptr<PrivateKey> private_key(new PrivateKey({std::move(x)}));
41
42 return {{std::move(public_key), std::move(private_key)}};
43 }
44
GeneratePublicKeyFromShares(const std::vector<std::unique_ptr<elgamal::PublicKey>> & shares)45 StatusOr<std::unique_ptr<PublicKey>> GeneratePublicKeyFromShares(
46 const std::vector<std::unique_ptr<elgamal::PublicKey>>& shares) {
47 if (shares.empty()) {
48 return InvalidArgumentError(
49 "ElGamal::GeneratePublicKeyFromShares() : empty shares provided");
50 }
51 ASSIGN_OR_RETURN(ECPoint g, (*shares.begin())->g.Clone());
52 ASSIGN_OR_RETURN(ECPoint y, (*shares.begin())->y.Clone());
53 for (size_t i = 1; i < shares.size(); i++) {
54 CHECK(g.CompareTo((*shares.at(i)).g))
55 << "Invalid public key shares provided with different generators g";
56 ASSIGN_OR_RETURN(y, y.Add((*shares.at(i)).y));
57 }
58
59 return absl::WrapUnique(new PublicKey({std::move(g), std::move(y)}));
60 }
61
Mul(const elgamal::Ciphertext & ciphertext1,const elgamal::Ciphertext & ciphertext2)62 StatusOr<elgamal::Ciphertext> Mul(const elgamal::Ciphertext& ciphertext1,
63 const elgamal::Ciphertext& ciphertext2) {
64 ASSIGN_OR_RETURN(ECPoint u, ciphertext1.u.Add(ciphertext2.u));
65 ASSIGN_OR_RETURN(ECPoint e, ciphertext1.e.Add(ciphertext2.e));
66 return {{std::move(u), std::move(e)}};
67 }
68
Exp(const elgamal::Ciphertext & ciphertext,const BigNum & scalar)69 StatusOr<elgamal::Ciphertext> Exp(const elgamal::Ciphertext& ciphertext,
70 const BigNum& scalar) {
71 ASSIGN_OR_RETURN(ECPoint u, ciphertext.u.Mul(scalar));
72 ASSIGN_OR_RETURN(ECPoint e, ciphertext.e.Mul(scalar));
73 return {{std::move(u), std::move(e)}};
74 }
75
GetZero(const ECGroup * group)76 StatusOr<Ciphertext> GetZero(const ECGroup* group) {
77 ASSIGN_OR_RETURN(ECPoint u, group->GetPointAtInfinity());
78 ASSIGN_OR_RETURN(ECPoint e, group->GetPointAtInfinity());
79 return {{std::move(u), std::move(e)}};
80 }
81
CloneCiphertext(const Ciphertext & ciphertext)82 StatusOr<Ciphertext> CloneCiphertext(const Ciphertext& ciphertext) {
83 ASSIGN_OR_RETURN(ECPoint clone_u, ciphertext.u.Clone());
84 ASSIGN_OR_RETURN(ECPoint clone_e, ciphertext.e.Clone());
85 return {{std::move(clone_u), std::move(clone_e)}};
86 }
87
IsCiphertextZero(const Ciphertext & ciphertext)88 bool IsCiphertextZero(const Ciphertext& ciphertext) {
89 return ciphertext.u.IsPointAtInfinity() && ciphertext.e.IsPointAtInfinity();
90 }
91
92 } // namespace elgamal
93
94 ////////////////////////////////////////////////////////////////////////////////
95 // PUBLIC ELGAMAL
96 ////////////////////////////////////////////////////////////////////////////////
97
ElGamalEncrypter(const ECGroup * ec_group,std::unique_ptr<elgamal::PublicKey> elgamal_public_key)98 ElGamalEncrypter::ElGamalEncrypter(
99 const ECGroup* ec_group,
100 std::unique_ptr<elgamal::PublicKey> elgamal_public_key)
101 : ec_group_(ec_group), public_key_(std::move(elgamal_public_key)) {}
102
103 // Encrypts a message m, that has already been mapped onto the curve.
Encrypt(const ECPoint & message) const104 StatusOr<elgamal::Ciphertext> ElGamalEncrypter::Encrypt(
105 const ECPoint& message) const {
106 BigNum r = ec_group_->GeneratePrivateKey(); // generate a random exponent
107 // u = g^r , e = m * y^r .
108 ASSIGN_OR_RETURN(ECPoint u, public_key_->g.Mul(r));
109 ASSIGN_OR_RETURN(ECPoint y_to_r, public_key_->y.Mul(r));
110 ASSIGN_OR_RETURN(ECPoint e, message.Add(y_to_r));
111 return {{std::move(u), std::move(e)}};
112 }
113
ReRandomize(const elgamal::Ciphertext & elgamal_ciphertext) const114 StatusOr<elgamal::Ciphertext> ElGamalEncrypter::ReRandomize(
115 const elgamal::Ciphertext& elgamal_ciphertext) const {
116 BigNum r = ec_group_->GeneratePrivateKey(); // generate a random exponent
117 // u = old_u * g^r , e = old_e * y^r .
118 ASSIGN_OR_RETURN(ECPoint g_to_r, public_key_->g.Mul(r));
119 ASSIGN_OR_RETURN(ECPoint u, elgamal_ciphertext.u.Add(g_to_r));
120 ASSIGN_OR_RETURN(ECPoint y_to_r, public_key_->y.Mul(r));
121 ASSIGN_OR_RETURN(ECPoint e, elgamal_ciphertext.e.Add(y_to_r));
122 return {{std::move(u), std::move(e)}};
123 }
124
125 ////////////////////////////////////////////////////////////////////////////////
126 // PRIVATE ELGAMAL
127 ////////////////////////////////////////////////////////////////////////////////
128
ElGamalDecrypter(std::unique_ptr<elgamal::PrivateKey> elgamal_private_key)129 ElGamalDecrypter::ElGamalDecrypter(
130 std::unique_ptr<elgamal::PrivateKey> elgamal_private_key)
131 : private_key_(std::move(elgamal_private_key)) {}
132
Decrypt(const elgamal::Ciphertext & ciphertext) const133 StatusOr<ECPoint> ElGamalDecrypter::Decrypt(
134 const elgamal::Ciphertext& ciphertext) const {
135 ASSIGN_OR_RETURN(ECPoint u_to_x, ciphertext.u.Mul(private_key_->x));
136 ASSIGN_OR_RETURN(ECPoint u_to_x_inverse, u_to_x.Inverse());
137 ASSIGN_OR_RETURN(ECPoint message, ciphertext.e.Add(u_to_x_inverse));
138 return {std::move(message)};
139 }
140
PartialDecrypt(const elgamal::Ciphertext & ciphertext) const141 StatusOr<elgamal::Ciphertext> ElGamalDecrypter::PartialDecrypt(
142 const elgamal::Ciphertext& ciphertext) const {
143 ASSIGN_OR_RETURN(ECPoint clone_u, ciphertext.u.Clone());
144 ASSIGN_OR_RETURN(ECPoint dec_e, ElGamalDecrypter::Decrypt(ciphertext));
145 return {{std::move(clone_u), std::move(dec_e)}};
146 }
147
148 } // namespace private_join_and_compute
149