use crate::{
EmptyContext, HashError, HashableMessage, Integer, IntegerOperationError, OperationsTrait,
RecursiveHashTrait, VerifyDomainTrait,
elgamal::{EncryptionParameterDomainError, EncryptionParameters},
integer::ModExponentiateError,
number_theory::{QuadraticResidueError, QuadraticResidueTrait},
};
use std::iter::zip;
use thiserror::Error;
#[derive(Error, Debug)]
#[error(transparent)]
pub struct ExponentiationProofError(#[from] ExponentiationProofErrorRepr);
#[derive(Error, Debug)]
pub enum PhiExpError {
#[error("Error calculating g^x mod p")]
GExpXModP { source: ModExponentiateError },
}
#[derive(Error, Debug)]
#[allow(clippy::enum_variant_names)]
enum ExponentiationProofErrorRepr {
#[error("Error checking the elgamal parameters")]
CheckElgamal(Vec<EncryptionParameterDomainError>),
#[error("The list {0} must have the same length as the list {1}")]
CheckListSameSize(String, String),
#[error("{name} is not qudartic residue at position {pos}")]
NotQuadraticResidueList {
name: &'static str,
pos: usize,
source: QuadraticResidueError,
},
#[error("Error Compute Phi Exponentiation")]
PhiExpError {
#[from]
source: PhiExpError,
},
#[error("Error hashing e'")]
EPrimeHash { source: HashError },
#[error("Error in y^e mod p claculting cs'")]
YExpEModP { source: ModExponentiateError },
#[error("Error in v^(-1) mod p claculting cs'")]
InverseVModP { source: IntegerOperationError },
}
fn compute_phi_exponentiation(
ep: &EncryptionParameters,
x: &Integer,
gs: &[&Integer],
) -> Result<Vec<Integer>, PhiExpError> {
gs.iter()
.map(|g| {
g.mod_exponentiate(x, ep.p())
.map_err(|e| PhiExpError::GExpXModP { source: e })
})
.collect::<Result<Vec<_>, _>>()
}
pub fn verify_exponentiation(
ep: &EncryptionParameters,
gs: &[&Integer],
ys: &[&Integer],
(e, z): (&Integer, &Integer),
i_aux: &Vec<String>,
) -> Result<bool, ExponentiationProofError> {
verify_exponentiation_impl(ep, gs, ys, (e, z), i_aux).map_err(ExponentiationProofError::from)
}
fn verify_exponentiation_impl(
ep: &EncryptionParameters,
gs: &[&Integer],
ys: &[&Integer],
(e, z): (&Integer, &Integer),
i_aux: &Vec<String>,
) -> Result<bool, ExponentiationProofErrorRepr> {
if cfg!(feature = "checks") {
let domain_errs = ep.verifiy_domain(&EmptyContext::default());
if !domain_errs.is_empty() {
return Err(ExponentiationProofErrorRepr::CheckElgamal(domain_errs));
}
if gs.len() != ys.len() {
return Err(ExponentiationProofErrorRepr::CheckListSameSize(
"gs".to_string(),
"ys".to_string(),
));
}
for (pos, g) in gs.iter().enumerate() {
g.result_is_quadratic_residue_unchecked(ep.p())
.map_err(|e| ExponentiationProofErrorRepr::NotQuadraticResidueList {
pos,
name: "g",
source: e,
})?;
}
for (pos, y) in ys.iter().enumerate() {
y.result_is_quadratic_residue_unchecked(ep.p())
.map_err(|e| ExponentiationProofErrorRepr::NotQuadraticResidueList {
pos,
name: "y",
source: e,
})?;
}
}
let xs = compute_phi_exponentiation(ep, z, gs)?;
let f_list = vec![
HashableMessage::from(ep.p()),
HashableMessage::from(ep.q()),
HashableMessage::from(
gs.iter()
.map(|g| HashableMessage::from(*g))
.collect::<Vec<_>>(),
),
];
let f = HashableMessage::from(&f_list);
let c_prime_s = zip(&xs, ys)
.map(|(x, y)| {
y.mod_exponentiate(e, ep.p())
.map_err(|e| ExponentiationProofErrorRepr::YExpEModP { source: e })
.and_then(|v| {
v.mod_inverse(ep.p())
.map_err(|e| ExponentiationProofErrorRepr::InverseVModP { source: e })
})
.map(|v| x.mod_multiply(&v, ep.p()))
})
.collect::<Result<Vec<_>, _>>()?;
let mut h_aux_l: Vec<HashableMessage> = vec![];
h_aux_l.push(HashableMessage::from("ExponentiationProof"));
if !i_aux.is_empty() {
h_aux_l.push(HashableMessage::from(i_aux.as_slice()));
}
let h_aux = HashableMessage::from(&h_aux_l);
let l_final: Vec<HashableMessage> = vec![
f,
HashableMessage::from(
ys.iter()
.map(|y| HashableMessage::from(*y))
.collect::<Vec<_>>(),
),
HashableMessage::from(c_prime_s.as_slice()),
h_aux,
];
let e_prime = HashableMessage::from(&l_final)
.recursive_hash()
.map_err(|e| ExponentiationProofErrorRepr::EPrimeHash { source: e })?
.into_integer();
Ok(&e_prime == e)
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
test_json_data::{
get_test_cases_from_json_file, json_array_64_value_to_array_integer,
json_array_value_to_array_string, json_value_to_encryption_parameters,
},
zero_knowledge_proofs::test::{Proof, proof_from_json_values},
};
use serde_json::Value;
struct Input {
bases: Vec<Integer>,
statement: Vec<Integer>,
proof: Proof,
additional_information: Vec<String>,
}
fn get_input(input: &Value) -> Input {
Input {
bases: json_array_64_value_to_array_integer(&input["bases"]),
statement: json_array_64_value_to_array_integer(&input["statement"]),
proof: proof_from_json_values(&input["proof"]),
additional_information: json_array_value_to_array_string(
&input["additional_information"],
),
}
}
#[test]
fn test_verify() {
for tc in get_test_cases_from_json_file("zeroknowledgeproofs", "verify-exponentiation.json")
{
let ep = json_value_to_encryption_parameters(&tc["context"]);
let input = get_input(&tc["input"]);
let res = verify_exponentiation(
&ep,
input.bases.iter().collect::<Vec<_>>().as_slice(),
input.statement.iter().collect::<Vec<_>>().as_slice(),
(&input.proof.e, &input.proof.z),
&input.additional_information,
);
assert!(res.is_ok(), "{}", &tc["description"]);
assert!(res.unwrap(), "{}", &tc["description"])
}
}
}