2021-09-29 21:45:35 -07:00
|
|
|
use {
|
|
|
|
crate::encryption::{
|
2021-10-26 23:04:30 -07:00
|
|
|
discrete_log::{DecodeU32Precomputation, DiscreteLog},
|
2021-10-05 06:02:52 -07:00
|
|
|
pedersen::{
|
|
|
|
Pedersen, PedersenBase, PedersenCommitment, PedersenDecryptHandle, PedersenOpening,
|
|
|
|
},
|
2021-09-29 21:45:35 -07:00
|
|
|
},
|
|
|
|
arrayref::{array_ref, array_refs},
|
|
|
|
core::ops::{Add, Div, Mul, Sub},
|
|
|
|
curve25519_dalek::{
|
|
|
|
ristretto::{CompressedRistretto, RistrettoPoint},
|
|
|
|
scalar::Scalar,
|
|
|
|
},
|
|
|
|
serde::{Deserialize, Serialize},
|
2021-10-21 17:48:25 -07:00
|
|
|
solana_sdk::{
|
|
|
|
instruction::Instruction,
|
|
|
|
message::Message,
|
|
|
|
pubkey::Pubkey,
|
|
|
|
signature::Signature,
|
|
|
|
signer::{Signer, SignerError},
|
|
|
|
},
|
2021-10-26 23:04:30 -07:00
|
|
|
std::convert::TryInto,
|
2021-09-29 21:45:35 -07:00
|
|
|
subtle::{Choice, ConstantTimeEq},
|
|
|
|
zeroize::Zeroize,
|
|
|
|
};
|
2021-10-11 10:43:43 -07:00
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
|
|
|
use {
|
|
|
|
rand::{rngs::OsRng, CryptoRng, RngCore},
|
2021-10-12 07:21:07 -07:00
|
|
|
sha3::Sha3_512,
|
2021-10-11 10:43:43 -07:00
|
|
|
std::{
|
|
|
|
fmt,
|
|
|
|
fs::{self, File, OpenOptions},
|
|
|
|
io::{Read, Write},
|
|
|
|
path::Path,
|
|
|
|
},
|
|
|
|
};
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
struct ElGamal;
|
|
|
|
impl ElGamal {
|
2021-10-29 10:47:49 -07:00
|
|
|
/// The function generates the public and secret keys for ElGamal encryption from the provided
|
|
|
|
/// randomness generator
|
2021-09-29 21:45:35 -07:00
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
|
|
|
#[allow(non_snake_case)]
|
2021-10-12 07:48:15 -07:00
|
|
|
fn keygen<T: RngCore + CryptoRng>(rng: &mut T) -> ElGamalKeypair {
|
2021-09-29 21:45:35 -07:00
|
|
|
// sample a non-zero scalar
|
|
|
|
let mut s: Scalar;
|
|
|
|
loop {
|
|
|
|
s = Scalar::random(rng);
|
|
|
|
|
|
|
|
if s != Scalar::zero() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-29 10:47:49 -07:00
|
|
|
Self::keygen_with_scalar(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generates the public and secret keys for ElGamal encryption from a non-zero Scalar
|
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
fn keygen_with_scalar(s: Scalar) -> ElGamalKeypair {
|
|
|
|
assert!(s != Scalar::zero());
|
|
|
|
|
2021-09-29 21:45:35 -07:00
|
|
|
let H = PedersenBase::default().H;
|
|
|
|
let P = s.invert() * H;
|
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
ElGamalKeypair {
|
2021-10-11 11:32:39 -07:00
|
|
|
public: ElGamalPubkey(P),
|
|
|
|
secret: ElGamalSecretKey(s),
|
2021-10-07 12:44:17 -07:00
|
|
|
}
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// On input a public key and a message to be encrypted, the function
|
|
|
|
/// returns an ElGamal ciphertext of the message under the public key.
|
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
2021-10-12 07:48:15 -07:00
|
|
|
fn encrypt<T: Into<Scalar>>(public: &ElGamalPubkey, amount: T) -> ElGamalCiphertext {
|
2021-10-05 06:02:52 -07:00
|
|
|
let (message_comm, open) = Pedersen::new(amount);
|
2021-10-11 11:32:39 -07:00
|
|
|
let decrypt_handle = public.decrypt_handle(&open);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
message_comm,
|
|
|
|
decrypt_handle,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// On input a public key, message, and Pedersen opening, the function
|
|
|
|
/// returns an ElGamal ciphertext of the message under the public key using
|
|
|
|
/// the opening.
|
2021-10-12 07:48:15 -07:00
|
|
|
fn encrypt_with<T: Into<Scalar>>(
|
2021-10-11 11:32:39 -07:00
|
|
|
public: &ElGamalPubkey,
|
2021-09-29 21:45:35 -07:00
|
|
|
amount: T,
|
2021-10-05 06:02:52 -07:00
|
|
|
open: &PedersenOpening,
|
2021-09-30 11:11:53 -07:00
|
|
|
) -> ElGamalCiphertext {
|
2021-10-05 06:02:52 -07:00
|
|
|
let message_comm = Pedersen::with(amount, open);
|
2021-10-11 11:32:39 -07:00
|
|
|
let decrypt_handle = public.decrypt_handle(open);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
message_comm,
|
|
|
|
decrypt_handle,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// On input a secret key and a ciphertext, the function decrypts the ciphertext.
|
|
|
|
///
|
2021-10-01 09:43:59 -07:00
|
|
|
/// The output of the function is of type `DiscreteLog`. The exact message
|
|
|
|
/// can be recovered via the DiscreteLog's decode method.
|
2021-10-12 07:48:15 -07:00
|
|
|
fn decrypt(secret: &ElGamalSecretKey, ct: &ElGamalCiphertext) -> DiscreteLog {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalSecretKey(s) = secret;
|
2021-09-30 11:11:53 -07:00
|
|
|
let ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
message_comm,
|
|
|
|
decrypt_handle,
|
|
|
|
} = ct;
|
|
|
|
|
2021-10-01 09:43:59 -07:00
|
|
|
DiscreteLog {
|
2021-09-29 21:45:35 -07:00
|
|
|
generator: PedersenBase::default().G,
|
|
|
|
target: message_comm.get_point() - s * decrypt_handle.get_point(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// On input a secret key and a ciphertext, the function decrypts the
|
|
|
|
/// ciphertext for a u32 value.
|
2021-10-12 07:48:15 -07:00
|
|
|
fn decrypt_u32(secret: &ElGamalSecretKey, ct: &ElGamalCiphertext) -> Option<u32> {
|
2021-10-11 11:32:39 -07:00
|
|
|
let discrete_log_instance = Self::decrypt(secret, ct);
|
2021-09-29 21:45:35 -07:00
|
|
|
discrete_log_instance.decode_u32()
|
|
|
|
}
|
2021-09-30 08:54:14 -07:00
|
|
|
|
|
|
|
/// On input a secret key, ciphertext, and hashmap, the function decrypts the
|
|
|
|
/// ciphertext for a u32 value.
|
2021-10-12 07:48:15 -07:00
|
|
|
fn decrypt_u32_online(
|
2021-10-11 11:32:39 -07:00
|
|
|
secret: &ElGamalSecretKey,
|
2021-09-30 08:54:14 -07:00
|
|
|
ct: &ElGamalCiphertext,
|
2021-10-26 23:04:30 -07:00
|
|
|
hashmap: &DecodeU32Precomputation,
|
2021-09-30 08:54:14 -07:00
|
|
|
) -> Option<u32> {
|
2021-10-11 11:32:39 -07:00
|
|
|
let discrete_log_instance = Self::decrypt(secret, ct);
|
2021-09-30 08:54:14 -07:00
|
|
|
discrete_log_instance.decode_u32_online(hashmap)
|
|
|
|
}
|
2021-10-12 07:48:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A (twisted) ElGamal encryption keypair.
|
2021-10-25 20:53:01 -07:00
|
|
|
#[derive(PartialEq)]
|
2021-10-12 07:48:15 -07:00
|
|
|
pub struct ElGamalKeypair {
|
|
|
|
/// The public half of this keypair.
|
|
|
|
pub public: ElGamalPubkey,
|
|
|
|
/// The secret half of this keypair.
|
|
|
|
pub secret: ElGamalSecretKey,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ElGamalKeypair {
|
2021-10-13 06:45:16 -07:00
|
|
|
/// Generates the public and secret keys for ElGamal encryption from Ed25519 signing key and an
|
|
|
|
/// address.
|
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
|
|
|
#[allow(non_snake_case)]
|
2021-10-21 17:48:25 -07:00
|
|
|
pub fn new(signer: &dyn Signer, address: &Pubkey) -> Result<Self, SignerError> {
|
2021-10-29 10:47:49 -07:00
|
|
|
let message = Message::new(
|
|
|
|
&[Instruction::new_with_bytes(
|
|
|
|
*address,
|
|
|
|
b"ElGamalSecretKey",
|
|
|
|
vec![],
|
|
|
|
)],
|
|
|
|
Some(&signer.try_pubkey()?),
|
|
|
|
);
|
|
|
|
let signature = signer.try_sign_message(&message.serialize())?;
|
|
|
|
|
|
|
|
// Some `Signer` implementations return the default signature, which is not suitable for
|
|
|
|
// use as key material
|
|
|
|
if signature == Signature::default() {
|
|
|
|
return Err(SignerError::Custom("Rejecting default signature".into()));
|
|
|
|
}
|
2021-10-13 06:45:16 -07:00
|
|
|
|
2021-10-29 10:47:49 -07:00
|
|
|
let scalar = Scalar::hash_from_bytes::<Sha3_512>(signature.as_ref());
|
|
|
|
Ok(ElGamal::keygen_with_scalar(scalar))
|
2021-10-13 06:45:16 -07:00
|
|
|
}
|
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
/// Generates the public and secret keys for ElGamal encryption.
|
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
|
|
|
#[allow(clippy::new_ret_no_self)]
|
|
|
|
pub fn default() -> Self {
|
|
|
|
Self::with(&mut OsRng) // using OsRng for now
|
|
|
|
}
|
|
|
|
|
|
|
|
/// On input a randomness generator, the function generates the public and
|
|
|
|
/// secret keys for ElGamal encryption.
|
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
pub fn with<T: RngCore + CryptoRng>(rng: &mut T) -> Self {
|
|
|
|
ElGamal::keygen(rng)
|
|
|
|
}
|
2021-10-11 10:43:43 -07:00
|
|
|
|
|
|
|
pub fn to_bytes(&self) -> [u8; 64] {
|
2021-10-11 11:32:39 -07:00
|
|
|
let mut bytes = self.public.to_bytes().to_vec();
|
|
|
|
bytes.extend(self.secret.to_bytes());
|
2021-10-11 10:43:43 -07:00
|
|
|
bytes.try_into().expect("incorrect length")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
|
|
|
Some(Self {
|
2021-10-11 11:32:39 -07:00
|
|
|
public: ElGamalPubkey::from_bytes(bytes[..32].try_into().ok()?)?,
|
|
|
|
secret: ElGamalSecretKey::from_bytes(bytes[32..].try_into().ok()?)?,
|
2021-10-11 10:43:43 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads a JSON-encoded keypair from a `Reader` implementor
|
|
|
|
pub fn read_json<R: Read>(reader: &mut R) -> Result<Self, Box<dyn std::error::Error>> {
|
|
|
|
let bytes: Vec<u8> = serde_json::from_reader(reader)?;
|
|
|
|
Self::from_bytes(&bytes).ok_or_else(|| {
|
2021-10-11 11:25:16 -07:00
|
|
|
std::io::Error::new(std::io::ErrorKind::Other, "Invalid ElGamalKeypair").into()
|
2021-10-11 10:43:43 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads keypair from a file
|
|
|
|
pub fn read_json_file<F: AsRef<Path>>(path: F) -> Result<Self, Box<dyn std::error::Error>> {
|
|
|
|
let mut file = File::open(path.as_ref())?;
|
|
|
|
Self::read_json(&mut file)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Writes to a `Write` implementer with JSON-encoding
|
|
|
|
pub fn write_json<W: Write>(
|
|
|
|
&self,
|
|
|
|
writer: &mut W,
|
|
|
|
) -> Result<String, Box<dyn std::error::Error>> {
|
|
|
|
let bytes = self.to_bytes();
|
|
|
|
let json = serde_json::to_string(&bytes.to_vec())?;
|
|
|
|
writer.write_all(&json.clone().into_bytes())?;
|
|
|
|
Ok(json)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Write keypair to a file with JSON-encoding
|
|
|
|
pub fn write_json_file<F: AsRef<Path>>(
|
|
|
|
&self,
|
|
|
|
outfile: F,
|
|
|
|
) -> Result<String, Box<dyn std::error::Error>> {
|
|
|
|
let outfile = outfile.as_ref();
|
|
|
|
|
|
|
|
if let Some(outdir) = outfile.parent() {
|
|
|
|
fs::create_dir_all(outdir)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut f = {
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
{
|
|
|
|
OpenOptions::new()
|
|
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
|
|
{
|
|
|
|
use std::os::unix::fs::OpenOptionsExt;
|
|
|
|
OpenOptions::new().mode(0o600)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.write(true)
|
|
|
|
.truncate(true)
|
|
|
|
.create(true)
|
|
|
|
.open(outfile)?;
|
|
|
|
|
|
|
|
self.write_json(&mut f)
|
|
|
|
}
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Public key for the ElGamal encryption scheme.
|
|
|
|
#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, Eq, PartialEq)]
|
2021-09-30 11:11:53 -07:00
|
|
|
pub struct ElGamalPubkey(RistrettoPoint);
|
|
|
|
impl ElGamalPubkey {
|
2021-10-12 07:21:07 -07:00
|
|
|
/// Derive the `ElGamalPubkey` that uniquely corresponds to an `ElGamalSecretKey`
|
|
|
|
#[allow(non_snake_case)]
|
2021-10-12 07:36:17 -07:00
|
|
|
pub fn new(secret: &ElGamalSecretKey) -> Self {
|
2021-10-12 07:21:07 -07:00
|
|
|
let H = PedersenBase::default().H;
|
2021-10-12 07:36:17 -07:00
|
|
|
ElGamalPubkey(secret.0 * H)
|
2021-10-12 07:21:07 -07:00
|
|
|
}
|
|
|
|
|
2021-09-29 21:45:35 -07:00
|
|
|
pub fn get_point(&self) -> RistrettoPoint {
|
|
|
|
self.0
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::wrong_self_convention)]
|
|
|
|
pub fn to_bytes(&self) -> [u8; 32] {
|
|
|
|
self.0.compress().to_bytes()
|
|
|
|
}
|
|
|
|
|
2021-10-11 10:43:43 -07:00
|
|
|
pub fn from_bytes(bytes: &[u8; 32]) -> Option<ElGamalPubkey> {
|
2021-09-30 11:11:53 -07:00
|
|
|
Some(ElGamalPubkey(
|
2021-09-29 21:45:35 -07:00
|
|
|
CompressedRistretto::from_slice(bytes).decompress()?,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Utility method for code ergonomics.
|
|
|
|
#[cfg(not(target_arch = "bpf"))]
|
2021-09-30 11:11:53 -07:00
|
|
|
pub fn encrypt<T: Into<Scalar>>(&self, msg: T) -> ElGamalCiphertext {
|
2021-10-12 07:48:15 -07:00
|
|
|
ElGamal::encrypt(self, msg)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Utility method for code ergonomics.
|
2021-10-05 06:02:52 -07:00
|
|
|
pub fn encrypt_with<T: Into<Scalar>>(
|
|
|
|
&self,
|
|
|
|
msg: T,
|
|
|
|
open: &PedersenOpening,
|
|
|
|
) -> ElGamalCiphertext {
|
2021-10-12 07:48:15 -07:00
|
|
|
ElGamal::encrypt_with(self, msg, open)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Generate a decryption token from an ElGamal public key and a Pedersen
|
|
|
|
/// opening.
|
2021-10-05 06:20:35 -07:00
|
|
|
pub fn decrypt_handle(self, open: &PedersenOpening) -> PedersenDecryptHandle {
|
2021-10-05 06:14:04 -07:00
|
|
|
PedersenDecryptHandle::new(&self, open)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
impl From<RistrettoPoint> for ElGamalPubkey {
|
|
|
|
fn from(point: RistrettoPoint) -> ElGamalPubkey {
|
|
|
|
ElGamalPubkey(point)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-11 10:43:43 -07:00
|
|
|
impl fmt::Display for ElGamalPubkey {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{}", base64::encode(self.to_bytes()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-29 21:45:35 -07:00
|
|
|
/// Secret key for the ElGamal encryption scheme.
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Zeroize)]
|
|
|
|
#[zeroize(drop)]
|
2021-10-01 09:48:45 -07:00
|
|
|
pub struct ElGamalSecretKey(Scalar);
|
|
|
|
impl ElGamalSecretKey {
|
2021-10-21 17:48:25 -07:00
|
|
|
pub fn new(signer: &dyn Signer, address: &Pubkey) -> Result<Self, SignerError> {
|
|
|
|
let message = Message::new(
|
|
|
|
&[Instruction::new_with_bytes(
|
|
|
|
*address,
|
|
|
|
b"ElGamalSecretKey",
|
|
|
|
vec![],
|
|
|
|
)],
|
|
|
|
Some(&signer.try_pubkey()?),
|
|
|
|
);
|
|
|
|
let signature = signer.try_sign_message(&message.serialize())?;
|
|
|
|
|
|
|
|
// Some `Signer` implementations return the default signature, which is not suitable for
|
|
|
|
// use as key material
|
|
|
|
if signature == Signature::default() {
|
|
|
|
Err(SignerError::Custom("Rejecting default signature".into()))
|
|
|
|
} else {
|
|
|
|
Ok(ElGamalSecretKey(Scalar::hash_from_bytes::<Sha3_512>(
|
2021-10-25 20:58:45 -07:00
|
|
|
signature.as_ref(),
|
2021-10-21 17:48:25 -07:00
|
|
|
)))
|
|
|
|
}
|
2021-10-12 07:21:07 -07:00
|
|
|
}
|
|
|
|
|
2021-09-29 21:45:35 -07:00
|
|
|
pub fn get_scalar(&self) -> Scalar {
|
|
|
|
self.0
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Utility method for code ergonomics.
|
2021-10-01 09:43:59 -07:00
|
|
|
pub fn decrypt(&self, ct: &ElGamalCiphertext) -> DiscreteLog {
|
2021-10-12 07:48:15 -07:00
|
|
|
ElGamal::decrypt(self, ct)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Utility method for code ergonomics.
|
2021-09-30 11:11:53 -07:00
|
|
|
pub fn decrypt_u32(&self, ct: &ElGamalCiphertext) -> Option<u32> {
|
2021-10-12 07:48:15 -07:00
|
|
|
ElGamal::decrypt_u32(self, ct)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
2021-09-30 08:54:14 -07:00
|
|
|
/// Utility method for code ergonomics.
|
|
|
|
pub fn decrypt_u32_online(
|
|
|
|
&self,
|
|
|
|
ct: &ElGamalCiphertext,
|
2021-10-26 23:04:30 -07:00
|
|
|
hashmap: &DecodeU32Precomputation,
|
2021-09-30 08:54:14 -07:00
|
|
|
) -> Option<u32> {
|
2021-10-12 07:48:15 -07:00
|
|
|
ElGamal::decrypt_u32_online(self, ct, hashmap)
|
2021-09-30 08:54:14 -07:00
|
|
|
}
|
|
|
|
|
2021-09-29 21:45:35 -07:00
|
|
|
pub fn to_bytes(&self) -> [u8; 32] {
|
|
|
|
self.0.to_bytes()
|
|
|
|
}
|
|
|
|
|
2021-10-11 10:43:43 -07:00
|
|
|
pub fn from_bytes(bytes: [u8; 32]) -> Option<ElGamalSecretKey> {
|
|
|
|
Scalar::from_canonical_bytes(bytes).map(ElGamalSecretKey)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-01 09:48:45 -07:00
|
|
|
impl From<Scalar> for ElGamalSecretKey {
|
|
|
|
fn from(scalar: Scalar) -> ElGamalSecretKey {
|
|
|
|
ElGamalSecretKey(scalar)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-01 09:48:45 -07:00
|
|
|
impl Eq for ElGamalSecretKey {}
|
|
|
|
impl PartialEq for ElGamalSecretKey {
|
2021-09-29 21:45:35 -07:00
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.ct_eq(other).unwrap_u8() == 1u8
|
|
|
|
}
|
|
|
|
}
|
2021-10-01 09:48:45 -07:00
|
|
|
impl ConstantTimeEq for ElGamalSecretKey {
|
2021-09-29 21:45:35 -07:00
|
|
|
fn ct_eq(&self, other: &Self) -> Choice {
|
|
|
|
self.0.ct_eq(&other.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Ciphertext for the ElGamal encryption scheme.
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, Eq, PartialEq)]
|
2021-09-30 11:11:53 -07:00
|
|
|
pub struct ElGamalCiphertext {
|
2021-10-05 06:02:52 -07:00
|
|
|
pub message_comm: PedersenCommitment,
|
|
|
|
pub decrypt_handle: PedersenDecryptHandle,
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
2021-09-30 11:11:53 -07:00
|
|
|
impl ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
pub fn add_to_msg<T: Into<Scalar>>(&self, message: T) -> Self {
|
2021-10-05 06:02:52 -07:00
|
|
|
let diff_comm = Pedersen::with(message, &PedersenOpening::default());
|
2021-09-30 11:11:53 -07:00
|
|
|
ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
message_comm: self.message_comm + diff_comm,
|
|
|
|
decrypt_handle: self.decrypt_handle,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sub_to_msg<T: Into<Scalar>>(&self, message: T) -> Self {
|
2021-10-05 06:02:52 -07:00
|
|
|
let diff_comm = Pedersen::with(message, &PedersenOpening::default());
|
2021-09-30 11:11:53 -07:00
|
|
|
ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
message_comm: self.message_comm - diff_comm,
|
|
|
|
decrypt_handle: self.decrypt_handle,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::wrong_self_convention)]
|
|
|
|
pub fn to_bytes(&self) -> [u8; 64] {
|
|
|
|
let mut bytes = [0u8; 64];
|
|
|
|
|
|
|
|
bytes[..32].copy_from_slice(self.message_comm.get_point().compress().as_bytes());
|
|
|
|
bytes[32..].copy_from_slice(self.decrypt_handle.get_point().compress().as_bytes());
|
|
|
|
bytes
|
|
|
|
}
|
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
pub fn from_bytes(bytes: &[u8]) -> Option<ElGamalCiphertext> {
|
2021-09-29 21:45:35 -07:00
|
|
|
let bytes = array_ref![bytes, 0, 64];
|
|
|
|
let (message_comm, decrypt_handle) = array_refs![bytes, 32, 32];
|
|
|
|
|
|
|
|
let message_comm = CompressedRistretto::from_slice(message_comm).decompress()?;
|
|
|
|
let decrypt_handle = CompressedRistretto::from_slice(decrypt_handle).decompress()?;
|
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
Some(ElGamalCiphertext {
|
2021-10-05 06:02:52 -07:00
|
|
|
message_comm: PedersenCommitment(message_comm),
|
|
|
|
decrypt_handle: PedersenDecryptHandle(decrypt_handle),
|
2021-09-29 21:45:35 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Utility method for code ergonomics.
|
2021-10-11 11:32:39 -07:00
|
|
|
pub fn decrypt(&self, secret: &ElGamalSecretKey) -> DiscreteLog {
|
2021-10-12 07:48:15 -07:00
|
|
|
ElGamal::decrypt(secret, self)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Utility method for code ergonomics.
|
2021-10-11 11:32:39 -07:00
|
|
|
pub fn decrypt_u32(&self, secret: &ElGamalSecretKey) -> Option<u32> {
|
2021-10-12 07:48:15 -07:00
|
|
|
ElGamal::decrypt_u32(secret, self)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
2021-09-30 08:54:14 -07:00
|
|
|
|
|
|
|
/// Utility method for code ergonomics.
|
|
|
|
pub fn decrypt_u32_online(
|
|
|
|
&self,
|
2021-10-11 11:32:39 -07:00
|
|
|
secret: &ElGamalSecretKey,
|
2021-10-26 23:04:30 -07:00
|
|
|
hashmap: &DecodeU32Precomputation,
|
2021-09-30 08:54:14 -07:00
|
|
|
) -> Option<u32> {
|
2021-10-12 07:48:15 -07:00
|
|
|
ElGamal::decrypt_u32_online(secret, self, hashmap)
|
2021-09-30 08:54:14 -07:00
|
|
|
}
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
2021-10-05 06:14:04 -07:00
|
|
|
impl From<(PedersenCommitment, PedersenDecryptHandle)> for ElGamalCiphertext {
|
|
|
|
fn from((comm, handle): (PedersenCommitment, PedersenDecryptHandle)) -> Self {
|
|
|
|
ElGamalCiphertext {
|
|
|
|
message_comm: comm,
|
|
|
|
decrypt_handle: handle,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
impl<'a, 'b> Add<&'b ElGamalCiphertext> for &'a ElGamalCiphertext {
|
|
|
|
type Output = ElGamalCiphertext;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
fn add(self, other: &'b ElGamalCiphertext) -> ElGamalCiphertext {
|
|
|
|
ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
message_comm: self.message_comm + other.message_comm,
|
|
|
|
decrypt_handle: self.decrypt_handle + other.decrypt_handle,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
define_add_variants!(
|
|
|
|
LHS = ElGamalCiphertext,
|
|
|
|
RHS = ElGamalCiphertext,
|
|
|
|
Output = ElGamalCiphertext
|
|
|
|
);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
impl<'a, 'b> Sub<&'b ElGamalCiphertext> for &'a ElGamalCiphertext {
|
|
|
|
type Output = ElGamalCiphertext;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
fn sub(self, other: &'b ElGamalCiphertext) -> ElGamalCiphertext {
|
|
|
|
ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
message_comm: self.message_comm - other.message_comm,
|
|
|
|
decrypt_handle: self.decrypt_handle - other.decrypt_handle,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
define_sub_variants!(
|
|
|
|
LHS = ElGamalCiphertext,
|
|
|
|
RHS = ElGamalCiphertext,
|
|
|
|
Output = ElGamalCiphertext
|
|
|
|
);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
impl<'a, 'b> Mul<&'b Scalar> for &'a ElGamalCiphertext {
|
|
|
|
type Output = ElGamalCiphertext;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
fn mul(self, other: &'b Scalar) -> ElGamalCiphertext {
|
|
|
|
ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
message_comm: self.message_comm * other,
|
|
|
|
decrypt_handle: self.decrypt_handle * other,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
define_mul_variants!(
|
|
|
|
LHS = ElGamalCiphertext,
|
|
|
|
RHS = Scalar,
|
|
|
|
Output = ElGamalCiphertext
|
|
|
|
);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
impl<'a, 'b> Div<&'b Scalar> for &'a ElGamalCiphertext {
|
|
|
|
type Output = ElGamalCiphertext;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
fn div(self, other: &'b Scalar) -> ElGamalCiphertext {
|
|
|
|
ElGamalCiphertext {
|
2021-09-29 21:45:35 -07:00
|
|
|
message_comm: self.message_comm * other.invert(),
|
|
|
|
decrypt_handle: self.decrypt_handle * other.invert(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-30 11:11:53 -07:00
|
|
|
define_div_variants!(
|
|
|
|
LHS = ElGamalCiphertext,
|
|
|
|
RHS = Scalar,
|
|
|
|
Output = ElGamalCiphertext
|
|
|
|
);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-10-21 17:48:25 -07:00
|
|
|
use {
|
|
|
|
super::*,
|
|
|
|
crate::encryption::pedersen::Pedersen,
|
|
|
|
solana_sdk::{signature::Keypair, signer::null_signer::NullSigner},
|
|
|
|
};
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_encrypt_decrypt_correctness() {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalKeypair { public, secret } = ElGamalKeypair::default();
|
2021-09-29 21:45:35 -07:00
|
|
|
let msg: u32 = 57;
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct = ElGamal::encrypt(&public, msg);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-01 09:43:59 -07:00
|
|
|
let expected_instance = DiscreteLog {
|
2021-09-29 21:45:35 -07:00
|
|
|
generator: PedersenBase::default().G,
|
|
|
|
target: Scalar::from(msg) * PedersenBase::default().G,
|
|
|
|
};
|
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
assert_eq!(expected_instance, ElGamal::decrypt(&secret, &ct));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
// Commenting out for faster testing
|
2021-10-11 11:32:39 -07:00
|
|
|
// assert_eq!(msg, ElGamalKeypair::decrypt_u32(&secret, &ct).unwrap());
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_decrypt_handle() {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalKeypair {
|
|
|
|
public: pk_1,
|
|
|
|
secret: sk_1,
|
|
|
|
} = ElGamalKeypair::default();
|
|
|
|
let ElGamalKeypair {
|
|
|
|
public: pk_2,
|
|
|
|
secret: sk_2,
|
|
|
|
} = ElGamalKeypair::default();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
let msg: u32 = 77;
|
2021-10-05 06:02:52 -07:00
|
|
|
let (comm, open) = Pedersen::new(msg);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-05 06:20:35 -07:00
|
|
|
let decrypt_handle_1 = pk_1.decrypt_handle(&open);
|
|
|
|
let decrypt_handle_2 = pk_2.decrypt_handle(&open);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-05 06:14:04 -07:00
|
|
|
let ct_1: ElGamalCiphertext = (comm, decrypt_handle_1).into();
|
|
|
|
let ct_2: ElGamalCiphertext = (comm, decrypt_handle_2).into();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-01 09:43:59 -07:00
|
|
|
let expected_instance = DiscreteLog {
|
2021-09-29 21:45:35 -07:00
|
|
|
generator: PedersenBase::default().G,
|
|
|
|
target: Scalar::from(msg) * PedersenBase::default().G,
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(expected_instance, sk_1.decrypt(&ct_1));
|
|
|
|
assert_eq!(expected_instance, sk_2.decrypt(&ct_2));
|
|
|
|
|
|
|
|
// Commenting out for faster testing
|
|
|
|
// assert_eq!(msg, sk_1.decrypt_u32(&ct_1).unwrap());
|
|
|
|
// assert_eq!(msg, sk_2.decrypt_u32(&ct_2).unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_homomorphic_addition() {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalKeypair { public, secret: _ } = ElGamalKeypair::default();
|
2021-09-29 21:45:35 -07:00
|
|
|
let msg_0: u64 = 57;
|
|
|
|
let msg_1: u64 = 77;
|
|
|
|
|
|
|
|
// Add two ElGamal ciphertexts
|
2021-10-05 06:02:52 -07:00
|
|
|
let open_0 = PedersenOpening::random(&mut OsRng);
|
|
|
|
let open_1 = PedersenOpening::random(&mut OsRng);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct_0 = ElGamal::encrypt_with(&public, msg_0, &open_0);
|
|
|
|
let ct_1 = ElGamal::encrypt_with(&public, msg_1, &open_1);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct_sum = ElGamal::encrypt_with(&public, msg_0 + msg_1, &(open_0 + open_1));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(ct_sum, ct_0 + ct_1);
|
|
|
|
|
|
|
|
// Add to ElGamal ciphertext
|
2021-10-05 06:02:52 -07:00
|
|
|
let open = PedersenOpening::random(&mut OsRng);
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct = ElGamal::encrypt_with(&public, msg_0, &open);
|
|
|
|
let ct_sum = ElGamal::encrypt_with(&public, msg_0 + msg_1, &open);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(ct_sum, ct.add_to_msg(msg_1));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_homomorphic_subtraction() {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalKeypair { public, secret: _ } = ElGamalKeypair::default();
|
2021-09-29 21:45:35 -07:00
|
|
|
let msg_0: u64 = 77;
|
|
|
|
let msg_1: u64 = 55;
|
|
|
|
|
|
|
|
// Subtract two ElGamal ciphertexts
|
2021-10-05 06:02:52 -07:00
|
|
|
let open_0 = PedersenOpening::random(&mut OsRng);
|
|
|
|
let open_1 = PedersenOpening::random(&mut OsRng);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct_0 = ElGamal::encrypt_with(&public, msg_0, &open_0);
|
|
|
|
let ct_1 = ElGamal::encrypt_with(&public, msg_1, &open_1);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct_sub = ElGamal::encrypt_with(&public, msg_0 - msg_1, &(open_0 - open_1));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(ct_sub, ct_0 - ct_1);
|
|
|
|
|
|
|
|
// Subtract to ElGamal ciphertext
|
2021-10-05 06:02:52 -07:00
|
|
|
let open = PedersenOpening::random(&mut OsRng);
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct = ElGamal::encrypt_with(&public, msg_0, &open);
|
|
|
|
let ct_sub = ElGamal::encrypt_with(&public, msg_0 - msg_1, &open);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(ct_sub, ct.sub_to_msg(msg_1));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_homomorphic_multiplication() {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalKeypair { public, secret: _ } = ElGamalKeypair::default();
|
2021-09-29 21:45:35 -07:00
|
|
|
let msg_0: u64 = 57;
|
|
|
|
let msg_1: u64 = 77;
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
let open = PedersenOpening::random(&mut OsRng);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct = ElGamal::encrypt_with(&public, msg_0, &open);
|
2021-09-29 21:45:35 -07:00
|
|
|
let scalar = Scalar::from(msg_1);
|
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct_prod = ElGamal::encrypt_with(&public, msg_0 * msg_1, &(open * scalar));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(ct_prod, ct * scalar);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_homomorphic_division() {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalKeypair { public, secret: _ } = ElGamalKeypair::default();
|
2021-09-29 21:45:35 -07:00
|
|
|
let msg_0: u64 = 55;
|
|
|
|
let msg_1: u64 = 5;
|
|
|
|
|
2021-10-05 06:02:52 -07:00
|
|
|
let open = PedersenOpening::random(&mut OsRng);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct = ElGamal::encrypt_with(&public, msg_0, &open);
|
2021-09-29 21:45:35 -07:00
|
|
|
let scalar = Scalar::from(msg_1);
|
|
|
|
|
2021-10-12 07:48:15 -07:00
|
|
|
let ct_div = ElGamal::encrypt_with(&public, msg_0 / msg_1, &(open / scalar));
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(ct_div, ct / scalar);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_serde_ciphertext() {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalKeypair { public, secret: _ } = ElGamalKeypair::default();
|
2021-09-29 21:45:35 -07:00
|
|
|
let msg: u64 = 77;
|
2021-10-11 11:32:39 -07:00
|
|
|
let ct = public.encrypt(msg);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
let encoded = bincode::serialize(&ct).unwrap();
|
2021-09-30 11:11:53 -07:00
|
|
|
let decoded: ElGamalCiphertext = bincode::deserialize(&encoded).unwrap();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
assert_eq!(ct, decoded);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_serde_pubkey() {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalKeypair { public, secret: _ } = ElGamalKeypair::default();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-11 11:32:39 -07:00
|
|
|
let encoded = bincode::serialize(&public).unwrap();
|
2021-09-30 11:11:53 -07:00
|
|
|
let decoded: ElGamalPubkey = bincode::deserialize(&encoded).unwrap();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-11 11:32:39 -07:00
|
|
|
assert_eq!(public, decoded);
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_serde_secretkey() {
|
2021-10-11 11:32:39 -07:00
|
|
|
let ElGamalKeypair { public: _, secret } = ElGamalKeypair::default();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-11 11:32:39 -07:00
|
|
|
let encoded = bincode::serialize(&secret).unwrap();
|
2021-10-01 09:48:45 -07:00
|
|
|
let decoded: ElGamalSecretKey = bincode::deserialize(&encoded).unwrap();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-10-11 11:32:39 -07:00
|
|
|
assert_eq!(secret, decoded);
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
2021-10-11 10:43:43 -07:00
|
|
|
|
|
|
|
fn tmp_file_path(name: &str) -> String {
|
|
|
|
use std::env;
|
|
|
|
let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
|
2021-10-11 11:25:16 -07:00
|
|
|
let keypair = ElGamalKeypair::default();
|
2021-10-11 11:32:39 -07:00
|
|
|
format!("{}/tmp/{}-{}", out_dir, name, keypair.public)
|
2021-10-11 10:43:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_write_keypair_file() {
|
|
|
|
let outfile = tmp_file_path("test_write_keypair_file.json");
|
2021-10-11 11:25:16 -07:00
|
|
|
let serialized_keypair = ElGamalKeypair::default().write_json_file(&outfile).unwrap();
|
2021-10-11 10:43:43 -07:00
|
|
|
let keypair_vec: Vec<u8> = serde_json::from_str(&serialized_keypair).unwrap();
|
|
|
|
assert!(Path::new(&outfile).exists());
|
|
|
|
assert_eq!(
|
|
|
|
keypair_vec,
|
2021-10-11 11:25:16 -07:00
|
|
|
ElGamalKeypair::read_json_file(&outfile)
|
2021-10-11 10:43:43 -07:00
|
|
|
.unwrap()
|
|
|
|
.to_bytes()
|
|
|
|
.to_vec()
|
|
|
|
);
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
{
|
|
|
|
use std::os::unix::fs::PermissionsExt;
|
|
|
|
assert_eq!(
|
|
|
|
File::open(&outfile)
|
|
|
|
.expect("open")
|
|
|
|
.metadata()
|
|
|
|
.expect("metadata")
|
|
|
|
.permissions()
|
|
|
|
.mode()
|
|
|
|
& 0o777,
|
|
|
|
0o600
|
|
|
|
);
|
|
|
|
}
|
|
|
|
fs::remove_file(&outfile).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_write_keypair_file_overwrite_ok() {
|
|
|
|
let outfile = tmp_file_path("test_write_keypair_file_overwrite_ok.json");
|
|
|
|
|
2021-10-11 11:25:16 -07:00
|
|
|
ElGamalKeypair::default().write_json_file(&outfile).unwrap();
|
|
|
|
ElGamalKeypair::default().write_json_file(&outfile).unwrap();
|
2021-10-11 10:43:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_write_keypair_file_truncate() {
|
|
|
|
let outfile = tmp_file_path("test_write_keypair_file_truncate.json");
|
|
|
|
|
2021-10-11 11:25:16 -07:00
|
|
|
ElGamalKeypair::default().write_json_file(&outfile).unwrap();
|
|
|
|
ElGamalKeypair::read_json_file(&outfile).unwrap();
|
2021-10-11 10:43:43 -07:00
|
|
|
|
|
|
|
// Ensure outfile is truncated
|
|
|
|
{
|
|
|
|
let mut f = File::create(&outfile).unwrap();
|
|
|
|
f.write_all(String::from_utf8([b'a'; 2048].to_vec()).unwrap().as_bytes())
|
|
|
|
.unwrap();
|
|
|
|
}
|
2021-10-11 11:25:16 -07:00
|
|
|
ElGamalKeypair::default().write_json_file(&outfile).unwrap();
|
|
|
|
ElGamalKeypair::read_json_file(&outfile).unwrap();
|
2021-10-11 10:43:43 -07:00
|
|
|
}
|
2021-10-21 17:48:25 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_secret_key_new() {
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
|
|
|
|
assert_ne!(
|
|
|
|
ElGamalSecretKey::new(&keypair1, &Pubkey::default())
|
|
|
|
.unwrap()
|
|
|
|
.0,
|
|
|
|
ElGamalSecretKey::new(&keypair2, &Pubkey::default())
|
|
|
|
.unwrap()
|
|
|
|
.0,
|
|
|
|
);
|
|
|
|
|
|
|
|
let null_signer = NullSigner::new(&Pubkey::default());
|
|
|
|
assert!(ElGamalSecretKey::new(&null_signer, &Pubkey::default()).is_err());
|
|
|
|
}
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|