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