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 #ifndef PRIVATE_JOIN_AND_COMPUTE_EC_COMMUTATIVE_CIPHER_H_
17 #define PRIVATE_JOIN_AND_COMPUTE_EC_COMMUTATIVE_CIPHER_H_
18 
19 #include <memory>
20 #include <string>
21 #include <utility>
22 
23 #include "absl/strings/string_view.h"
24 #include "private_join_and_compute/crypto/big_num.h"
25 #include "private_join_and_compute/crypto/context.h"
26 #include "private_join_and_compute/crypto/ec_group.h"
27 #include "private_join_and_compute/crypto/ec_point.h"
28 #include "private_join_and_compute/util/status.inc"
29 
30 namespace private_join_and_compute {
31 
32 // ECCommutativeCipher class with the property that K1(K2(a)) = K2(K1(a))
33 // where K(a) is encryption with the key K. https://eprint.iacr.org/2008/356.pdf
34 //
35 // This class allows two parties to determine if they share the same value,
36 // without revealing the sensitive value to each other.
37 //
38 // This class also allows homomorphically re-encrypting an ElGamal ciphertext
39 // with an EC cipher key K. If the original ciphertext was an encryption of m,
40 // then the re-encrypted ciphertext is effectively an encryption of K(m). This
41 // re-encryption does not re-randomize the ciphertext, and so is only secure
42 // when the underlying messages "m" are pseudorandom.
43 //
44 // The encryption is performed over an elliptic curve.
45 //
46 // This class is not thread-safe.
47 //
48 // Security: The provided bit security is half the number of bits of the
49 //  underlying curve. For example, using curve NID_X9_62_prime256v1 gives 128
50 //  bit security.
51 //
52 // Example: To generate a cipher with a new private key for the named curve
53 //    NID_X9_62_prime256v1. The key can be securely stored and reused.
54 //    #include <openssl/obj_mac.h>
55 //    std::unique_ptr<ECCommutativeCipher> cipher =
56 //        ECCommutativeCipher::CreateWithNewKey(
57 //            NID_X9_62_prime256v1, ECCommutativeCipher::HashType::SSWU_RO);
58 //    string key_bytes = cipher->GetPrivateKeyBytes();
59 //
60 //  Example: To generate a cipher with an existing private key for the named
61 //    curve NID_X9_62_prime256v1.
62 //    #include <openssl/obj_mac.h>
63 //    std::unique_ptr<ECCommutativeCipher> cipher =
64 //        ECCommutativeCipher::CreateFromKey(
65 //            NID_X9_62_prime256v1, key_bytes,
66 //            ECCommutativeCipher::HashType::SSWU_RO);
67 //
68 // Example: To encrypt a message using a std::unique_ptr<ECCommutativeCipher>
69 //    cipher generated as above.
70 //    string encrypted_string = cipher->Encrypt("secret");
71 //
72 // Example: To re-encrypt a message already encrypted by another party using a
73 //    std::unique_ptr<ECCommutativeCipher> cipher generated as above.
74 //    ::private_join_and_compute::StatusOr<string> double_encrypted_string =
75 //        cipher->ReEncrypt(encrypted_string);
76 //
77 // Example: To decrypt a message that has already been encrypted by the same
78 //    party using a std::unique_ptr<ECCommutativeCipher> cipher generated as
79 //    above.
80 //    ::private_join_and_compute::StatusOr<string> decrypted_string =
81 //        cipher->Decrypt(encrypted_string);
82 //
83 // Example: To re-encrypt a message that has already been encrypted using a
84 // std::unique_ptr<CommutativeElGamal> ElGamal key:
85 //    ::private_join_and_compute::StatusOr<std::pair<string, string>>
86 //    double_encrypted_string =
87 //        cipher->ReEncryptElGamalCiphertext(elgamal_ciphertext);
88 
89 class ECCommutativeCipher {
90  public:
91   // The hash function used by the ECCommutativeCipher in order to hash strings
92   // to EC curve points.
93   enum HashType {
94     SHA256,
95     SHA384,
96     SHA512,
97     SSWU_RO,
98   };
99 
100   // Check for valid HashType.
101   static bool ValidateHashType(HashType hash_type);
102 
103   // Check for valid HashType.
104   static bool ValidateHashType(int hash_type);
105 
106   // ECCommutativeCipher is neither copyable nor assignable.
107   ECCommutativeCipher(const ECCommutativeCipher&) = delete;
108   ECCommutativeCipher& operator=(const ECCommutativeCipher&) = delete;
109 
110   // Creates an ECCommutativeCipher object with a new random private key.
111   // Use this method when the key is created for the first time or it needs to
112   // be refreshed.
113   // Returns INVALID_ARGUMENT status instead if the curve_id is not valid
114   // or INTERNAL status when crypto operations are not successful.
115   static ::private_join_and_compute::StatusOr<
116       std::unique_ptr<ECCommutativeCipher>>
117   CreateWithNewKey(int curve_id, HashType hash_type);
118 
119   // Creates an ECCommutativeCipher object with the given private key.
120   // A new key should be created for each session and all values should be
121   // unique in one session because the encryption is deterministic.
122   // Use this when the key is stored securely to be used at different steps of
123   // the protocol in the same session or by multiple processes.
124   // Returns INVALID_ARGUMENT status instead if the private_key is not valid for
125   // the given curve or the curve_id is not valid.
126   // Returns INTERNAL status when crypto operations are not successful.
127   static ::private_join_and_compute::StatusOr<
128       std::unique_ptr<ECCommutativeCipher>>
129   CreateFromKey(int curve_id, absl::string_view key_bytes, HashType hash_type);
130 
131   // Creates an ECCommutativeCipher object with a new private key generated from
132   // the given seed and index.  This will deterministically generate a key and
133   // this should not be re-used across multiple sessions.  The seed should be a
134   // cryptographically strong random string of at least 16 bytes.
135   // Returns INTERNAL status when crypto operations are not successful.
136   static ::private_join_and_compute::StatusOr<
137       std::unique_ptr<ECCommutativeCipher>>
138   CreateWithKeyFromSeed(int curve_id, absl::string_view seed_bytes,
139                         absl::string_view tag_bytes, HashType hash_type);
140 
141   // Encrypts a string with the private key to a point on the elliptic curve.
142   //
143   // To encrypt, the string is hashed to a point on the curve which is then
144   // multiplied with the private key.
145   //
146   // The resulting point is returned encoded in compressed form as defined in
147   // ANSI X9.62 ECDSA.
148   //
149   // Returns an INVALID_ARGUMENT error code if an error occurs.
150   //
151   // This method is not threadsafe.
152   ::private_join_and_compute::StatusOr<std::string> Encrypt(
153       absl::string_view plaintext);
154 
155   // Encrypts an encoded point with the private key.
156   //
157   // Returns an INVALID_ARGUMENT error code if the input is not a valid encoding
158   // of a point on this curve as defined in ANSI X9.62 ECDSA.
159   //
160   // The result is a point encoded in compressed form.
161   //
162   // This method can also be used to encrypt a value that has already been
163   // hashed to the curve.
164   //
165   // This method is not threadsafe.
166   ::private_join_and_compute::StatusOr<std::string> ReEncrypt(
167       absl::string_view ciphertext);
168 
169   // Encrypts an ElGamal ciphertext with the private key.
170   //
171   // Returns an INVALID_ARGUMENT error code if the input is not a valid encoding
172   // of an ElGamal ciphertext on this curve as defined in ANSI X9.62 ECDSA.
173   //
174   // The result is another ElGamal ciphertext, encoded in compressed form.
175   //
176   // This method is not threadsafe.
177   ::private_join_and_compute::StatusOr<std::pair<std::string, std::string>>
178   ReEncryptElGamalCiphertext(
179       const std::pair<std::string, std::string>& elgamal_ciphertext);
180 
181   // Decrypts an encoded point with the private key.
182   //
183   // Returns an INVALID_ARGUMENT error code if the input is not a valid encoding
184   // of a point on this curve as defined in ANSI X9.62 ECDSA.
185   //
186   // The result is a point encoded in compressed form.
187   //
188   // If the input point was double-encrypted, once with this key and once with
189   // another key, then the result point is single-encrypted with the other key.
190   //
191   // If the input point was single encrypted with this key, then the result
192   // point is the original, unencrypted point. Note that this will not reverse
193   // hashing to the curve.
194   //
195   // This method is not threadsafe.
196   ::private_join_and_compute::StatusOr<std::string> Decrypt(
197       absl::string_view ciphertext);
198 
199   // Hashes a string to a point on the elliptic curve using the
200   // "try-and-increment" method.
201   // See Section 5.2 of https://crypto.stanford.edu/~dabo/papers/bfibe.pdf.
202   //
203   // The resulting point is returned encoded in compressed form as defined in
204   // ANSI X9.62 ECDSA.
205   //
206   // Returns an INVALID_ARGUMENT error code if an error occurs.
207   //
208   // This method is not threadsafe.
209   ::private_join_and_compute::StatusOr<std::string> HashToTheCurve(
210       absl::string_view plaintext);
211 
212   // Returns the private key bytes so the key can be stored and reused.
213   std::string GetPrivateKeyBytes() const;
214 
215  private:
216   // Creates a new ECCommutativeCipher object with the given private key for
217   // the given EC group.
218   ECCommutativeCipher(std::unique_ptr<Context> context, ECGroup group,
219                       BigNum private_key, HashType hash_type);
220 
221   // Encrypts a point by multiplying the point with the private key.
222   ::private_join_and_compute::StatusOr<ECPoint> Encrypt(const ECPoint& point);
223 
224   // Hashes a string to a point on the elliptic curve.
225   ::private_join_and_compute::StatusOr<ECPoint> HashToTheCurveInternal(
226       absl::string_view plaintext);
227 
228   // Context used for storing temporary values to be reused across openssl
229   // function calls for better performance.
230   std::unique_ptr<Context> context_;
231 
232   // The EC Group representing the curve definition.
233   const ECGroup group_;
234 
235   // The private key used for encryption.
236   const BigNum private_key_;
237 
238   // The private key inverse, used for decryption.
239   const BigNum private_key_inverse_;
240 
241   // The hash function used by the cipher.
242   const HashType hash_type_;
243 };
244 
245 }  // namespace private_join_and_compute
246 
247 #endif  // PRIVATE_JOIN_AND_COMPUTE_EC_COMMUTATIVE_CIPHER_H_
248