clean up ElGamal decryption
This commit is contained in:
@ -1,17 +1,12 @@
|
|||||||
use core::ops::{Add, Neg, Sub};
|
use {
|
||||||
|
curve25519_dalek::{ristretto::RistrettoPoint, scalar::Scalar, traits::Identity},
|
||||||
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT as G;
|
serde::{Deserialize, Serialize},
|
||||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
std::collections::HashMap,
|
||||||
use curve25519_dalek::scalar::Scalar;
|
};
|
||||||
use curve25519_dalek::traits::Identity;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
|
const TWO15: u32 = 32768;
|
||||||
const TWO14: u32 = 16384; // 2^14
|
const TWO14: u32 = 16384; // 2^14
|
||||||
const TWO16: u32 = 65536; // 2^16
|
// const TWO16: u32 = 65536; // 2^16
|
||||||
const TWO18: u32 = 262144; // 2^18
|
const TWO18: u32 = 262144; // 2^18
|
||||||
|
|
||||||
/// Type that captures a discrete log challenge.
|
/// Type that captures a discrete log challenge.
|
||||||
@ -25,93 +20,52 @@ pub struct DiscreteLogInstance {
|
|||||||
pub target: RistrettoPoint,
|
pub target: RistrettoPoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Solves the discrete log instance using a 16/16 bit offline/online split
|
/// Builds a HashMap of 2^18 elements
|
||||||
impl DiscreteLogInstance {
|
pub fn decode_u32_precomputation(generator: RistrettoPoint) -> HashMap<[u8; 32], u32> {
|
||||||
/// Solves the discrete log problem under the assumption that the solution
|
let mut hashmap = HashMap::new();
|
||||||
/// is a 32-bit number.
|
|
||||||
pub fn decode_u32(self) -> Option<u32> {
|
|
||||||
let hashmap = DiscreteLogInstance::decode_u32_precomputation(self.generator);
|
|
||||||
self.decode_u32_online(&hashmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a HashMap of 2^16 elements
|
let two12_scalar = Scalar::from(TWO14);
|
||||||
pub fn decode_u32_precomputation(generator: RistrettoPoint) -> HashMap<HashableRistretto, u32> {
|
let identity = RistrettoPoint::identity(); // 0 * G
|
||||||
let mut hashmap = HashMap::new();
|
let generator = two12_scalar * generator; // 2^12 * G
|
||||||
|
|
||||||
let two16_scalar = Scalar::from(TWO16);
|
// iterator for 2^12*0G , 2^12*1G, 2^12*2G, ...
|
||||||
let identity = HashableRistretto(RistrettoPoint::identity()); // 0 * G
|
let ristretto_iter = RistrettoIterator::new(identity, generator);
|
||||||
let generator = HashableRistretto(two16_scalar * generator); // 2^16 * G
|
let mut steps_for_breakpoint = 0;
|
||||||
|
ristretto_iter.zip(0..TWO18).for_each(|(elem, x_hi)| {
|
||||||
|
let key = elem.compress().to_bytes();
|
||||||
|
hashmap.insert(key, x_hi);
|
||||||
|
|
||||||
// iterator for 2^16*0G , 2^16*1G, 2^16*2G, ...
|
// unclean way to print status update; will clean up later
|
||||||
let ristretto_iter = RistrettoIterator::new(identity, generator);
|
if x_hi % TWO15 == 0 {
|
||||||
ristretto_iter.zip(0..TWO16).for_each(|(elem, x_hi)| {
|
println!(" [{:?}/8] completed", steps_for_breakpoint);
|
||||||
hashmap.insert(elem, x_hi);
|
steps_for_breakpoint += 1;
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
println!(" [8/8] completed");
|
||||||
|
|
||||||
hashmap
|
hashmap
|
||||||
}
|
|
||||||
|
|
||||||
/// Solves the discrete log instance using the pre-computed HashMap by enumerating through 2^16
|
|
||||||
/// possible solutions
|
|
||||||
pub fn decode_u32_online(self, hashmap: &HashMap<HashableRistretto, u32>) -> Option<u32> {
|
|
||||||
// iterator for 0G, -1G, -2G, ...
|
|
||||||
let ristretto_iter = RistrettoIterator::new(
|
|
||||||
HashableRistretto(self.target),
|
|
||||||
HashableRistretto(-self.generator),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut decoded = None;
|
|
||||||
ristretto_iter.zip(0..TWO16).for_each(|(elem, x_lo)| {
|
|
||||||
if hashmap.contains_key(&elem) {
|
|
||||||
let x_hi = hashmap[&elem];
|
|
||||||
decoded = Some(x_lo + TWO16 * x_hi);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
decoded
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Solves the discrete log instance using a 18/14 bit offline/online split
|
/// Solves the discrete log instance using a 18/14 bit offline/online split
|
||||||
impl DiscreteLogInstance {
|
impl DiscreteLogInstance {
|
||||||
/// Solves the discrete log problem under the assumption that the solution
|
/// Solves the discrete log problem under the assumption that the solution
|
||||||
/// is a 32-bit number.
|
/// is a 32-bit number.
|
||||||
pub fn decode_u32_alt(self) -> Option<u32> {
|
pub fn decode_u32(self) -> Option<u32> {
|
||||||
let hashmap = DiscreteLogInstance::decode_u32_precomputation_alt(self.generator);
|
let hashmap = decode_u32_precomputation(self.generator);
|
||||||
self.decode_u32_online_alt(&hashmap)
|
self.decode_u32_online(&hashmap)
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a HashMap of 2^18 elements
|
|
||||||
pub fn decode_u32_precomputation_alt(
|
|
||||||
generator: RistrettoPoint,
|
|
||||||
) -> HashMap<HashableRistretto, u32> {
|
|
||||||
let mut hashmap = HashMap::new();
|
|
||||||
|
|
||||||
let two12_scalar = Scalar::from(TWO14);
|
|
||||||
let identity = HashableRistretto(RistrettoPoint::identity()); // 0 * G
|
|
||||||
let generator = HashableRistretto(two12_scalar * generator); // 2^12 * G
|
|
||||||
|
|
||||||
// iterator for 2^12*0G , 2^12*1G, 2^12*2G, ...
|
|
||||||
let ristretto_iter = RistrettoIterator::new(identity, generator);
|
|
||||||
ristretto_iter.zip(0..TWO18).for_each(|(elem, x_hi)| {
|
|
||||||
hashmap.insert(elem, x_hi);
|
|
||||||
});
|
|
||||||
|
|
||||||
hashmap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Solves the discrete log instance using the pre-computed HashMap by enumerating through 2^14
|
/// Solves the discrete log instance using the pre-computed HashMap by enumerating through 2^14
|
||||||
/// possible solutions
|
/// possible solutions
|
||||||
pub fn decode_u32_online_alt(self, hashmap: &HashMap<HashableRistretto, u32>) -> Option<u32> {
|
pub fn decode_u32_online(self, hashmap: &HashMap<[u8; 32], u32>) -> Option<u32> {
|
||||||
// iterator for 0G, -1G, -2G, ...
|
// iterator for 0G, -1G, -2G, ...
|
||||||
let ristretto_iter = RistrettoIterator::new(
|
let ristretto_iter = RistrettoIterator::new(self.target, -self.generator);
|
||||||
HashableRistretto(self.target),
|
|
||||||
HashableRistretto(-self.generator),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut decoded = None;
|
let mut decoded = None;
|
||||||
ristretto_iter.zip(0..TWO14).for_each(|(elem, x_lo)| {
|
ristretto_iter.zip(0..TWO14).for_each(|(elem, x_lo)| {
|
||||||
if hashmap.contains_key(&elem) {
|
let key = elem.compress().to_bytes();
|
||||||
let x_hi = hashmap[&elem];
|
if hashmap.contains_key(&key) {
|
||||||
|
let x_hi = hashmap[&key];
|
||||||
decoded = Some(x_lo + TWO14 * x_hi);
|
decoded = Some(x_lo + TWO14 * x_hi);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -119,92 +73,34 @@ impl DiscreteLogInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type wrapper for RistrettoPoint that implements the Hash trait
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq)]
|
|
||||||
pub struct HashableRistretto(pub RistrettoPoint);
|
|
||||||
|
|
||||||
impl HashableRistretto {
|
|
||||||
pub fn encode<T: Into<Scalar>>(amount: T) -> Self {
|
|
||||||
HashableRistretto(amount.into() * G)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for HashableRistretto {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
bincode::serialize(self).unwrap().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for HashableRistretto {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self == other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// HashableRistretto iterator.
|
/// HashableRistretto iterator.
|
||||||
///
|
///
|
||||||
/// Given an initial point X and a stepping point P, the iterator iterates through
|
/// Given an initial point X and a stepping point P, the iterator iterates through
|
||||||
/// X + 0*P, X + 1*P, X + 2*P, X + 3*P, ...
|
/// X + 0*P, X + 1*P, X + 2*P, X + 3*P, ...
|
||||||
struct RistrettoIterator {
|
struct RistrettoIterator {
|
||||||
pub curr: HashableRistretto,
|
pub curr: RistrettoPoint,
|
||||||
pub step: HashableRistretto,
|
pub step: RistrettoPoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RistrettoIterator {
|
impl RistrettoIterator {
|
||||||
fn new(curr: HashableRistretto, step: HashableRistretto) -> Self {
|
fn new(curr: RistrettoPoint, step: RistrettoPoint) -> Self {
|
||||||
RistrettoIterator { curr, step }
|
RistrettoIterator { curr, step }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for RistrettoIterator {
|
impl Iterator for RistrettoIterator {
|
||||||
type Item = HashableRistretto;
|
type Item = RistrettoPoint;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let r = self.curr;
|
let r = self.curr;
|
||||||
self.curr = self.curr + self.step;
|
self.curr += self.step;
|
||||||
Some(r)
|
Some(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Add<&'b HashableRistretto> for &'a HashableRistretto {
|
|
||||||
type Output = HashableRistretto;
|
|
||||||
|
|
||||||
fn add(self, other: &HashableRistretto) -> HashableRistretto {
|
|
||||||
HashableRistretto(self.0 + other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
define_add_variants!(
|
|
||||||
LHS = HashableRistretto,
|
|
||||||
RHS = HashableRistretto,
|
|
||||||
Output = HashableRistretto
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<'a, 'b> Sub<&'b HashableRistretto> for &'a HashableRistretto {
|
|
||||||
type Output = HashableRistretto;
|
|
||||||
|
|
||||||
fn sub(self, other: &HashableRistretto) -> HashableRistretto {
|
|
||||||
HashableRistretto(self.0 - other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
define_sub_variants!(
|
|
||||||
LHS = HashableRistretto,
|
|
||||||
RHS = HashableRistretto,
|
|
||||||
Output = HashableRistretto
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Neg for HashableRistretto {
|
|
||||||
type Output = HashableRistretto;
|
|
||||||
|
|
||||||
fn neg(self) -> HashableRistretto {
|
|
||||||
HashableRistretto(-self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use {super::*, curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT as G};
|
||||||
|
|
||||||
/// Discrete log test for 16/16 split
|
/// Discrete log test for 16/16 split
|
||||||
///
|
///
|
||||||
@ -212,7 +108,6 @@ mod tests {
|
|||||||
/// - 8 sec for precomputation
|
/// - 8 sec for precomputation
|
||||||
/// - 3 sec for online computation
|
/// - 3 sec for online computation
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn test_decode_correctness() {
|
fn test_decode_correctness() {
|
||||||
let amount: u32 = 65545;
|
let amount: u32 = 65545;
|
||||||
|
|
||||||
@ -223,7 +118,7 @@ mod tests {
|
|||||||
|
|
||||||
// Very informal measurements for now
|
// Very informal measurements for now
|
||||||
let start_precomputation = time::precise_time_s();
|
let start_precomputation = time::precise_time_s();
|
||||||
let precomputed_hashmap = DiscreteLogInstance::decode_u32_precomputation(G);
|
let precomputed_hashmap = decode_u32_precomputation(G);
|
||||||
let end_precomputation = time::precise_time_s();
|
let end_precomputation = time::precise_time_s();
|
||||||
|
|
||||||
let start_online = time::precise_time_s();
|
let start_online = time::precise_time_s();
|
||||||
@ -241,42 +136,4 @@ mod tests {
|
|||||||
end_online - start_online
|
end_online - start_online
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Discrete log test for 18/14 split
|
|
||||||
///
|
|
||||||
/// Very informal measurements on my machine:
|
|
||||||
/// - 33 sec for precomputation
|
|
||||||
/// - 0.8 sec for online computation
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn test_decode_alt_correctness() {
|
|
||||||
let amount: u32 = 65545;
|
|
||||||
|
|
||||||
let instance = DiscreteLogInstance {
|
|
||||||
generator: G,
|
|
||||||
target: Scalar::from(amount) * G,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Very informal measurements for now
|
|
||||||
let start_precomputation = time::precise_time_s();
|
|
||||||
let precomputed_hashmap = DiscreteLogInstance::decode_u32_precomputation_alt(G);
|
|
||||||
let end_precomputation = time::precise_time_s();
|
|
||||||
|
|
||||||
let start_online = time::precise_time_s();
|
|
||||||
let computed_amount = instance
|
|
||||||
.decode_u32_online_alt(&precomputed_hashmap)
|
|
||||||
.unwrap();
|
|
||||||
let end_online = time::precise_time_s();
|
|
||||||
|
|
||||||
assert_eq!(amount, computed_amount);
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"18/14 Split precomputation: {:?} sec",
|
|
||||||
end_precomputation - start_precomputation
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"18/14 Split online computation: {:?} sec",
|
|
||||||
end_online - start_online
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
||||||
use {
|
use {
|
||||||
crate::encryption::{
|
crate::encryption::{
|
||||||
encode::DiscreteLogInstance,
|
dlog::DiscreteLogInstance,
|
||||||
pedersen::{Pedersen, PedersenBase, PedersenComm, PedersenDecHandle, PedersenOpen},
|
pedersen::{Pedersen, PedersenBase, PedersenComm, PedersenDecHandle, PedersenOpen},
|
||||||
},
|
},
|
||||||
arrayref::{array_ref, array_refs},
|
arrayref::{array_ref, array_refs},
|
||||||
@ -12,6 +12,7 @@ use {
|
|||||||
scalar::Scalar,
|
scalar::Scalar,
|
||||||
},
|
},
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
|
std::collections::HashMap,
|
||||||
std::convert::TryInto,
|
std::convert::TryInto,
|
||||||
subtle::{Choice, ConstantTimeEq},
|
subtle::{Choice, ConstantTimeEq},
|
||||||
zeroize::Zeroize,
|
zeroize::Zeroize,
|
||||||
@ -100,6 +101,17 @@ impl ElGamal {
|
|||||||
let discrete_log_instance = ElGamal::decrypt(sk, ct);
|
let discrete_log_instance = ElGamal::decrypt(sk, ct);
|
||||||
discrete_log_instance.decode_u32()
|
discrete_log_instance.decode_u32()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// On input a secret key, ciphertext, and hashmap, the function decrypts the
|
||||||
|
/// ciphertext for a u32 value.
|
||||||
|
pub fn decrypt_u32_online(
|
||||||
|
sk: &ElGamalSK,
|
||||||
|
ct: &ElGamalCiphertext,
|
||||||
|
hashmap: &HashMap<[u8; 32], u32>,
|
||||||
|
) -> Option<u32> {
|
||||||
|
let discrete_log_instance = ElGamal::decrypt(sk, ct);
|
||||||
|
discrete_log_instance.decode_u32_online(hashmap)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Public key for the ElGamal encryption scheme.
|
/// Public key for the ElGamal encryption scheme.
|
||||||
@ -164,6 +176,15 @@ impl ElGamalSK {
|
|||||||
ElGamal::decrypt_u32(self, ct)
|
ElGamal::decrypt_u32(self, ct)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Utility method for code ergonomics.
|
||||||
|
pub fn decrypt_u32_online(
|
||||||
|
&self,
|
||||||
|
ct: &ElGamalCiphertext,
|
||||||
|
hashmap: &HashMap<[u8; 32], u32>,
|
||||||
|
) -> Option<u32> {
|
||||||
|
ElGamal::decrypt_u32_online(self, ct, hashmap)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
pub fn to_bytes(&self) -> [u8; 32] {
|
||||||
self.0.to_bytes()
|
self.0.to_bytes()
|
||||||
}
|
}
|
||||||
@ -249,6 +270,15 @@ impl ElGamalCiphertext {
|
|||||||
pub fn decrypt_u32(&self, sk: &ElGamalSK) -> Option<u32> {
|
pub fn decrypt_u32(&self, sk: &ElGamalSK) -> Option<u32> {
|
||||||
ElGamal::decrypt_u32(sk, self)
|
ElGamal::decrypt_u32(sk, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Utility method for code ergonomics.
|
||||||
|
pub fn decrypt_u32_online(
|
||||||
|
&self,
|
||||||
|
sk: &ElGamalSK,
|
||||||
|
hashmap: &HashMap<[u8; 32], u32>,
|
||||||
|
) -> Option<u32> {
|
||||||
|
ElGamal::decrypt_u32_online(sk, self, hashmap)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Add<&'b ElGamalCiphertext> for &'a ElGamalCiphertext {
|
impl<'a, 'b> Add<&'b ElGamalCiphertext> for &'a ElGamalCiphertext {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
pub mod elgamal;
|
|
||||||
pub mod dlog;
|
pub mod dlog;
|
||||||
|
pub mod elgamal;
|
||||||
pub mod pedersen;
|
pub mod pedersen;
|
||||||
|
Reference in New Issue
Block a user