Implement threshold encryption.

master
Andreas Fackler 7 years ago committed by Vladimir Komendantskiy
parent 419aae5adc
commit 40196fae1d
  1. 197
      mod.rs

197
mod.rs

@ -26,7 +26,7 @@ impl<E: Engine> PartialEq for PublicKey<E> {
impl<E: Engine> PublicKey<E> { impl<E: Engine> PublicKey<E> {
/// Returns `true` if the signature matches the element of `E::G2`. /// Returns `true` if the signature matches the element of `E::G2`.
pub fn verify_g2<H: Into<E::G2Affine>>(&self, sig: &Signature<E>, hash: H) -> bool { pub fn verify_g2<H: Into<E::G2Affine>>(&self, sig: &Signature<E>, hash: H) -> bool {
E::pairing(self.0, hash) == E::pairing(E::G1::one(), sig.0) E::pairing(self.0, hash) == E::pairing(E::G1Affine::one(), sig.0)
} }
/// Returns `true` if the signature matches the message. /// Returns `true` if the signature matches the message.
@ -34,21 +34,22 @@ impl<E: Engine> PublicKey<E> {
self.verify_g2(sig, hash_g2::<E, M>(msg)) self.verify_g2(sig, hash_g2::<E, M>(msg))
} }
/// Returns `true` if the decryption share matches the ciphertext.
pub fn verify_decryption_share(&self, share: &DecryptionShare<E>, ct: &Ciphertext<E>) -> bool {
let Ciphertext(ref u, ref v, ref w) = *ct;
let hash = hash_g1_g2::<E, _>(*u, v);
E::pairing(share.0, hash) == E::pairing(self.0, *w)
}
/// Encrypts the message. /// Encrypts the message.
pub fn encrypt<M: AsRef<[u8]>>(&self, msg: M) -> Ciphertext<E> { pub fn encrypt<M: AsRef<[u8]>>(&self, msg: M) -> Ciphertext<E> {
let r: E::Fr = OsRng::new().expect(ERR_OS_RNG).gen(); let r: E::Fr = OsRng::new().expect(ERR_OS_RNG).gen();
let u = E::G1Affine::one().mul(r); let u = E::G1Affine::one().mul(r);
let v: Vec<u8> = { let v: Vec<u8> = {
let mut g = self.0; let g = self.0.into_affine().mul(r);
g.mul_assign(r); xor_vec(&hash_bytes::<E>(g, msg.as_ref().len()), msg.as_ref())
hash_bytes::<E>(g, msg.as_ref().len())
.into_iter()
.zip(msg.as_ref())
.map(|(x, y)| x ^ y)
.collect()
}; };
let mut w = hash_g1_g2::<E, _>(u, &v); let w = hash_g1_g2::<E, _>(u, &v).into_affine().mul(r);
w.mul_assign(r);
Ciphertext(u, v, w) Ciphertext(u, v, w)
} }
} }
@ -100,14 +101,16 @@ impl<E: Engine> SecretKey<E> {
return None; return None;
} }
let Ciphertext(ref u, ref v, _) = *ct; let Ciphertext(ref u, ref v, _) = *ct;
let mut g = *u; let g = u.into_affine().mul(self.0);
g.mul_assign(self.0); Some(xor_vec(&hash_bytes::<E>(g, v.len()), v))
let decrypted = hash_bytes::<E>(g, v.len()) }
.into_iter()
.zip(v) /// Returns a decryption share, or `None`, if the ciphertext isn't valid.
.map(|(x, y)| x ^ y) pub fn decrypt_share(&self, ct: &Ciphertext<E>) -> Option<DecryptionShare<E>> {
.collect(); if !ct.verify() {
Some(decrypted) return None;
}
Some(DecryptionShare(ct.0.into_affine().mul(self.0)))
} }
} }
@ -127,7 +130,17 @@ impl<E: Engine> Ciphertext<E> {
pub fn verify(&self) -> bool { pub fn verify(&self) -> bool {
let Ciphertext(ref u, ref v, ref w) = *self; let Ciphertext(ref u, ref v, ref w) = *self;
let hash = hash_g1_g2::<E, _>(*u, v); let hash = hash_g1_g2::<E, _>(*u, v);
E::pairing(E::G1Affine::one(), w.into_affine()) == E::pairing(u.into_affine(), hash) E::pairing(E::G1Affine::one(), *w) == E::pairing(*u, hash)
}
}
/// A decryption share. A threshold of decryption shares can be used to decrypt a message.
#[derive(Debug)]
pub struct DecryptionShare<E: Engine>(E::G1);
impl<E: Engine> PartialEq for DecryptionShare<E> {
fn eq(&self, other: &DecryptionShare<E>) -> bool {
self.0 == other.0
} }
} }
@ -166,45 +179,25 @@ impl<E: Engine> PublicKeySet<E> {
PublicKey(pk) PublicKey(pk)
} }
/// Verifies that the given signatures are correct and combines them into a signature that can /// Combines the shares into a signature that can be verified with the main public key.
/// be verified with the main public key. pub fn combine_signatures<'a, ITR, IND>(&self, shares: ITR) -> Result<Signature<E>>
pub fn combine_signatures<'a, ITR, IND>(&self, items: ITR) -> Result<Signature<E>>
where where
ITR: IntoIterator<Item = (&'a IND, &'a Signature<E>)>, ITR: IntoIterator<Item = (&'a IND, &'a Signature<E>)>,
IND: Into<<E::Fr as PrimeField>::Repr> + Clone + 'a, IND: Into<<E::Fr as PrimeField>::Repr> + Clone + 'a,
{ {
let sigs: Vec<_> = items let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
.into_iter() Ok(Signature(interpolate(self.coeff.len(), samples)?))
.map(|(i, sig)| { }
let mut x = E::Fr::one();
x.add_assign(&E::Fr::from_repr(i.clone().into()).expect("invalid index")); /// Combines the shares to decrypt the ciphertext.
(x, sig) pub fn decrypt<'a, ITR, IND>(&self, shares: ITR, ct: &Ciphertext<E>) -> Result<Vec<u8>>
}) where
.collect(); ITR: IntoIterator<Item = (&'a IND, &'a DecryptionShare<E>)>,
if sigs.len() < self.coeff.len() { IND: Into<<E::Fr as PrimeField>::Repr> + Clone + 'a,
return Err(ErrorKind::NotEnoughShares.into()); {
} let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
let mut result = E::G2::zero(); let g = interpolate(self.coeff.len(), samples)?;
let mut indexes = Vec::new(); Ok(xor_vec(&hash_bytes::<E>(g, ct.1.len()), &ct.1))
for (x, sig) in sigs.iter().take(self.coeff.len()) {
if indexes.contains(x) {
return Err(ErrorKind::DuplicateEntry.into());
}
indexes.push(x.clone());
// Compute the value at 0 of the Lagrange polynomial that is `0` at the other data
// points but `1` at `x`.
let mut l0 = E::Fr::one();
for (x0, _) in sigs.iter().take(self.coeff.len()).filter(|(x0, _)| x0 != x) {
let mut denom = *x0;
denom.sub_assign(x);
l0.mul_assign(x0);
l0.mul_assign(&denom.inverse().expect("indices are different"));
}
let mut summand = sig.0;
summand.mul_assign(l0);
result.add_assign(&summand);
}
Ok(Signature(result))
} }
} }
@ -235,8 +228,7 @@ impl<E: Engine> SecretKeySet<E> {
where where
T: Into<<E::Fr as PrimeField>::Repr>, T: Into<<E::Fr as PrimeField>::Repr>,
{ {
let mut x = E::Fr::one(); let x = from_repr_plus_1(i.into());
x.add_assign(&E::Fr::from_repr(i.into()).expect("invalid index"));
let mut pk = *self.coeff.last().expect("at least one coefficient"); let mut pk = *self.coeff.last().expect("at least one coefficient");
for c in self.coeff.iter().rev().skip(1) { for c in self.coeff.iter().rev().skip(1) {
pk.mul_assign(&x); pk.mul_assign(&x);
@ -288,6 +280,53 @@ fn hash_bytes<E: Engine>(g1: E::G1, len: usize) -> Vec<u8> {
rng.gen_iter().take(len).collect() rng.gen_iter().take(len).collect()
} }
/// Returns the bitwise xor.
fn xor_vec(x: &[u8], y: &[u8]) -> Vec<u8> {
x.iter().zip(y).map(|(a, b)| a ^ b).collect()
}
/// Given a list of `t` samples `(i - 1, f(i) * g)` for a polynomial `f` of degree `t - 1`, and a
/// group generator `g`, returns `f(0) * g`.
pub fn interpolate<'a, C, ITR, IND>(t: usize, items: ITR) -> Result<C>
where
C: CurveProjective,
ITR: IntoIterator<Item = (&'a IND, &'a C)>,
IND: Into<<C::Scalar as PrimeField>::Repr> + Clone + 'a,
{
let samples: Vec<_> = items
.into_iter()
.map(|(i, sample)| (from_repr_plus_1::<C::Scalar>(i.clone().into()), sample))
.collect();
if samples.len() < t {
return Err(ErrorKind::NotEnoughShares.into());
}
let mut result = C::zero();
let mut indexes = Vec::new();
for (x, sample) in samples.iter().take(t) {
if indexes.contains(x) {
return Err(ErrorKind::DuplicateEntry.into());
}
indexes.push(x.clone());
// Compute the value at 0 of the Lagrange polynomial that is `0` at the other data
// points but `1` at `x`.
let mut l0 = C::Scalar::one();
for (x0, _) in samples.iter().take(t).filter(|(x0, _)| x0 != x) {
let mut denom = *x0;
denom.sub_assign(x);
l0.mul_assign(x0);
l0.mul_assign(&denom.inverse().expect("indices are different"));
}
result.add_assign(&sample.into_affine().mul(l0));
}
Ok(result)
}
fn from_repr_plus_1<F: PrimeField>(repr: F::Repr) -> F {
let mut x = F::one();
x.add_assign(&F::from_repr(repr).expect("invalid index"));
x
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -366,6 +405,38 @@ mod tests {
assert_eq!(None, sk_bob.decrypt(&fake_ciphertext)); assert_eq!(None, sk_bob.decrypt(&fake_ciphertext));
} }
#[test]
fn test_threshold_enc() {
let mut rng = rand::thread_rng();
let sk_set = SecretKeySet::<Bls12>::new(3, &mut rng);
let pk_set = sk_set.public_keys();
let msg = b"Totally real news";
let ciphertext = pk_set.public_key().encrypt(&msg[..]);
// The threshold is 3, so 4 signature shares will suffice to decrypt.
let shares: BTreeMap<_, _> = [5, 8, 7, 10]
.into_iter()
.map(|i| {
let ski = sk_set.secret_key_share(*i);
let share = ski.decrypt_share(&ciphertext).expect("ciphertext is valid");
(*i, share)
})
.collect();
// Each of the shares is valid matching its public key share.
for (i, share) in &shares {
pk_set
.public_key_share(*i)
.verify_decryption_share(share, &ciphertext);
}
// Combined, they can decrypt the message.
let decrypted = pk_set
.decrypt(&shares, &ciphertext)
.expect("decryption shares match");
assert_eq!(msg[..], decrypted[..]);
}
/// Some basic sanity checks for the hash function. /// Some basic sanity checks for the hash function.
#[test] #[test]
fn test_hash_g2() { fn test_hash_g2() {
@ -402,7 +473,7 @@ mod tests {
mod serde { mod serde {
use pairing::{CurveAffine, CurveProjective, EncodedPoint, Engine}; use pairing::{CurveAffine, CurveProjective, EncodedPoint, Engine};
use super::{PublicKey, Signature}; use super::{DecryptionShare, PublicKey, Signature};
use serde::de::Error as DeserializeError; use serde::de::Error as DeserializeError;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -433,6 +504,18 @@ mod serde {
} }
} }
impl<E: Engine> Serialize for DecryptionShare<E> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
serialize_projective(&self.0, s)
}
}
impl<'de, E: Engine> Deserialize<'de> for DecryptionShare<E> {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
Ok(DecryptionShare(deserialize_projective(d)?))
}
}
/// Serializes the compressed representation of a group element. /// Serializes the compressed representation of a group element.
fn serialize_projective<S, C>(c: &C, s: S) -> Result<S::Ok, S::Error> fn serialize_projective<S, C>(c: &C, s: S) -> Result<S::Ok, S::Error>
where where

Loading…
Cancel
Save