blob: 5ced8e07f4b63b430eccfbde2621b25c6682af0e [file] [log] [blame]
/*
* Copyright 2019 Google LLC.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef EC_COMMUTATIVE_CIPHER_H_
#define EC_COMMUTATIVE_CIPHER_H_
#include <memory>
#include <string>
#include <utility>
#include "third_party/abseil-cpp/absl/strings/string_view.h"
#include "third_party/private-join-and-compute/base/private_join_and_compute_export.h"
#include "third_party/private-join-and-compute/src/crypto/big_num.h"
#include "third_party/private-join-and-compute/src/crypto/context.h"
#include "third_party/private-join-and-compute/src/crypto/ec_group.h"
#include "third_party/private-join-and-compute/src/crypto/ec_point.h"
#include "third_party/private-join-and-compute/src/util/status.inc"
namespace private_join_and_compute {
// ECCommutativeCipher class with the property that K1(K2(a)) = K2(K1(a))
// where K(a) is encryption with the key K. https://eprint.iacr.org/2008/356.pdf
//
// This class allows two parties to determine if they share the same value,
// without revealing the sensitive value to each other.
//
// This class also allows homomorphically re-encrypting an ElGamal ciphertext
// with an EC cipher key K. If the original ciphertext was an encryption of m,
// then the re-encrypted ciphertext is effectively an encryption of K(m). This
// re-encryption does not re-randomize the ciphertext, and so is only secure
// when the underlying messages "m" are pseudorandom.
//
// The encryption is performed over an elliptic curve.
//
// This class is not thread-safe.
//
// Security: The provided bit security is half the number of bits of the
// underlying curve. For example, using curve NID_X9_62_prime256v1 gives 128
// bit security.
//
// Example: To generate a cipher with a new private key for the named curve
// NID_X9_62_prime256v1. The key can be securely stored and reused.
// #include <openssl/obj_mac.h>
// std::unique_ptr<ECCommutativeCipher> cipher =
// ECCommutativeCipher::CreateWithNewKey(
// NID_X9_62_prime256v1, ECCommutativeCipher::HashType::SSWU_RO);
// string key_bytes = cipher->GetPrivateKeyBytes();
//
// Example: To generate a cipher with an existing private key for the named
// curve NID_X9_62_prime256v1.
// #include <openssl/obj_mac.h>
// std::unique_ptr<ECCommutativeCipher> cipher =
// ECCommutativeCipher::CreateFromKey(
// NID_X9_62_prime256v1, key_bytes,
// ECCommutativeCipher::HashType::SSWU_RO);
//
// Example: To encrypt a message using a std::unique_ptr<ECCommutativeCipher>
// cipher generated as above.
// string encrypted_string = cipher->Encrypt("secret");
//
// Example: To re-encrypt a message already encrypted by another party using a
// std::unique_ptr<ECCommutativeCipher> cipher generated as above.
// ::private_join_and_compute::StatusOr<string> double_encrypted_string =
// cipher->ReEncrypt(encrypted_string);
//
// Example: To decrypt a message that has already been encrypted by the same
// party using a std::unique_ptr<ECCommutativeCipher> cipher generated as
// above.
// ::private_join_and_compute::StatusOr<string> decrypted_string =
// cipher->Decrypt(encrypted_string);
//
// Example: To re-encrypt a message that has already been encrypted using a
// std::unique_ptr<CommutativeElGamal> ElGamal key:
// ::private_join_and_compute::StatusOr<std::pair<string, string>>
// double_encrypted_string =
// cipher->ReEncryptElGamalCiphertext(elgamal_ciphertext);
class PRIVATE_COMPUTE_EXPORT ECCommutativeCipher {
public:
// The hash function used by the ECCommutativeCipher in order to hash strings
// to EC curve points.
enum HashType {
SHA256,
SHA384,
SHA512,
SSWU_RO,
};
// Check for valid HashType.
static bool ValidateHashType(HashType hash_type);
// Check for valid HashType.
static bool ValidateHashType(int hash_type);
// ECCommutativeCipher is neither copyable nor assignable.
ECCommutativeCipher(const ECCommutativeCipher&) = delete;
ECCommutativeCipher& operator=(const ECCommutativeCipher&) = delete;
// Creates an ECCommutativeCipher object with a new random private key.
// Use this method when the key is created for the first time or it needs to
// be refreshed.
// Returns INVALID_ARGUMENT status instead if the curve_id is not valid
// or INTERNAL status when crypto operations are not successful.
static ::private_join_and_compute::StatusOr<std::unique_ptr<ECCommutativeCipher>>
CreateWithNewKey(int curve_id, HashType hash_type);
// Creates an ECCommutativeCipher object with the given private key.
// A new key should be created for each session and all values should be
// unique in one session because the encryption is deterministic.
// Use this when the key is stored securely to be used at different steps of
// the protocol in the same session or by multiple processes.
// Returns INVALID_ARGUMENT status instead if the private_key is not valid for
// the given curve or the curve_id is not valid.
// Returns INTERNAL status when crypto operations are not successful.
static ::private_join_and_compute::StatusOr<
std::unique_ptr<ECCommutativeCipher>>
CreateFromKey(int curve_id, absl::string_view key_bytes, HashType hash_type);
// Creates an ECCommutativeCipher object with a new private key generated from
// the given seed and index. This will deterministically generate a key and
// this should not be re-used across multiple sessions. The seed should be a
// cryptographically strong random string of at least 16 bytes.
// Returns INTERNAL status when crypto operations are not successful.
static ::private_join_and_compute::StatusOr<
std::unique_ptr<ECCommutativeCipher>>
CreateWithKeyFromSeed(int curve_id,
absl::string_view seed_bytes,
absl::string_view tag_bytes,
HashType hash_type);
// Encrypts a string with the private key to a point on the elliptic curve.
//
// To encrypt, the string is hashed to a point on the curve which is then
// multiplied with the private key.
//
// The resulting point is returned encoded in compressed form as defined in
// ANSI X9.62 ECDSA.
//
// Returns an INVALID_ARGUMENT error code if an error occurs.
//
// This method is not threadsafe.
::private_join_and_compute::StatusOr<std::string> Encrypt(
absl::string_view plaintext);
// Encrypts an encoded point with the private key.
//
// Returns an INVALID_ARGUMENT error code if the input is not a valid encoding
// of a point on this curve as defined in ANSI X9.62 ECDSA.
//
// The result is a point encoded in compressed form.
//
// This method can also be used to encrypt a value that has already been
// hashed to the curve.
//
// This method is not threadsafe.
::private_join_and_compute::StatusOr<std::string> ReEncrypt(
absl::string_view ciphertext);
// Encrypts an ElGamal ciphertext with the private key.
//
// Returns an INVALID_ARGUMENT error code if the input is not a valid encoding
// of an ElGamal ciphertext on this curve as defined in ANSI X9.62 ECDSA.
//
// The result is another ElGamal ciphertext, encoded in compressed form.
//
// This method is not threadsafe.
::private_join_and_compute::StatusOr<std::pair<std::string, std::string>>
ReEncryptElGamalCiphertext(
const std::pair<std::string, std::string>& elgamal_ciphertext);
// Decrypts an encoded point with the private key.
//
// Returns an INVALID_ARGUMENT error code if the input is not a valid encoding
// of a point on this curve as defined in ANSI X9.62 ECDSA.
//
// The result is a point encoded in compressed form.
//
// If the input point was double-encrypted, once with this key and once with
// another key, then the result point is single-encrypted with the other key.
//
// If the input point was single encrypted with this key, then the result
// point is the original, unencrypted point. Note that this will not reverse
// hashing to the curve.
//
// This method is not threadsafe.
::private_join_and_compute::StatusOr<std::string> Decrypt(
absl::string_view ciphertext);
// Hashes a string to a point on the elliptic curve using the
// "try-and-increment" method.
// See Section 5.2 of https://crypto.stanford.edu/~dabo/papers/bfibe.pdf.
//
// The resulting point is returned encoded in compressed form as defined in
// ANSI X9.62 ECDSA.
//
// Returns an INVALID_ARGUMENT error code if an error occurs.
//
// This method is not threadsafe.
::private_join_and_compute::StatusOr<std::string> HashToTheCurve(
absl::string_view plaintext);
// Returns the private key bytes so the key can be stored and reused.
std::string GetPrivateKeyBytes() const;
private:
// Creates a new ECCommutativeCipher object with the given private key for
// the given EC group.
ECCommutativeCipher(std::unique_ptr<Context> context, ECGroup group,
BigNum private_key, HashType hash_type);
// Encrypts a point by multiplying the point with the private key.
::private_join_and_compute::StatusOr<ECPoint> Encrypt(const ECPoint& point);
// Hashes a string to a point on the elliptic curve.
::private_join_and_compute::StatusOr<ECPoint> HashToTheCurveInternal(
absl::string_view plaintext);
// Context used for storing temporary values to be reused across openssl
// function calls for better performance.
std::unique_ptr<Context> context_;
// The EC Group representing the curve definition.
const ECGroup group_;
// The private key used for encryption.
const BigNum private_key_;
// The private key inverse, used for decryption.
const BigNum private_key_inverse_;
// The hash function used by the cipher.
const HashType hash_type_;
};
} // namespace private_join_and_compute
#endif // EC_COMMUTATIVE_CIPHER_H_