Skip to content

Commit

Permalink
threshold without a trusted dealer. Missing misbehaviour handling
Browse files Browse the repository at this point in the history
  • Loading branch information
iquerejeta committed Aug 2, 2019
1 parent 288d61b commit b74282e
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 20 deletions.
40 changes: 21 additions & 19 deletions examples/threshold_no_trusted_dealer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ use std::collections::BTreeMap;
use std::time::Instant;

use threshold_crypto::{
Ciphertext, DecryptionShare, PublicKeySet, PublicKeyShare, SecretKeySet,
SecretKeyShare, G1,
HomCiphertext, DecryptionShare, PublicKeySet, PublicKeyShare, SecretKeySet,
SecretKeyShare, G1, G1Affine, FrRepr,
};
use pairing::{CurveAffine};

#[derive(Clone, Debug)]
struct UntrustedSociety {
Expand Down Expand Up @@ -94,7 +95,7 @@ struct Actor {
sk_2: SecretKeyShare, // Don't really see why this is here
pk_1: PublicKeyShare,
com_sk_1: G1,
msg_inbox: Option<Ciphertext>,
msg_inbox: Option<HomCiphertext>,
}

impl Actor{
Expand Down Expand Up @@ -148,15 +149,15 @@ impl PartialActor {
}

// Sends an encrypted message to an `Actor`.
fn send_msg(actor: &mut Actor, enc_msg: Ciphertext) {
fn send_msg(actor: &mut Actor, enc_msg: HomCiphertext) {
actor.msg_inbox = Some(enc_msg);
}

// A meeting of the different actors. At this meeting, actors collaborate to decrypt a shared
// ciphertext.
struct DecryptionMeeting {
pk_set: PublicKeySet,
ciphertext: Option<Ciphertext>,
ciphertext: Option<HomCiphertext>,
dec_shares: BTreeMap<usize, DecryptionShare>,
}

Expand All @@ -175,19 +176,19 @@ impl DecryptionMeeting {
self.ciphertext = Some(ciphertext.clone());
}

let dec_share = actor.sk_1.decrypt_share(&ciphertext).unwrap();
let dec_share_is_valid = actor
.pk_1
.verify_decryption_share(&dec_share, &ciphertext);
assert!(dec_share_is_valid);
let dec_share = actor.sk_1.hom_decrypt_share_no_verify(&ciphertext);
// let dec_share_is_valid = actor
// .pk_1
// .verify_decryption_share(&dec_share, &ciphertext);
// assert!(dec_share_is_valid);
self.dec_shares.insert(actor.id, dec_share);
}

// Tries to decrypt the shared ciphertext using the decryption shares.
fn decrypt_message(&self) -> Result<Vec<u8>, ()> {
fn hom_decrypt_message(&self) -> Result<G1, ()> {
let ciphertext = self.ciphertext.clone().unwrap();
self.pk_set
.decrypt(&self.dec_shares, &ciphertext)
.hom_decrypt(&self.dec_shares, &ciphertext)
.map_err(|_| ())
}
}
Expand All @@ -207,9 +208,9 @@ impl CheatingLogs {
fn main() {
// We will give an example were we have three trustees, where the treshold is one (this is, at least two entities must collaborate).
// Hence, we initiate three tellers.
let nmbr_users = 50;
let nmbr_users = 13;
let nmbr_users_128 = nmbr_users.clone() as u128;
let threshold = 40;
let threshold = 8;
let mut tellers: Vec<Teller> = Vec::new();
let mut master_pk_set: Vec<&PublicKeySet> = Vec::new();
let mut now = Instant::now();
Expand Down Expand Up @@ -243,8 +244,9 @@ fn main() {
let master_pub_key = society.clone().pk_set;

// We encrypt the plaintext
let msg = b"let's get pizza";
let ciphertext = master_pub_key.public_key().encrypt(msg);
let msg = FrRepr::from(17);
let encoded_msg = G1Affine::one().mul(msg);
let ciphertext = master_pub_key.public_key().hom_encrypt(&encoded_msg);

// We send the messages to each of the actors
for i in 0..nmbr_users {
Expand All @@ -258,14 +260,14 @@ fn main() {
now = Instant::now();
for i in 0..threshold {
meeting.accept_decryption_share(society.get_actor(i));
assert!(meeting.decrypt_message().is_err());
assert!(meeting.hom_decrypt_message().is_err());
}
// Then, we will get successful decryptions
for i in 0..(nmbr_users - threshold) {
meeting.accept_decryption_share(society.get_actor(threshold + i));
let res = meeting.decrypt_message();
let res = meeting.hom_decrypt_message();
assert!(res.is_ok());
assert_eq!(msg, res.unwrap().as_slice());
assert_eq!(encoded_msg, res.unwrap());
}
let time_decryption = now.elapsed().as_millis() / &nmbr_users_128;

Expand Down
143 changes: 142 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ptr::copy_nonoverlapping;
use core::ops::{Add, AddAssign};
use core::ops::{Add, AddAssign, Mul, };

use hex_fmt::HexFmt;
use log::debug;
Expand Down Expand Up @@ -119,6 +119,23 @@ impl PublicKey {
self.verify_g2(sig, hash_g2(msg))
}

/// Encrypt a message such the additive propertiers of ECC is mantained to the
/// message. For this to work, the message must be encoded as a point in the
/// curve
pub fn hom_encrypt(&self, msg: &G1) -> HomCiphertext {
self.hom_encrypt_with_rng(&mut OsRng::new().expect(ERR_OS_RNG), msg)
}
/// Homomorphically encrypts the message
pub fn hom_encrypt_with_rng<R: Rng>(&self, rng: &mut R, msg: &G1) -> HomCiphertext {
let r: Fr = rng.gen04();
let u = G1Affine::one().mul(r);
let v: G1 = {
let mut g = self.0.into_affine().mul(r);
g.add_assign(&msg);
g
};
HomCiphertext(u, v)
}
/// Encrypts the message using the OS random number generator.
///
/// Uses the `OsRng` by default. To pass in a custom random number generator, use
Expand Down Expand Up @@ -402,6 +419,15 @@ impl SecretKey {
self.sign_g2(hash_g2(msg))
}

/// Return the decryption of the homomorphically encrypted text. Attention
/// here as we are not verifying the HomCiphertext.
pub fn hom_decrypt(&self, ct: &HomCiphertext) -> G1 {
let HomCiphertext(ref u, ref v) = *ct;
let mut g = v.clone();
g.sub_assign(&u.into_affine().mul(*self.0));
g
}

/// Returns the decrypted text, or `None`, if the ciphertext isn't valid.
pub fn decrypt(&self, ct: &Ciphertext) -> Option<Vec<u8>> {
if !ct.verify() {
Expand Down Expand Up @@ -495,6 +521,11 @@ impl SecretKeyShare {
DecryptionShare(ct.0.into_affine().mul(*(self.0).0))
}

/// Returns a decryption share, without validating the homomorphic ciphertext.
pub fn hom_decrypt_share_no_verify(&self, ct: &HomCiphertext) -> DecryptionShare {
DecryptionShare(ct.0.into_affine().mul(*(self.0).0))
}

/// Generates a non-redacted debug string. This method differs from
/// the `Debug` implementation in that it *does* leak the secret prime
/// field element.
Expand All @@ -510,6 +541,69 @@ impl SecretKeyShare {
}
}

/// A homomorphically encrypted message in G1
#[derive(Deserialize, Serialize, Debug, Copy, Clone, PartialEq, Eq)]
pub struct HomCiphertext(
#[serde(with = "serde_impl::projective")] G1,
#[serde(with = "serde_impl::projective")] G1,
);

impl Hash for HomCiphertext {
fn hash<H: Hasher>(&self, state: &mut H) {
let HomCiphertext(ref u, ref v) = *self;
u.into_affine().into_compressed().as_ref().hash(state);
v.into_affine().into_compressed().as_ref().hash(state);
}
}

impl PartialOrd for HomCiphertext {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(&other))
}
}

impl Ord for HomCiphertext {
fn cmp(&self, other: &Self) -> Ordering {
let HomCiphertext(ref u0, ref v0) = self;
let HomCiphertext(ref u1, ref v1) = other;
cmp_projective(u0, u1)
.then(cmp_projective(v0, v1))
}
}

impl <'a, 'b> Add<&'b HomCiphertext> for &'a HomCiphertext {
type Output = HomCiphertext;

fn add(self, other: &'b HomCiphertext) -> HomCiphertext {
let mut point1 = G1::zero();
let mut point2 = G1::zero();
point1.add_assign(&self.0);
point1.add_assign(&other.0);
point2.add_assign(&self.1);
point2.add_assign(&other.1);
HomCiphertext(point1, point2)
}
}

impl <'a, 'b> Mul<&'b Fr> for &'a HomCiphertext {
type Output = HomCiphertext;

fn mul(self, other: &'b Fr) -> HomCiphertext {
HomCiphertext(self.0.into_affine().mul(*other), self.1.into_affine().mul(*other))
}
}

// Attention to chosen plaintext attacks.. see whether we are exposed to that.
// impl HomCiphertext {
// /// Returns `true` if this is a valid ciphertext. This check is necessary to prevent
// /// chosen-ciphertext attacks.
// pub fn verify(&self) -> bool {
// let HomCiphertext(ref u, ref v) = *self;
// let hash = hash_g1_g2(*u, v);
// PEngine::pairing(G1Affine::one(), *w) == PEngine::pairing(*u, hash)
// }
// }

/// An encrypted message.
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct Ciphertext(
Expand Down Expand Up @@ -656,6 +750,19 @@ impl PublicKeySet {
Ok(Signature(interpolate(self.commit.degree(), samples)?))
}

/// Combines the shares to homomorphically decrypt the ciphertext.
pub fn hom_decrypt<'a, T, I>(&self, shares: I, ct: &HomCiphertext) -> Result<G1>
where
I: IntoIterator<Item = (T, &'a DecryptionShare)>,
T: IntoFr,
{
let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
let mut g = ct.1;
let decryption_point = interpolate(self.commit.degree(), samples)?;
g.sub_assign(&decryption_point);
Ok(g)
}

/// Combines the shares to decrypt the ciphertext.
pub fn decrypt<'a, T, I>(&self, shares: I, ct: &Ciphertext) -> Result<Vec<u8>>
where
Expand Down Expand Up @@ -929,6 +1036,40 @@ mod tests {
assert!(!fake_ciphertext.verify());
assert_eq!(None, sk_bob.decrypt(&fake_ciphertext));
}
#[test]
fn test_hom_enc() {
let sk_bob: SecretKey = random();
let sk_eve: SecretKey = random();
let pk_bob = sk_bob.public_key();
let msg = G1::one();
let one = Fr::one();
let mut value = one.clone();


let ciphertext = pk_bob.hom_encrypt(&msg);

// Bob can decrypt the message.
let decrypted = sk_bob.hom_decrypt(&ciphertext);
assert_eq!(msg, decrypted);

// Eve can't
let decrypted_eve = sk_eve.hom_decrypt(&ciphertext);
assert_ne!(msg, decrypted_eve);

// Addition of ciphertexts results in the addition of plaintexts
value.add_assign(&one); // wtf is this?? I cant simply add two numbers?! Shitty Fr
let two = value.clone();
let msg_times_two = msg.into_affine().mul(two);

let added_ciphertext = &ciphertext + &ciphertext;
let decrypted_added = sk_bob.hom_decrypt(&added_ciphertext);
assert_eq!(msg_times_two, decrypted_added);

// Now we verify that multiplication by scalars is also preserved
let ctx_times_two = &ciphertext * &two;
assert_eq!(msg_times_two, sk_bob.hom_decrypt(&ctx_times_two));

}

#[test]
fn test_random_extreme_thresholds() {
Expand Down

0 comments on commit b74282e

Please sign in to comment.