#[cfg(feature = "ffi")]
mod ffi;
pub mod proofs;
pub mod types;
use self::types::{CHUNK_SIZE as CHUNK_SIZE_ENC_TRANS, *};
use crate::{
common::types::Amount, curve_arithmetic::*, elgamal::*, id::types::*, random_oracle::*,
};
use rand::*;
#[deprecated(
since = "5.0.1",
note = "encrypted transfers are deprecated and partially removed since protocol version 7"
)]
pub fn encrypt_amount<C: Curve, R: Rng>(
context: &GlobalContext<C>,
pk: &PublicKey<C>,
amount: Amount,
csprng: &mut R,
) -> (EncryptedAmount<C>, EncryptedAmountRandomness<C>) {
let h = context.encryption_in_exponent_generator();
let mut ciphers = encrypt_u64_in_chunks_given_generator(
pk,
amount.micro_ccd(),
CHUNK_SIZE_ENC_TRANS,
h,
csprng,
);
let (encryption_hi, randomness_hi) = ciphers.pop().unwrap();
let (encryption_low, randomness_low) = ciphers.pop().unwrap();
let enc = EncryptedAmount {
encryptions: [encryption_low, encryption_hi],
};
let rand = EncryptedAmountRandomness {
randomness: [randomness_low, randomness_hi],
};
(enc, rand)
}
#[deprecated(
since = "5.0.1",
note = "encrypted transfers are deprecated and partially removed since protocol version 7"
)]
pub fn encrypt_amount_with_fixed_randomness<C: Curve>(
context: &GlobalContext<C>,
amount: Amount,
) -> EncryptedAmount<C> {
let h = context.encryption_in_exponent_generator();
let val = amount.micro_ccd();
let chunks = CHUNK_SIZE_ENC_TRANS
.u64_to_chunks(val)
.into_iter()
.map(Value::<C>::from)
.collect::<Vec<_>>();
let mut ciphers = Vec::with_capacity(chunks.len());
for x in chunks {
let cipher = Cipher(C::zero_point(), h.mul_by_scalar(&x));
ciphers.push(cipher);
}
let encryption_hi = ciphers.pop().unwrap();
let encryption_low = ciphers.pop().unwrap();
EncryptedAmount {
encryptions: [encryption_low, encryption_hi],
}
}
#[deprecated(
since = "5.0.1",
note = "encrypted transfers are deprecated and partially removed since protocol version 7"
)]
pub fn aggregate<C: Curve>(
left: &EncryptedAmount<C>,
right: &EncryptedAmount<C>,
) -> EncryptedAmount<C> {
let encryption_hi = left.encryptions[1].combine(&right.encryptions[1]);
let encryption_low = left.encryptions[0].combine(&right.encryptions[0]);
EncryptedAmount {
encryptions: [encryption_low, encryption_hi],
}
}
pub fn decrypt_amount<C: Curve>(
table: &BabyStepGiantStep<C>,
sk: &SecretKey<C>,
amount: &EncryptedAmount<C>,
) -> Amount {
let low_chunk = sk.decrypt_exponent(&amount.encryptions[0], table);
let hi_chunk = sk.decrypt_exponent(&amount.encryptions[1], table);
Amount::from_micro_ccd(
CHUNK_SIZE_ENC_TRANS.chunks_to_u64([low_chunk, hi_chunk].iter().copied()),
)
}
impl<C: Curve> EncryptedAmount<C> {
pub fn join(&self) -> Cipher<C> {
let scale = 1u64 << u8::from(CHUNK_SIZE_ENC_TRANS);
self.encryptions[1]
.scale_u64(scale)
.combine(&self.encryptions[0])
}
}
#[deprecated(
since = "5.0.1",
note = "encrypted transfers are deprecated and partially removed since protocol version 7"
)]
pub fn make_transfer_data<C: Curve, R: Rng>(
ctx: &GlobalContext<C>,
receiver_pk: &PublicKey<C>,
sender_sk: &SecretKey<C>,
input_amount: &AggregatedDecryptedAmount<C>,
to_transfer: Amount,
csprng: &mut R,
) -> Option<EncryptedAmountTransferData<C>> {
let sender_pk = &PublicKey::from(sender_sk);
#[allow(deprecated)]
let mut ro = RandomOracle::domain("EncryptedTransfer");
ro.append_message(b"ctx", &ctx);
ro.append_message(b"receiver_pk", &receiver_pk);
ro.append_message(b"sender_pk", &sender_pk);
proofs::gen_enc_trans(
ctx,
&mut ro,
sender_pk,
sender_sk,
receiver_pk,
input_amount.agg_index,
&input_amount.agg_encrypted_amount.join(),
input_amount.agg_amount,
to_transfer,
csprng,
)
}
pub fn verify_transfer_data<C: Curve>(
ctx: &GlobalContext<C>,
receiver_pk: &PublicKey<C>,
sender_pk: &PublicKey<C>,
before_amount: &EncryptedAmount<C>,
transfer_data: &EncryptedAmountTransferData<C>,
) -> bool {
#[allow(deprecated)]
let mut ro = RandomOracle::domain("EncryptedTransfer");
ro.append_message(b"ctx", &ctx);
ro.append_message(b"receiver_pk", &receiver_pk);
ro.append_message(b"sender_pk", &sender_pk);
proofs::verify_enc_trans(
ctx,
&mut ro,
transfer_data,
sender_pk,
receiver_pk,
&before_amount.join(),
)
.is_ok()
}
pub fn make_sec_to_pub_transfer_data<C: Curve, R: Rng>(
ctx: &GlobalContext<C>,
sk: &SecretKey<C>,
input_amount: &AggregatedDecryptedAmount<C>,
to_transfer: Amount,
csprng: &mut R,
) -> Option<SecToPubAmountTransferData<C>> {
let pk = &PublicKey::from(sk);
#[allow(deprecated)]
let mut ro = RandomOracle::domain("SecToPubTransfer");
ro.append_message(b"ctx", &ctx);
ro.append_message(b"pk", &pk);
proofs::gen_sec_to_pub_trans(
ctx,
&mut ro,
pk,
sk,
input_amount.agg_index,
&input_amount.agg_encrypted_amount.join(),
input_amount.agg_amount,
to_transfer,
csprng,
)
}
pub fn verify_sec_to_pub_transfer_data<C: Curve>(
ctx: &GlobalContext<C>,
pk: &PublicKey<C>,
before_amount: &EncryptedAmount<C>,
transfer_data: &SecToPubAmountTransferData<C>,
) -> bool {
#[allow(deprecated)]
let mut ro = RandomOracle::domain("SecToPubTransfer");
ro.append_message(b"ctx", &ctx);
ro.append_message(b"pk", &pk);
proofs::verify_sec_to_pub_trans(ctx, &mut ro, transfer_data, pk, &before_amount.join()).is_ok()
}
#[cfg(test)]
mod tests {
use crate::curve_arithmetic::arkworks_instances::ArkGroup;
use super::*;
use ark_bls12_381::G1Projective;
type SomeCurve = ArkGroup<G1Projective>;
#[test]
fn test_encrypt_decrypt() {
let mut csprng = thread_rng();
let context = GlobalContext::<SomeCurve>::generate(String::from("genesis_string"));
let sk = SecretKey::generate(context.elgamal_generator(), &mut csprng);
let pk = PublicKey::from(&sk);
let amount = Amount::from_micro_ccd(csprng.gen::<u64>());
#[allow(deprecated)]
let (enc_amount, _) = encrypt_amount(&context, &pk, amount, &mut csprng);
let m = 1 << 16;
let table = BabyStepGiantStep::new(context.encryption_in_exponent_generator(), m);
let decrypted = decrypt_amount(&table, &sk, &enc_amount);
assert_eq!(
amount, decrypted,
"Decrypted amount differs from the original."
);
}
#[test]
fn test_scale() {
let mut csprng = thread_rng();
let context = GlobalContext::<SomeCurve>::generate(String::from("genesis_string"));
let sk = SecretKey::generate(context.elgamal_generator(), &mut csprng);
let pk = PublicKey::from(&sk);
let amount_1 = u64::from(csprng.gen::<u32>());
let amount_1 = Amount::from_micro_ccd(amount_1 << 2);
#[allow(deprecated)]
let (enc_amount_1, _) = encrypt_amount(&context, &pk, amount_1, &mut csprng);
let m = 1 << 16;
let table = BabyStepGiantStep::new(context.encryption_in_exponent_generator(), m);
let decrypted_1 = sk.decrypt_exponent(&enc_amount_1.join(), &table);
assert_eq!(
amount_1,
Amount::from_micro_ccd(decrypted_1),
"Decrypted combined encrypted amount differs from expected."
);
}
#[test]
fn test_encryption_randomness_zero() {
let mut csprng = thread_rng();
let context = GlobalContext::<SomeCurve>::generate(String::from("genesis_string"));
let sk = SecretKey::generate(context.elgamal_generator(), &mut csprng);
let amount = Amount::from_micro_ccd(csprng.gen::<u64>());
#[allow(deprecated)]
let dummy_encryption = encrypt_amount_with_fixed_randomness(&context, amount);
let m = 1 << 16;
let table = BabyStepGiantStep::new(context.encryption_in_exponent_generator(), m);
let decrypted = decrypt_amount(&table, &sk, &dummy_encryption);
assert_eq!(
amount, decrypted,
"Decrypted amount differs from the original."
);
}
#[allow(non_snake_case)]
#[test]
fn test_make_and_verify_transfer_data() {
let mut csprng = thread_rng();
let sk_sender: SecretKey<SomeCurve> = SecretKey::generate_all(&mut csprng);
let pk_sender = PublicKey::from(&sk_sender);
let sk_receiver: SecretKey<SomeCurve> =
SecretKey::generate(&pk_sender.generator, &mut csprng);
let pk_receiver = PublicKey::from(&sk_receiver);
let s: u64 = csprng.gen();
let a = csprng.gen_range(0..s);
let m = 2; let n = 32;
let nm = n * m;
let context = GlobalContext::<SomeCurve>::generate_size(String::from("genesis_string"), nm);
#[allow(deprecated)]
let S_in_chunks =
encrypt_amount(&context, &pk_sender, Amount::from_micro_ccd(s), &mut csprng);
let index = csprng.gen::<u64>().into(); let input_amount = AggregatedDecryptedAmount {
agg_amount: Amount::from_micro_ccd(s),
agg_encrypted_amount: S_in_chunks.0.clone(),
agg_index: index,
};
#[allow(deprecated)]
let transfer_data = make_transfer_data(
&context,
&pk_receiver,
&sk_sender,
&input_amount,
Amount::from_micro_ccd(a),
&mut csprng,
)
.unwrap();
assert_eq!(
verify_transfer_data(
&context,
&pk_receiver,
&pk_sender,
&S_in_chunks.0,
&transfer_data
),
true
);
}
#[test]
#[allow(non_snake_case)]
fn test_make_and_verify_sec_to_pub_transfer_data() {
let mut csprng = thread_rng();
let sk_sender: SecretKey<SomeCurve> = SecretKey::generate_all(&mut csprng);
let pk_sender = PublicKey::from(&sk_sender);
let s: u64 = csprng.gen();
let a = csprng.gen_range(0..s);
let m = 2; let n = 32;
let nm = n * m;
let context = GlobalContext::<SomeCurve>::generate_size(String::from("genesis_string"), nm);
#[allow(deprecated)]
let S_in_chunks =
encrypt_amount(&context, &pk_sender, Amount::from_micro_ccd(s), &mut csprng);
let index = csprng.gen::<u64>().into(); let input_amount = AggregatedDecryptedAmount {
agg_amount: Amount::from_micro_ccd(s),
agg_encrypted_amount: S_in_chunks.0.clone(),
agg_index: index,
};
let transfer_data = make_sec_to_pub_transfer_data(
&context,
&sk_sender,
&input_amount,
Amount::from_micro_ccd(a),
&mut csprng,
)
.unwrap();
assert_eq!(
verify_sec_to_pub_transfer_data(&context, &pk_sender, &S_in_chunks.0, &transfer_data),
true
);
}
}