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_COMMUTATIVE_ELGAMAL_H_
17 #define PRIVATE_JOIN_AND_COMPUTE_COMMUTATIVE_ELGAMAL_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/elgamal.h"
25 #include "private_join_and_compute/util/status.inc"
26 
27 // Defines functions to generate ElGamal public/private keys, and
28 // to encrypt/decrypt messages using those keys.
29 // The ciphertexts thus produced are "commutative" with ec_commutative_cipher.
30 // That is, one can perform an ElGamal encryption, followed by an EC encryption,
31 // followed by decryptions in any order. Note that we only support one level of
32 // ElGamal encryption (and any number of levels of EC encryption.)
33 //
34 // This class is NOT thread-safe.
35 //
36 // Example: To generate a with new public/private ElGamal key pair for the named
37 // curve NID_X9_62_prime256v1. The key can be securely stored and reused.
38 //    #include <openssl/obj_mac.h>
39 //    std::unique_ptr<CommutativeElGamal> elgamal =
40 //       CommutativeElGamal::CreateWithNewKeyPair(NID_X9_62_prime256v1).value();
41 //    StatusOr<stringpair> public_key_bytes = elgamal->GetPublicKeyBytes();
42 //    StatusOr<string> private_key_bytes = elgamal->GetPrivateKeyBytes();
43 //
44 //  Example: To generate a cipher with an existing public/private key pair for
45 //  the named curve NID_X9_62_prime256v1.
46 //    #include <openssl/obj_mac.h>
47 //    StatusOr<std::unique_ptr<CommutativeElGamal>> elgamal =
48 //        CommutativeElGamal::CreateFromPublicAndPrivateKeys(NID_X9_62_prime256v1,
49 //        public_key_bytes, private_key_bytes);
50 //
51 //  Example: To generate a cipher with an existing public key _only_ for
52 //  the named curve NID_X9_62_prime256v1. The resulting object can only encrypt,
53 //  not decrypt.
54 //    #include <openssl/obj_mac.h>
55 //    StatusOr<std::unique_ptr<CommutativeElGamal>> elgamal =
56 //        CommutativeElGamal::CreateFromPublicKey(NID_X9_62_prime256v1,
57 //        public_key_bytes);
58 //
59 // Example: To encrypt a message using a std::unique_ptr<ECCommutativeCipher>
60 //    cipher generated as above. Note that the secret must already mapped to the
61 //    curve before encrypting it.
62 //    #include <openssl/obj_mac.h>
63 //    Context context;
64 //    EcPointUtil ec_point_util =
65 //        ECPointUtil::Create(NID_X9_62_prime256v1).value();
66 //    string point =
67 //        ec_point_util->HashToCurve("secret").value();
68 //    StatusOr<stringpair> encrypted_point = elgamal->Encrypt(point);
69 //
70 // Example: To decrypt a message that has been encrypted using the same ElGamal
71 //    key. This does not reverse hashing to the curve.
72 //
73 //    StatusOr<string> decrypted_point =
74 //        cipher->Decrypt(encrypted_point);
75 
76 namespace private_join_and_compute {
77 
78 class CommutativeElGamal {
79  public:
80   // CommutativeElGamal is neither copyable nor assignable.
81   CommutativeElGamal(const CommutativeElGamal&) = delete;
82   CommutativeElGamal& operator=(const CommutativeElGamal&) = delete;
83 
84   ~CommutativeElGamal() = default;
85 
86   // Creates a new CommutativeElGamal object by generating a new public/private
87   // key pair.
88   // Returns INVALID_ARGUMENT status instead if the curve_id is not valid
89   // or INTERNAL status when crypto operations are not successful.
90   static StatusOr<std::unique_ptr<CommutativeElGamal>> CreateWithNewKeyPair(
91       int curve_id);
92 
93   // Creates a new CommutativeElGamal object using the given public key.
94   // The resulting object will not be able to decrypt ciphertexts, since it
95   // doesn't have the private key. However, it can still create encryptions.
96   // Returns INVALID_ARGUMENT status instead if the public_key is not valid for
97   // the given curve or the curve_id is not valid.
98   // Returns INTERNAL status when crypto operations are not successful.
99   static StatusOr<std::unique_ptr<CommutativeElGamal>> CreateFromPublicKey(
100       int curve_id,
101       const std::pair<std::string, std::string>& public_key_bytes);
102 
103   // Creates a new CommutativeElGamal object using the given public and private
104   // keys. The resulting object will be able to both encrypt and decrypt.
105   // Returns INVALID_ARGUMENT status instead if either key is not valid for
106   // the given curve, the keys are inconsistent, or the curve_id is not valid.
107   // Returns INTERNAL status when crypto operations are not successful.
108   static StatusOr<std::unique_ptr<CommutativeElGamal>>
109   CreateFromPublicAndPrivateKeys(
110       int curve_id, const std::pair<std::string, std::string>& public_key_bytes,
111       absl::string_view private_key_bytes);
112 
113   // Encrypts the supplied point, and returns the resulting ElGamal ciphertext.
114   // Returns INVALID_ARGUMENT if the input is not on the same curve.
115   // Returns INTERNAL when crypto operations fail.
116   StatusOr<std::pair<std::string, std::string>> Encrypt(
117       absl::string_view plaintext) const;
118 
119   // Encrypts the identity element of the EC group (typically the point at
120   // infinity).  Note that the ciphertext returned by this method will never
121   // decrypt successfully; however, it can be used in homomorphic operations,
122   // though doing so is equivalent to rerandomizing the ciphertext.
123   StatusOr<std::pair<std::string, std::string>> EncryptIdentityElement() const;
124 
125   // Decrypts the supplied ElGamal ciphertext, and returns the underlying
126   // EC point.
127   // Returns INVALID_ARGUMENT if the input ciphertext is not on the same curve,
128   // or if this object does not have the ElGamal private key.
129   // Returns INTERNAL when crypto operations fail.
130   // A special point to note is that the decryption fails if the message
131   // decrypts to the point at infinity. This is because the point at infinity
132   // does not have a valid serialization in OpenSSL.
133   StatusOr<std::string> Decrypt(
134       const std::pair<std::string, std::string>& ciphertext) const;
135 
136   // Returns a byte representation of the public key.
137   // Return INTERNAL error if converting the public key to bytes fails.
138   StatusOr<std::pair<std::string, std::string>> GetPublicKeyBytes() const;
139 
140   // Returns a byte representation of the private key.
141   // Return INVALID_ARGUMENT if the object doesn't have the private key.
142   StatusOr<std::string> GetPrivateKeyBytes() const;
143 
144  private:
145   CommutativeElGamal(std::unique_ptr<Context> ctx, ECGroup group,
146                      std::unique_ptr<elgamal::PublicKey> elgamal_public_key,
147                      std::unique_ptr<elgamal::PrivateKey> elgamal_private_key);
148 
149   CommutativeElGamal(std::unique_ptr<Context> ctx, ECGroup group,
150                      std::unique_ptr<elgamal::PublicKey> elgamal_public_key);
151 
152   // Context used for storing temporary values to be reused across openssl
153   // function calls for better performance.
154   std::unique_ptr<Context> context_;
155 
156   // The EC Group representing the curve definition.
157   const ECGroup group_;
158 
159   std::unique_ptr<ElGamalEncrypter> encrypter_;
160   std::unique_ptr<ElGamalDecrypter> decrypter_;
161 };
162 
163 }  // namespace private_join_and_compute
164 #endif  // PRIVATE_JOIN_AND_COMPUTE_COMMUTATIVE_ELGAMAL_H_
165