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/ec_commutative_cipher.h"
17 
18 #include <memory>
19 #include <string>
20 #include <utility>
21 
22 #include "private_join_and_compute/crypto/big_num.h"
23 #include "private_join_and_compute/crypto/ec_group.h"
24 #include "private_join_and_compute/crypto/elgamal.h"
25 #include "private_join_and_compute/util/status.inc"
26 
27 namespace private_join_and_compute {
28 
29 namespace {
30 
31 constexpr absl::string_view kEcCommutativeCipherDst = "ECCommutativeCipher";
32 
33 // Invert private scalar using Fermat's Little Theorem to avoid side-channel
34 // attacks. This avoids the caveat of ModInverseBlinded, namely that the
35 // underlying BN_mod_inverse_blinded is not available on all platforms.
InvertPrivateScalar(const BigNum & scalar,const ECGroup & ec_group,Context & context)36 BigNum InvertPrivateScalar(const BigNum& scalar, const ECGroup& ec_group,
37                            Context& context) {
38   const BigNum& order = ec_group.GetOrder();
39   return scalar.ModExp(order.Sub(context.Two()), order);
40 }
41 
42 }  // namespace
43 
ECCommutativeCipher(std::unique_ptr<Context> context,ECGroup group,BigNum private_key,HashType hash_type)44 ECCommutativeCipher::ECCommutativeCipher(std::unique_ptr<Context> context,
45                                          ECGroup group, BigNum private_key,
46                                          HashType hash_type)
47     : context_(std::move(context)),
48       group_(std::move(group)),
49       private_key_(std::move(private_key)),
50       private_key_inverse_(
51           InvertPrivateScalar(private_key_, group_, *context_)),
52       hash_type_(hash_type) {}
53 
ValidateHashType(HashType hash_type)54 bool ECCommutativeCipher::ValidateHashType(HashType hash_type) {
55   return (hash_type == SHA256 || hash_type == SHA384 || hash_type == SHA512 ||
56           hash_type == SSWU_RO);
57 }
58 
ValidateHashType(int hash_type)59 bool ECCommutativeCipher::ValidateHashType(int hash_type) {
60   return hash_type >= SHA256 && hash_type <= SSWU_RO;
61 }
62 
63 StatusOr<std::unique_ptr<ECCommutativeCipher>>
CreateWithNewKey(int curve_id,HashType hash_type)64 ECCommutativeCipher::CreateWithNewKey(int curve_id, HashType hash_type) {
65   std::unique_ptr<Context> context(new Context);
66   ASSIGN_OR_RETURN(ECGroup group, ECGroup::Create(curve_id, context.get()));
67   if (!ECCommutativeCipher::ValidateHashType(hash_type)) {
68     return InvalidArgumentError("Invalid hash type.");
69   }
70   BigNum private_key = group.GeneratePrivateKey();
71   return std::unique_ptr<ECCommutativeCipher>(new ECCommutativeCipher(
72       std::move(context), std::move(group), std::move(private_key), hash_type));
73 }
74 
75 StatusOr<std::unique_ptr<ECCommutativeCipher>>
CreateFromKey(int curve_id,absl::string_view key_bytes,HashType hash_type)76 ECCommutativeCipher::CreateFromKey(int curve_id, absl::string_view key_bytes,
77                                    HashType hash_type) {
78   std::unique_ptr<Context> context(new Context);
79   ASSIGN_OR_RETURN(ECGroup group, ECGroup::Create(curve_id, context.get()));
80   if (!ECCommutativeCipher::ValidateHashType(hash_type)) {
81     return InvalidArgumentError("Invalid hash type.");
82   }
83   BigNum private_key = context->CreateBigNum(key_bytes);
84   auto status = group.CheckPrivateKey(private_key);
85   if (!status.ok()) {
86     return status;
87   }
88   return std::unique_ptr<ECCommutativeCipher>(new ECCommutativeCipher(
89       std::move(context), std::move(group), std::move(private_key), hash_type));
90 }
91 
92 StatusOr<std::unique_ptr<ECCommutativeCipher>>
CreateWithKeyFromSeed(int curve_id,absl::string_view seed_bytes,absl::string_view tag_bytes,HashType hash_type)93 ECCommutativeCipher::CreateWithKeyFromSeed(int curve_id,
94                                            absl::string_view seed_bytes,
95                                            absl::string_view tag_bytes,
96                                            HashType hash_type) {
97   std::unique_ptr<Context> context(new Context);
98   ASSIGN_OR_RETURN(ECGroup group, ECGroup::Create(curve_id, context.get()));
99   if (seed_bytes.size() < 16) {
100     return InvalidArgumentError("Seed is too short.");
101   }
102   if (!ECCommutativeCipher::ValidateHashType(hash_type)) {
103     return InvalidArgumentError("Invalid hash type.");
104   }
105   BigNum private_key = context->PRF(seed_bytes, tag_bytes, group.GetOrder());
106   return std::unique_ptr<ECCommutativeCipher>(new ECCommutativeCipher(
107       std::move(context), std::move(group), std::move(private_key), hash_type));
108 }
109 
Encrypt(absl::string_view plaintext)110 StatusOr<std::string> ECCommutativeCipher::Encrypt(
111     absl::string_view plaintext) {
112   ASSIGN_OR_RETURN(ECPoint hashed_point, HashToTheCurveInternal(plaintext));
113   ASSIGN_OR_RETURN(ECPoint encrypted_point, Encrypt(hashed_point));
114   return encrypted_point.ToBytesCompressed();
115 }
116 
ReEncrypt(absl::string_view ciphertext)117 StatusOr<std::string> ECCommutativeCipher::ReEncrypt(
118     absl::string_view ciphertext) {
119   ASSIGN_OR_RETURN(ECPoint point, group_.CreateECPoint(ciphertext));
120   ASSIGN_OR_RETURN(ECPoint reencrypted_point, Encrypt(point));
121   return reencrypted_point.ToBytesCompressed();
122 }
123 
Encrypt(const ECPoint & point)124 StatusOr<ECPoint> ECCommutativeCipher::Encrypt(const ECPoint& point) {
125   return point.Mul(private_key_);
126 }
127 
128 StatusOr<std::pair<std::string, std::string>>
ReEncryptElGamalCiphertext(const std::pair<std::string,std::string> & elgamal_ciphertext)129 ECCommutativeCipher::ReEncryptElGamalCiphertext(
130     const std::pair<std::string, std::string>& elgamal_ciphertext) {
131   ASSIGN_OR_RETURN(ECPoint u, group_.CreateECPoint(elgamal_ciphertext.first));
132   ASSIGN_OR_RETURN(ECPoint e, group_.CreateECPoint(elgamal_ciphertext.second));
133 
134   elgamal::Ciphertext decoded_ciphertext = {std::move(u), std::move(e)};
135 
136   ASSIGN_OR_RETURN(elgamal::Ciphertext reencrypted_ciphertext,
137                    elgamal::Exp(decoded_ciphertext, private_key_));
138 
139   ASSIGN_OR_RETURN(std::string serialized_u,
140                    reencrypted_ciphertext.u.ToBytesCompressed());
141   ASSIGN_OR_RETURN(std::string serialized_e,
142                    reencrypted_ciphertext.e.ToBytesCompressed());
143 
144   return std::make_pair(std::move(serialized_u), std::move(serialized_e));
145 }
146 
Decrypt(absl::string_view ciphertext)147 StatusOr<std::string> ECCommutativeCipher::Decrypt(
148     absl::string_view ciphertext) {
149   ASSIGN_OR_RETURN(ECPoint point, group_.CreateECPoint(ciphertext));
150   ASSIGN_OR_RETURN(ECPoint decrypted_point, point.Mul(private_key_inverse_));
151   return decrypted_point.ToBytesCompressed();
152 }
153 
154 ::private_join_and_compute::StatusOr<ECPoint>
HashToTheCurveInternal(absl::string_view plaintext)155 ECCommutativeCipher::HashToTheCurveInternal(absl::string_view plaintext) {
156   StatusOr<ECPoint> status_or_point;
157   if (hash_type_ == SHA512) {
158     status_or_point = group_.GetPointByHashingToCurveSha512(plaintext);
159   } else if (hash_type_ == SHA384) {
160     status_or_point = group_.GetPointByHashingToCurveSha384(plaintext);
161   } else if (hash_type_ == SHA256) {
162     status_or_point = group_.GetPointByHashingToCurveSha256(plaintext);
163   } else if (hash_type_ == SSWU_RO) {
164     status_or_point = group_.GetPointByHashingToCurveSswuRo(
165         plaintext, kEcCommutativeCipherDst);
166   } else {
167     return InvalidArgumentError("Invalid hash type.");
168   }
169   return status_or_point;
170 }
171 
172 ::private_join_and_compute::StatusOr<std::string>
HashToTheCurve(absl::string_view plaintext)173 ECCommutativeCipher::HashToTheCurve(absl::string_view plaintext) {
174   ASSIGN_OR_RETURN(ECPoint point, HashToTheCurveInternal(plaintext));
175   return point.ToBytesCompressed();
176 }
177 
GetPrivateKeyBytes() const178 std::string ECCommutativeCipher::GetPrivateKeyBytes() const {
179   return private_key_.ToBytes();
180 }
181 
182 }  // namespace private_join_and_compute
183