use merlin::Transcript;
use rand_core::{CryptoRng, RngCore};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use subtle::ConstantTimeEq;
#[cfg(feature = "serde")]
use crate::serde::ScalarHelper;
use crate::{
alloc::{vec, Vec},
group::Group,
proofs::{TranscriptForGroup, VerificationError},
PublicKey, SecretKey,
};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = ""))]
pub struct LogEqualityProof<G: Group> {
#[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
challenge: G::Scalar,
#[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
response: G::Scalar,
}
impl<G: Group> LogEqualityProof<G> {
pub fn new<R: CryptoRng + RngCore>(
log_base: &PublicKey<G>,
secret: &SecretKey<G>,
powers: (G::Element, G::Element),
transcript: &mut Transcript,
rng: &mut R,
) -> Self {
transcript.start_proof(b"log_eq");
transcript.append_element_bytes(b"K", log_base.as_bytes());
transcript.append_element::<G>(b"[r]G", &powers.0);
transcript.append_element::<G>(b"[r]K", &powers.1);
let random_scalar = SecretKey::<G>::generate(rng);
transcript.append_element::<G>(b"[x]G", &G::mul_generator(random_scalar.expose_scalar()));
transcript.append_element::<G>(
b"[x]K",
&(log_base.as_element() * random_scalar.expose_scalar()),
);
let challenge = transcript.challenge_scalar::<G>(b"c");
let response = challenge * secret.expose_scalar() + random_scalar.expose_scalar();
Self {
challenge,
response,
}
}
pub fn verify(
&self,
log_base: &PublicKey<G>,
powers: (G::Element, G::Element),
transcript: &mut Transcript,
) -> Result<(), VerificationError> {
let commitments = (
G::vartime_double_mul_generator(&-self.challenge, powers.0, &self.response),
G::vartime_multi_mul(
&[-self.challenge, self.response],
[powers.1, log_base.as_element()],
),
);
transcript.start_proof(b"log_eq");
transcript.append_element_bytes(b"K", log_base.as_bytes());
transcript.append_element::<G>(b"[r]G", &powers.0);
transcript.append_element::<G>(b"[r]K", &powers.1);
transcript.append_element::<G>(b"[x]G", &commitments.0);
transcript.append_element::<G>(b"[x]K", &commitments.1);
let expected_challenge = transcript.challenge_scalar::<G>(b"c");
if expected_challenge.ct_eq(&self.challenge).into() {
Ok(())
} else {
Err(VerificationError::ChallengeMismatch)
}
}
pub fn to_bytes(self) -> Vec<u8> {
let mut bytes = vec![0_u8; 2 * G::SCALAR_SIZE];
G::serialize_scalar(&self.challenge, &mut bytes[..G::SCALAR_SIZE]);
G::serialize_scalar(&self.response, &mut bytes[G::SCALAR_SIZE..]);
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != 2 * G::SCALAR_SIZE {
return None;
}
let challenge = G::deserialize_scalar(&bytes[..G::SCALAR_SIZE])?;
let response = G::deserialize_scalar(&bytes[G::SCALAR_SIZE..])?;
Some(Self {
challenge,
response,
})
}
}
#[cfg(test)]
mod tests {
use rand::thread_rng;
use super::*;
use crate::group::Ristretto;
type Keypair = crate::Keypair<Ristretto>;
#[test]
fn log_equality_basics() {
let mut rng = thread_rng();
let log_base = Keypair::generate(&mut rng).public().clone();
for _ in 0..100 {
let (generator_val, secret) = Keypair::generate(&mut rng).into_tuple();
let key_val = log_base.as_element() * secret.expose_scalar();
let proof = LogEqualityProof::new(
&log_base,
&secret,
(generator_val.as_element(), key_val),
&mut Transcript::new(b"testing_log_equality"),
&mut rng,
);
proof
.verify(
&log_base,
(generator_val.as_element(), key_val),
&mut Transcript::new(b"testing_log_equality"),
)
.unwrap();
}
}
}