2021-04-13 08:55:38 +10:00
|
|
|
#![allow(non_snake_case)]
|
|
|
|
#![allow(non_upper_case_globals)]
|
|
|
|
#![allow(non_camel_case_types)]
|
|
|
|
|
|
|
|
use anyhow::{bail, Result};
|
|
|
|
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
|
|
|
|
use curve25519_dalek::edwards::EdwardsPoint;
|
|
|
|
use curve25519_dalek::scalar::Scalar;
|
|
|
|
use hash_edwards_to_edwards::hash_point_to_point;
|
2021-05-10 13:21:40 +10:00
|
|
|
use rand::{CryptoRng, Rng};
|
2021-04-13 08:55:38 +10:00
|
|
|
use std::convert::TryInto;
|
|
|
|
use tiny_keccak::{Hasher, Keccak};
|
|
|
|
|
2021-05-04 16:59:44 +10:00
|
|
|
pub const RING_SIZE: usize = 11;
|
2021-04-13 08:55:38 +10:00
|
|
|
const DOMAIN_TAG: &str = "CSLAG_c";
|
|
|
|
|
2021-05-10 15:34:19 +10:00
|
|
|
#[rustfmt::skip]
|
|
|
|
// aggregation hashes:
|
|
|
|
// mu_{P, C} =
|
|
|
|
// keccak256("CLSAG_agg_{0, 1}" ||
|
|
|
|
// ring || ring of commitments || I || z * hash_to_point(signing pk) || pseudooutput commitment)
|
|
|
|
//
|
|
|
|
// where z = blinding of real commitment - blinding of pseudooutput commitment.
|
|
|
|
|
|
|
|
// for every iteration we compute:
|
|
|
|
// c_p = h_prev * mu_P; and
|
|
|
|
// c_c = h_prev * mu_C.
|
|
|
|
//
|
|
|
|
// L_i = s_i * G + c_p * pk_i + c_c * (commitment_i - pseudoutcommitment)
|
|
|
|
// R_i = s_i * H_p_pk_i + c_p * I + c_c * (z * hash_to_point(signing pk))
|
|
|
|
//
|
|
|
|
// h = keccak256("CLSAG_round" || ring
|
|
|
|
// ring of commitments || pseudooutput commitment || msg || L_i || R_i)
|
2021-04-13 08:55:38 +10:00
|
|
|
fn challenge(
|
|
|
|
s_i: Scalar,
|
|
|
|
pk_i: EdwardsPoint,
|
|
|
|
h_prev: Scalar,
|
|
|
|
I: EdwardsPoint,
|
|
|
|
mut prefix: Keccak,
|
|
|
|
) -> Result<Scalar> {
|
|
|
|
let L_i = s_i * ED25519_BASEPOINT_POINT + h_prev * pk_i;
|
|
|
|
|
|
|
|
let H_p_pk_i = hash_point_to_point(pk_i);
|
|
|
|
|
|
|
|
let R_i = s_i * H_p_pk_i + h_prev * I;
|
|
|
|
|
|
|
|
prefix.update(&L_i.compress().as_bytes().to_vec());
|
|
|
|
prefix.update(&R_i.compress().as_bytes().to_vec());
|
|
|
|
|
|
|
|
let mut output = [0u8; 64];
|
|
|
|
prefix.finalize(&mut output);
|
|
|
|
|
|
|
|
Ok(Scalar::from_bytes_mod_order_wide(&output))
|
|
|
|
}
|
|
|
|
|
2021-05-10 15:34:19 +10:00
|
|
|
#[rustfmt::skip]
|
|
|
|
// h_0 = keccak256("CLSAG_round" || ring
|
|
|
|
// ring of commitments || pseudooutput commitment || msg || alpha * G || alpha * hash_to_point(signing pk))
|
|
|
|
//
|
|
|
|
// where alpha is random
|
|
|
|
|
2021-04-13 08:55:38 +10:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
fn final_challenge(
|
|
|
|
fake_responses: [Scalar; RING_SIZE - 1],
|
|
|
|
ring: [EdwardsPoint; RING_SIZE],
|
|
|
|
T_a: EdwardsPoint,
|
|
|
|
T_b: EdwardsPoint,
|
|
|
|
R_a: EdwardsPoint,
|
|
|
|
I_hat_a: EdwardsPoint,
|
|
|
|
I_hat_b: EdwardsPoint,
|
|
|
|
R_prime_a: EdwardsPoint,
|
2021-05-07 16:40:37 +10:00
|
|
|
I: EdwardsPoint,
|
2021-04-13 08:55:38 +10:00
|
|
|
msg: [u8; 32],
|
|
|
|
) -> Result<(Scalar, Scalar)> {
|
|
|
|
let h_0 = {
|
|
|
|
let ring = ring
|
|
|
|
.iter()
|
|
|
|
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
|
|
|
.collect::<Vec<u8>>();
|
|
|
|
|
|
|
|
let mut keccak = tiny_keccak::Keccak::v512();
|
|
|
|
keccak.update(DOMAIN_TAG.as_bytes());
|
|
|
|
keccak.update(ring.as_slice());
|
|
|
|
keccak.update(&msg);
|
|
|
|
keccak.update((T_a + T_b + R_a).compress().as_bytes());
|
|
|
|
keccak.update((I_hat_a + I_hat_b + R_prime_a).compress().as_bytes());
|
|
|
|
let mut output = [0u8; 64];
|
|
|
|
keccak.finalize(&mut output);
|
|
|
|
|
|
|
|
Scalar::from_bytes_mod_order_wide(&output)
|
|
|
|
};
|
|
|
|
// ring size is 11
|
|
|
|
|
|
|
|
let ring_concat = ring
|
|
|
|
.iter()
|
|
|
|
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
|
|
|
.collect::<Vec<u8>>();
|
|
|
|
|
|
|
|
let mut keccak = tiny_keccak::Keccak::v512();
|
|
|
|
keccak.update(DOMAIN_TAG.as_bytes());
|
|
|
|
keccak.update(ring_concat.as_slice());
|
|
|
|
keccak.update(&msg);
|
|
|
|
|
|
|
|
let h_last = fake_responses
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.fold(h_0, |h_prev, (i, s_i)| {
|
|
|
|
let pk_i = ring[i + 1];
|
|
|
|
// TODO: Do not unwrap here
|
|
|
|
challenge(*s_i, pk_i, h_prev, I, keccak.clone()).unwrap()
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok((h_last, h_0))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct AdaptorSignature {
|
|
|
|
s_0_a: Scalar,
|
|
|
|
s_0_b: Scalar,
|
|
|
|
fake_responses: [Scalar; RING_SIZE - 1],
|
|
|
|
h_0: Scalar,
|
|
|
|
/// Key image of the real key in the ring.
|
|
|
|
I: EdwardsPoint,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AdaptorSignature {
|
|
|
|
pub fn adapt(self, y: Scalar) -> Signature {
|
|
|
|
let r_last = self.s_0_a + self.s_0_b + y;
|
|
|
|
|
|
|
|
let responses = self
|
|
|
|
.fake_responses
|
|
|
|
.iter()
|
|
|
|
.chain([r_last].iter())
|
|
|
|
.copied()
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.try_into()
|
|
|
|
.expect("correct response size");
|
|
|
|
|
|
|
|
Signature {
|
|
|
|
responses,
|
|
|
|
h_0: self.h_0,
|
|
|
|
I: self.I,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Signature {
|
|
|
|
pub responses: [Scalar; RING_SIZE],
|
|
|
|
pub h_0: Scalar,
|
|
|
|
/// Key image of the real key in the ring.
|
|
|
|
pub I: EdwardsPoint,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Signature {
|
|
|
|
#[cfg(test)]
|
|
|
|
fn verify(&self, ring: [EdwardsPoint; RING_SIZE], msg: &[u8; 32]) -> Result<bool> {
|
|
|
|
let ring_concat = ring
|
|
|
|
.iter()
|
|
|
|
.flat_map(|pk| pk.compress().as_bytes().to_vec())
|
|
|
|
.collect::<Vec<u8>>();
|
|
|
|
|
|
|
|
let mut prefix = tiny_keccak::Keccak::v512();
|
|
|
|
prefix.update(DOMAIN_TAG.as_bytes());
|
|
|
|
prefix.update(ring_concat.as_slice());
|
|
|
|
prefix.update(msg);
|
|
|
|
|
|
|
|
let mut h = self.h_0;
|
|
|
|
|
|
|
|
for (i, s_i) in self.responses.iter().enumerate() {
|
|
|
|
let pk_i = ring[(i + 1) % RING_SIZE];
|
|
|
|
h = challenge(*s_i, pk_i, h, self.I, prefix.clone())?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(h == self.h_0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-04 16:59:44 +10:00
|
|
|
impl From<Signature> for monero::util::ringct::Clsag {
|
|
|
|
fn from(from: Signature) -> Self {
|
|
|
|
Self {
|
|
|
|
s: from
|
|
|
|
.responses
|
|
|
|
.iter()
|
|
|
|
.map(|s| monero::util::ringct::Key { key: s.to_bytes() })
|
|
|
|
.collect(),
|
|
|
|
c1: monero::util::ringct::Key {
|
|
|
|
key: from.h_0.to_bytes(),
|
|
|
|
},
|
|
|
|
D: monero::util::ringct::Key {
|
|
|
|
key: from.I.compress().to_bytes(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-13 08:55:38 +10:00
|
|
|
pub struct Alice0 {
|
|
|
|
// secret index is always 0
|
|
|
|
ring: [EdwardsPoint; RING_SIZE],
|
|
|
|
fake_responses: [Scalar; RING_SIZE - 1],
|
|
|
|
msg: [u8; 32],
|
|
|
|
// encryption key
|
|
|
|
R_a: EdwardsPoint,
|
|
|
|
// R'a = r_a*H_p(p_k) where p_k is the signing public key
|
|
|
|
R_prime_a: EdwardsPoint,
|
|
|
|
// this is not s_a cos of something to with one-time-address??
|
|
|
|
s_prime_a: Scalar,
|
|
|
|
// secret value:
|
|
|
|
alpha_a: Scalar,
|
|
|
|
H_p_pk: EdwardsPoint,
|
|
|
|
I_a: EdwardsPoint,
|
|
|
|
I_hat_a: EdwardsPoint,
|
|
|
|
T_a: EdwardsPoint,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Alice0 {
|
|
|
|
pub fn new(
|
|
|
|
ring: [EdwardsPoint; RING_SIZE],
|
|
|
|
msg: [u8; 32],
|
|
|
|
R_a: EdwardsPoint,
|
|
|
|
R_prime_a: EdwardsPoint,
|
|
|
|
s_prime_a: Scalar,
|
2021-05-10 13:21:40 +10:00
|
|
|
rng: &mut (impl Rng + CryptoRng),
|
2021-04-13 08:55:38 +10:00
|
|
|
) -> Result<Self> {
|
|
|
|
let mut fake_responses = [Scalar::zero(); RING_SIZE - 1];
|
|
|
|
for response in fake_responses.iter_mut().take(RING_SIZE - 1) {
|
2021-05-07 17:48:07 +10:00
|
|
|
*response = Scalar::random(rng);
|
2021-04-13 08:55:38 +10:00
|
|
|
}
|
2021-05-07 17:48:07 +10:00
|
|
|
let alpha_a = Scalar::random(rng);
|
2021-04-13 08:55:38 +10:00
|
|
|
|
|
|
|
let p_k = ring[0];
|
|
|
|
let H_p_pk = hash_point_to_point(p_k);
|
|
|
|
|
|
|
|
let I_a = s_prime_a * H_p_pk;
|
|
|
|
let I_hat_a = alpha_a * H_p_pk;
|
|
|
|
let T_a = alpha_a * ED25519_BASEPOINT_POINT;
|
|
|
|
|
|
|
|
Ok(Alice0 {
|
|
|
|
ring,
|
|
|
|
fake_responses,
|
|
|
|
msg,
|
|
|
|
R_a,
|
|
|
|
R_prime_a,
|
|
|
|
s_prime_a,
|
|
|
|
alpha_a,
|
|
|
|
H_p_pk,
|
|
|
|
I_a,
|
|
|
|
I_hat_a,
|
|
|
|
T_a,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-05-07 17:48:07 +10:00
|
|
|
pub fn next_message(&self, rng: &mut (impl Rng + CryptoRng)) -> Message0 {
|
2021-04-13 08:55:38 +10:00
|
|
|
Message0 {
|
|
|
|
pi_a: DleqProof::new(
|
|
|
|
ED25519_BASEPOINT_POINT,
|
|
|
|
self.T_a,
|
|
|
|
self.H_p_pk,
|
|
|
|
self.I_hat_a,
|
|
|
|
self.alpha_a,
|
2021-05-10 13:21:40 +10:00
|
|
|
rng,
|
2021-04-13 08:55:38 +10:00
|
|
|
),
|
|
|
|
c_a: Commitment::new(self.fake_responses, self.I_a, self.I_hat_a, self.T_a),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn receive(self, msg: Message1) -> Result<Alice1> {
|
|
|
|
msg.pi_b
|
|
|
|
.verify(ED25519_BASEPOINT_POINT, msg.T_b, self.H_p_pk, msg.I_hat_b)?;
|
|
|
|
|
|
|
|
let (h_last, h_0) = final_challenge(
|
|
|
|
self.fake_responses,
|
|
|
|
self.ring,
|
|
|
|
self.T_a,
|
|
|
|
msg.T_b,
|
|
|
|
self.R_a,
|
|
|
|
self.I_hat_a,
|
|
|
|
msg.I_hat_b,
|
|
|
|
self.R_prime_a,
|
2021-05-07 16:40:37 +10:00
|
|
|
self.I_a + msg.I_b,
|
2021-04-13 08:55:38 +10:00
|
|
|
self.msg,
|
|
|
|
)?;
|
|
|
|
|
2021-05-10 15:34:19 +10:00
|
|
|
// TODO: Final scalar is computed slightly differentley for Monero (involves
|
|
|
|
// mu_P and mu_C constants)
|
2021-04-13 08:55:38 +10:00
|
|
|
let s_0_a = self.alpha_a - h_last * self.s_prime_a;
|
|
|
|
|
|
|
|
Ok(Alice1 {
|
|
|
|
fake_responses: self.fake_responses,
|
|
|
|
h_0,
|
|
|
|
I_b: msg.I_b,
|
|
|
|
s_0_a,
|
|
|
|
I_a: self.I_a,
|
|
|
|
I_hat_a: self.I_hat_a,
|
|
|
|
T_a: self.T_a,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Alice1 {
|
|
|
|
fake_responses: [Scalar; RING_SIZE - 1],
|
|
|
|
I_a: EdwardsPoint,
|
|
|
|
I_hat_a: EdwardsPoint,
|
|
|
|
T_a: EdwardsPoint,
|
|
|
|
h_0: Scalar,
|
|
|
|
I_b: EdwardsPoint,
|
|
|
|
s_0_a: Scalar,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Alice1 {
|
|
|
|
pub fn next_message(&self) -> Message2 {
|
|
|
|
Message2 {
|
|
|
|
d_a: Opening::new(self.fake_responses, self.I_a, self.I_hat_a, self.T_a),
|
|
|
|
s_0_a: self.s_0_a,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn receive(self, msg: Message3) -> Alice2 {
|
|
|
|
let adaptor_sig = AdaptorSignature {
|
|
|
|
s_0_a: self.s_0_a,
|
|
|
|
s_0_b: msg.s_0_b,
|
|
|
|
fake_responses: self.fake_responses,
|
|
|
|
h_0: self.h_0,
|
|
|
|
I: self.I_a + self.I_b,
|
|
|
|
};
|
|
|
|
|
|
|
|
Alice2 { adaptor_sig }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Alice2 {
|
|
|
|
pub adaptor_sig: AdaptorSignature,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Bob0 {
|
|
|
|
// secret index is always 0
|
|
|
|
ring: [EdwardsPoint; RING_SIZE],
|
|
|
|
msg: [u8; 32],
|
|
|
|
// encryption key
|
|
|
|
R_a: EdwardsPoint,
|
|
|
|
// R'a = r_a*H_p(p_k) where p_k is the signing public key
|
|
|
|
R_prime_a: EdwardsPoint,
|
|
|
|
s_b: Scalar,
|
|
|
|
// secret value:
|
|
|
|
alpha_b: Scalar,
|
|
|
|
H_p_pk: EdwardsPoint,
|
|
|
|
I_b: EdwardsPoint,
|
|
|
|
I_hat_b: EdwardsPoint,
|
|
|
|
T_b: EdwardsPoint,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bob0 {
|
|
|
|
pub fn new(
|
|
|
|
ring: [EdwardsPoint; RING_SIZE],
|
|
|
|
msg: [u8; 32],
|
|
|
|
R_a: EdwardsPoint,
|
|
|
|
R_prime_a: EdwardsPoint,
|
|
|
|
s_b: Scalar,
|
2021-05-10 13:21:40 +10:00
|
|
|
rng: &mut (impl Rng + CryptoRng),
|
2021-04-13 08:55:38 +10:00
|
|
|
) -> Result<Self> {
|
2021-05-07 17:48:07 +10:00
|
|
|
let alpha_b = Scalar::random(rng);
|
2021-04-13 08:55:38 +10:00
|
|
|
|
|
|
|
let p_k = ring[0];
|
|
|
|
let H_p_pk = hash_point_to_point(p_k);
|
|
|
|
|
|
|
|
let I_b = s_b * H_p_pk;
|
|
|
|
let I_hat_b = alpha_b * H_p_pk;
|
|
|
|
let T_b = alpha_b * ED25519_BASEPOINT_POINT;
|
|
|
|
|
|
|
|
Ok(Bob0 {
|
|
|
|
ring,
|
|
|
|
msg,
|
|
|
|
R_a,
|
|
|
|
R_prime_a,
|
|
|
|
s_b,
|
|
|
|
alpha_b,
|
|
|
|
H_p_pk,
|
|
|
|
I_b,
|
|
|
|
I_hat_b,
|
|
|
|
T_b,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn receive(self, msg: Message0) -> Bob1 {
|
|
|
|
Bob1 {
|
|
|
|
ring: self.ring,
|
|
|
|
msg: self.msg,
|
|
|
|
R_a: self.R_a,
|
|
|
|
R_prime_a: self.R_prime_a,
|
|
|
|
s_b: self.s_b,
|
|
|
|
alpha_b: self.alpha_b,
|
|
|
|
H_p_pk: self.H_p_pk,
|
|
|
|
I_b: self.I_b,
|
|
|
|
I_hat_b: self.I_hat_b,
|
|
|
|
T_b: self.T_b,
|
|
|
|
pi_a: msg.pi_a,
|
|
|
|
c_a: msg.c_a,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Bob1 {
|
|
|
|
// secret index is always 0
|
|
|
|
ring: [EdwardsPoint; RING_SIZE],
|
|
|
|
msg: [u8; 32],
|
|
|
|
// encryption key
|
|
|
|
R_a: EdwardsPoint,
|
|
|
|
// R'a = r_a*H_p(p_k) where p_k is the signing public key
|
|
|
|
R_prime_a: EdwardsPoint,
|
|
|
|
s_b: Scalar,
|
|
|
|
// secret value:
|
|
|
|
alpha_b: Scalar,
|
|
|
|
H_p_pk: EdwardsPoint,
|
|
|
|
I_b: EdwardsPoint,
|
|
|
|
I_hat_b: EdwardsPoint,
|
|
|
|
T_b: EdwardsPoint,
|
|
|
|
pi_a: DleqProof,
|
|
|
|
c_a: Commitment,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bob1 {
|
2021-05-07 17:48:07 +10:00
|
|
|
pub fn next_message(&self, rng: &mut (impl Rng + CryptoRng)) -> Message1 {
|
2021-04-13 08:55:38 +10:00
|
|
|
Message1 {
|
|
|
|
I_b: self.I_b,
|
|
|
|
T_b: self.T_b,
|
|
|
|
I_hat_b: self.I_hat_b,
|
|
|
|
pi_b: DleqProof::new(
|
|
|
|
ED25519_BASEPOINT_POINT,
|
|
|
|
self.T_b,
|
|
|
|
self.H_p_pk,
|
|
|
|
self.I_hat_b,
|
|
|
|
self.alpha_b,
|
2021-05-10 13:21:40 +10:00
|
|
|
rng,
|
2021-04-13 08:55:38 +10:00
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn receive(self, msg: Message2) -> Result<Bob2> {
|
|
|
|
let (fake_responses, I_a, I_hat_a, T_a) = msg.d_a.open(self.c_a)?;
|
|
|
|
|
|
|
|
self.pi_a
|
|
|
|
.verify(ED25519_BASEPOINT_POINT, T_a, self.H_p_pk, I_hat_a)?;
|
|
|
|
|
|
|
|
let (h_last, h_0) = final_challenge(
|
|
|
|
fake_responses,
|
|
|
|
self.ring,
|
|
|
|
T_a,
|
|
|
|
self.T_b,
|
|
|
|
self.R_a,
|
|
|
|
I_hat_a,
|
|
|
|
self.I_hat_b,
|
|
|
|
self.R_prime_a,
|
2021-05-07 16:40:37 +10:00
|
|
|
I_a + self.I_b,
|
2021-04-13 08:55:38 +10:00
|
|
|
self.msg,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let s_0_b = self.alpha_b - h_last * self.s_b;
|
|
|
|
|
|
|
|
let adaptor_sig = AdaptorSignature {
|
|
|
|
s_0_a: msg.s_0_a,
|
|
|
|
s_0_b,
|
|
|
|
fake_responses,
|
|
|
|
h_0,
|
|
|
|
I: I_a + self.I_b,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Bob2 { s_0_b, adaptor_sig })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Bob2 {
|
|
|
|
s_0_b: Scalar,
|
|
|
|
pub adaptor_sig: AdaptorSignature,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bob2 {
|
|
|
|
pub fn next_message(&self) -> Message3 {
|
|
|
|
Message3 { s_0_b: self.s_0_b }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct DleqProof {
|
|
|
|
s: Scalar,
|
|
|
|
c: Scalar,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DleqProof {
|
|
|
|
fn new(
|
|
|
|
G: EdwardsPoint,
|
|
|
|
xG: EdwardsPoint,
|
|
|
|
H: EdwardsPoint,
|
|
|
|
xH: EdwardsPoint,
|
|
|
|
x: Scalar,
|
2021-05-10 13:21:40 +10:00
|
|
|
rng: &mut (impl Rng + CryptoRng),
|
2021-04-13 08:55:38 +10:00
|
|
|
) -> Self {
|
2021-05-07 17:48:07 +10:00
|
|
|
let r = Scalar::random(rng);
|
2021-04-13 08:55:38 +10:00
|
|
|
let rG = r * G;
|
|
|
|
let rH = r * H;
|
|
|
|
|
|
|
|
let mut keccak = tiny_keccak::Keccak::v256();
|
|
|
|
keccak.update(G.compress().as_bytes());
|
|
|
|
keccak.update(xG.compress().as_bytes());
|
|
|
|
keccak.update(H.compress().as_bytes());
|
|
|
|
keccak.update(xH.compress().as_bytes());
|
|
|
|
keccak.update(rG.compress().as_bytes());
|
|
|
|
keccak.update(rH.compress().as_bytes());
|
|
|
|
|
|
|
|
let mut output = [0u8; 32];
|
|
|
|
keccak.finalize(&mut output);
|
|
|
|
|
|
|
|
let c = Scalar::from_bytes_mod_order(output);
|
|
|
|
|
|
|
|
let s = r + c * x;
|
|
|
|
|
|
|
|
Self { s, c }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn verify(
|
|
|
|
&self,
|
|
|
|
G: EdwardsPoint,
|
|
|
|
xG: EdwardsPoint,
|
|
|
|
H: EdwardsPoint,
|
|
|
|
xH: EdwardsPoint,
|
|
|
|
) -> Result<()> {
|
|
|
|
let s = self.s;
|
|
|
|
let c = self.c;
|
|
|
|
|
|
|
|
let rG = (s * G) + (-c * xG);
|
|
|
|
let rH = (s * H) + (-c * xH);
|
|
|
|
|
|
|
|
let mut keccak = tiny_keccak::Keccak::v256();
|
|
|
|
keccak.update(G.compress().as_bytes());
|
|
|
|
keccak.update(xG.compress().as_bytes());
|
|
|
|
keccak.update(H.compress().as_bytes());
|
|
|
|
keccak.update(xH.compress().as_bytes());
|
|
|
|
keccak.update(rG.compress().as_bytes());
|
|
|
|
keccak.update(rH.compress().as_bytes());
|
|
|
|
|
|
|
|
let mut output = [0u8; 32];
|
|
|
|
keccak.finalize(&mut output);
|
|
|
|
|
|
|
|
let c_prime = Scalar::from_bytes_mod_order(output);
|
|
|
|
|
|
|
|
if c != c_prime {
|
|
|
|
bail!("invalid DLEQ proof")
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq)]
|
|
|
|
struct Commitment([u8; 32]);
|
|
|
|
|
|
|
|
impl Commitment {
|
|
|
|
fn new(
|
|
|
|
fake_responses: [Scalar; RING_SIZE - 1],
|
|
|
|
I_a: EdwardsPoint,
|
|
|
|
I_hat_a: EdwardsPoint,
|
|
|
|
T_a: EdwardsPoint,
|
|
|
|
) -> Self {
|
|
|
|
let fake_responses = fake_responses
|
|
|
|
.iter()
|
|
|
|
.flat_map(|r| r.as_bytes().to_vec())
|
|
|
|
.collect::<Vec<u8>>();
|
|
|
|
|
|
|
|
let mut keccak = tiny_keccak::Keccak::v256();
|
|
|
|
keccak.update(&fake_responses);
|
|
|
|
keccak.update(I_a.compress().as_bytes());
|
|
|
|
keccak.update(I_hat_a.compress().as_bytes());
|
|
|
|
keccak.update(T_a.compress().as_bytes());
|
|
|
|
|
|
|
|
let mut output = [0u8; 32];
|
|
|
|
keccak.finalize(&mut output);
|
|
|
|
|
|
|
|
Self(output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Opening {
|
|
|
|
fake_responses: [Scalar; RING_SIZE - 1],
|
|
|
|
I_a: EdwardsPoint,
|
|
|
|
I_hat_a: EdwardsPoint,
|
|
|
|
T_a: EdwardsPoint,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Opening {
|
|
|
|
fn new(
|
|
|
|
fake_responses: [Scalar; RING_SIZE - 1],
|
|
|
|
I_a: EdwardsPoint,
|
|
|
|
I_hat_a: EdwardsPoint,
|
|
|
|
T_a: EdwardsPoint,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
fake_responses,
|
|
|
|
I_a,
|
|
|
|
I_hat_a,
|
|
|
|
T_a,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn open(
|
|
|
|
self,
|
|
|
|
commitment: Commitment,
|
|
|
|
) -> Result<(
|
|
|
|
[Scalar; RING_SIZE - 1],
|
|
|
|
EdwardsPoint,
|
|
|
|
EdwardsPoint,
|
|
|
|
EdwardsPoint,
|
|
|
|
)> {
|
|
|
|
let self_commitment =
|
|
|
|
Commitment::new(self.fake_responses, self.I_a, self.I_hat_a, self.T_a);
|
|
|
|
|
|
|
|
if self_commitment == commitment {
|
|
|
|
Ok((self.fake_responses, self.I_a, self.I_hat_a, self.T_a))
|
|
|
|
} else {
|
|
|
|
bail!("opening does not match commitment")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice Sends this to Bob
|
|
|
|
pub struct Message0 {
|
|
|
|
c_a: Commitment,
|
|
|
|
pi_a: DleqProof,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob sends this to ALice
|
|
|
|
pub struct Message1 {
|
|
|
|
I_b: EdwardsPoint,
|
|
|
|
T_b: EdwardsPoint,
|
|
|
|
I_hat_b: EdwardsPoint,
|
|
|
|
pi_b: DleqProof,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice sends this to Bob
|
|
|
|
pub struct Message2 {
|
|
|
|
d_a: Opening,
|
|
|
|
s_0_a: Scalar,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob sends this to Alice
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct Message3 {
|
|
|
|
s_0_b: Scalar,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2021-05-10 13:21:40 +10:00
|
|
|
use rand::rngs::OsRng;
|
2021-04-13 08:55:38 +10:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn sign_and_verify_success() {
|
|
|
|
let msg_to_sign = b"hello world, monero is amazing!!";
|
|
|
|
|
|
|
|
let s_prime_a = Scalar::random(&mut OsRng);
|
|
|
|
let s_b = Scalar::random(&mut OsRng);
|
|
|
|
|
|
|
|
let pk = (s_prime_a + s_b) * ED25519_BASEPOINT_POINT;
|
|
|
|
|
|
|
|
let (r_a, R_a, R_prime_a) = {
|
|
|
|
let r_a = Scalar::random(&mut OsRng);
|
|
|
|
let R_a = r_a * ED25519_BASEPOINT_POINT;
|
|
|
|
|
|
|
|
let pk_hashed_to_point = hash_point_to_point(pk);
|
|
|
|
|
|
|
|
let R_prime_a = r_a * pk_hashed_to_point;
|
|
|
|
|
|
|
|
(r_a, R_a, R_prime_a)
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut ring = [EdwardsPoint::default(); RING_SIZE];
|
|
|
|
ring[0] = pk;
|
|
|
|
|
|
|
|
ring[1..].fill_with(|| {
|
|
|
|
let x = Scalar::random(&mut OsRng);
|
|
|
|
|
|
|
|
x * ED25519_BASEPOINT_POINT
|
|
|
|
});
|
|
|
|
|
2021-05-10 13:21:40 +10:00
|
|
|
let alice = Alice0::new(ring, *msg_to_sign, R_a, R_prime_a, s_prime_a, &mut OsRng).unwrap();
|
|
|
|
let bob = Bob0::new(ring, *msg_to_sign, R_a, R_prime_a, s_b, &mut OsRng).unwrap();
|
2021-04-13 08:55:38 +10:00
|
|
|
|
2021-05-10 13:21:40 +10:00
|
|
|
let msg = alice.next_message(&mut OsRng);
|
2021-04-13 08:55:38 +10:00
|
|
|
let bob = bob.receive(msg);
|
|
|
|
|
2021-05-10 13:21:40 +10:00
|
|
|
let msg = bob.next_message(&mut OsRng);
|
2021-04-13 08:55:38 +10:00
|
|
|
let alice = alice.receive(msg).unwrap();
|
|
|
|
|
|
|
|
let msg = alice.next_message();
|
|
|
|
let bob = bob.receive(msg).unwrap();
|
|
|
|
|
|
|
|
let msg = bob.next_message();
|
|
|
|
let alice = alice.receive(msg);
|
|
|
|
|
|
|
|
let sig = alice.adaptor_sig.adapt(r_a);
|
|
|
|
|
|
|
|
assert!(sig.verify(ring, msg_to_sign).unwrap());
|
|
|
|
}
|
|
|
|
}
|