use super::{secret_sharing::Threshold, types::*};
use crate::{
common::{
to_bytes,
types::{KeyIndex, TransactionTime},
ParseResult,
},
curve_arithmetic::{multiexp, Curve, Field, Pairing, PrimeField, Value},
elgamal::*,
pedersen_commitment::Commitment,
};
use anyhow::bail;
use ed25519_dalek::Verifier;
use either::Either;
use rand::*;
use sha2::{Digest, Sha256};
use std::collections::{btree_map::BTreeMap, BTreeSet};
pub fn commitment_to_share<C: Curve>(
share_number: &C::Scalar,
coeff_commitments: &[Commitment<C>],
) -> Commitment<C> {
let n = coeff_commitments.len();
if n == 0 {
return Commitment(C::zero_point());
}
let mut exponents = Vec::with_capacity(n);
let mut exponent: C::Scalar = C::Scalar::one();
for _ in 0..n {
exponents.push(exponent);
exponent.mul_assign(share_number);
}
Commitment(multiexp(coeff_commitments, &exponents))
}
pub fn evaluate_poly<F: Field, R: AsRef<F>>(coeffs: &[R], point: &F) -> F {
let mut eval: F = F::zero();
for rand in coeffs.iter().rev() {
eval.mul_assign(point);
eval.add_assign(rand.as_ref());
}
eval
}
#[allow(clippy::type_complexity)]
pub fn encrypt_prf_share<C: Curve, R: Rng>(
context: &GlobalContext<C>,
pk: &PublicKey<C>,
share: &Value<C>,
csprng: &mut R,
) -> ([Cipher<C>; 8], [Randomness<C>; 8], [C::Scalar; 8]) {
let h = context.encryption_in_exponent_generator();
let chunks = value_to_chunks::<C>(share, CHUNK_SIZE);
let mut ciphers = pk.encrypt_exponent_vec_given_generator(&chunks, h, csprng);
let (encryption_8, randomness_8) = ciphers.pop().unwrap();
let (encryption_7, randomness_7) = ciphers.pop().unwrap();
let (encryption_6, randomness_6) = ciphers.pop().unwrap();
let (encryption_5, randomness_5) = ciphers.pop().unwrap();
let (encryption_4, randomness_4) = ciphers.pop().unwrap();
let (encryption_3, randomness_3) = ciphers.pop().unwrap();
let (encryption_2, randomness_2) = ciphers.pop().unwrap();
let (encryption_1, randomness_1) = ciphers.pop().unwrap();
let enc = [
encryption_1,
encryption_2,
encryption_3,
encryption_4,
encryption_5,
encryption_6,
encryption_7,
encryption_8,
];
let rand = [
randomness_1,
randomness_2,
randomness_3,
randomness_4,
randomness_5,
randomness_6,
randomness_7,
randomness_8,
];
let chunks = [
*chunks[0], *chunks[1], *chunks[2], *chunks[3], *chunks[4], *chunks[5], *chunks[6],
*chunks[7],
];
(enc, rand, chunks)
}
pub fn encode_tags<'a, F: PrimeField, I: std::iter::IntoIterator<Item = &'a AttributeTag>>(
i: I,
) -> ParseResult<F> {
let max_tag = F::CAPACITY - 1;
let f = F::zero();
let mut limbs = f.into_repr().to_vec(); for &AttributeTag(tag) in i.into_iter() {
let idx = tag / 64;
let place = tag % 64;
if u32::from(tag) > max_tag || usize::from(idx) > limbs.len() {
bail!("Tag out of range: {}", tag)
}
let mask: u64 = 1 << place;
if limbs[usize::from(idx)] & mask != 0 {
bail!("Duplicate tag {}", tag)
} else {
limbs[usize::from(idx)] |= mask;
}
}
Ok(F::from_repr(limbs.as_slice())?)
}
pub fn encode_ars<F: PrimeField>(ars: &BTreeSet<ArIdentity>) -> Option<Vec<F>> {
let max_bit: usize = (F::CAPACITY - 1) as usize;
let ars = ars.iter().copied().collect::<Vec<_>>();
let num_ars_per_element = max_bit / 32;
let chunks = ars.chunks(num_ars_per_element);
let num_scalars = chunks.len();
let mut scalars = Vec::with_capacity(num_scalars);
let mut two = F::one();
two.add_assign(&F::one());
let two = two;
for chunk in chunks {
let mut f = F::zero().into_repr();
for (i, &ar_id) in chunk.iter().enumerate() {
let ar_id: u32 = ar_id.into();
let x: u64 = if i % 2 == 0 {
u64::from(ar_id)
} else {
u64::from(ar_id) << 32
};
f[i / 2] |= x;
}
let mut scalar = F::from_repr(f.as_slice()).ok()?;
scalar.mul_assign(&two);
scalars.push(scalar)
}
if num_scalars == 0 {
scalars.push(F::zero())
}
scalars.last_mut()?.add_assign(&F::one());
Some(scalars)
}
pub fn encode_public_credential_values<F: PrimeField>(
created_at: YearMonth,
valid_to: YearMonth,
threshold: Threshold,
) -> ParseResult<F> {
let mut f = F::zero().into_repr();
let ca: u32 = created_at.into();
let vt: u32 = valid_to.into();
let s = u64::from(vt) << 32 | u64::from(ca);
f[0] = s; let threshold: u8 = threshold.into();
f[1] = u64::from(threshold);
Ok(F::from_repr(f.as_slice())?)
}
pub fn verify_account_ownership_proof(
keys: &BTreeMap<KeyIndex, VerifyKey>,
threshold: SignatureThreshold,
proof_acc_sk: &AccountOwnershipProof,
msg: &[u8],
) -> bool {
let Ok(num_proofs) = proof_acc_sk.num_proofs() else {
return false;
};
if num_proofs < threshold
|| keys.len() > 255
|| keys.is_empty()
|| usize::from(u8::from(num_proofs)) != keys.len()
{
return false;
}
let mut processed = BTreeSet::new();
for (idx, key) in keys.iter() {
if !processed.insert(key) {
return false;
}
if let Some(sig) = proof_acc_sk.sigs.get(idx) {
let VerifyKey::Ed25519VerifyKey(ref key) = key;
if key.verify(msg, sig).is_err() {
return false;
}
} else {
return false;
}
}
true
}
pub fn credential_hash_to_sign<
P: Pairing,
C: Curve<Scalar = P::ScalarField>,
AttributeType: Attribute<C::Scalar>,
>(
values: &CredentialDeploymentValues<C, AttributeType>,
proofs: &IdOwnershipProofs<P, C>,
new_or_existing: &Either<TransactionTime, AccountAddress>,
) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(to_bytes(&values));
hasher.update(to_bytes(&proofs));
hasher.update(to_bytes(&new_or_existing));
let to_sign = &hasher.finalize();
to_sign.to_vec()
}
pub fn merge_iter<'a, K: Ord + 'a, V1: 'a, V2: 'a, I1, I2, F>(i1: I1, i2: I2, mut f: F)
where
I1: std::iter::IntoIterator<Item = (&'a K, &'a V1)>,
I2: std::iter::IntoIterator<Item = (&'a K, &'a V2)>,
F: FnMut(Either<&'a V1, &'a V2>),
{
let mut iter_1 = i1.into_iter().peekable();
let mut iter_2 = i2.into_iter().peekable();
while let (Some(&(tag_1, v_1)), Some(&(tag_2, v_2))) = (iter_1.peek(), iter_2.peek()) {
if tag_1 < tag_2 {
f(Either::Left(v_1));
let _ = iter_1.next().is_none();
} else {
f(Either::Right(v_2));
let _ = iter_2.next();
}
}
for (_, v) in iter_1 {
f(Either::Left(v))
}
for (_, v) in iter_2 {
f(Either::Right(v))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{common::to_bytes, curve_arithmetic::arkworks_instances::ArkField};
use ark_bls12_381::Fr;
use rand::{thread_rng, Rng};
use std::collections::BTreeMap;
#[test]
pub fn test_last_bit() {
let ars = (1..10).map(ArIdentity::new).collect::<BTreeSet<_>>();
let encoded = encode_ars::<ArkField<Fr>>(&ars).expect("Encodign should succeed.");
assert_eq!(encoded.len(), 2, "Encoded ARs should fit into two scalars.");
let s1 = to_bytes(&encoded[0]);
let s2 = to_bytes(&encoded[1]);
assert_eq!(s1[31] & 1u8, 0u8, "Last bit of the first scalar must be 0.");
assert_eq!(
s2[31] & 1u8,
1u8,
"Last bit of the second scalar must be 1."
);
}
#[test]
pub fn test_encoding_injective() {
let mut csprng = thread_rng();
let mut seen = BTreeMap::new();
for n in 1..50 {
let mut xs = vec![ArIdentity::new(1); n];
for x in xs.iter_mut() {
*x = ArIdentity::new(csprng.gen_range(1..100));
}
let set = xs.iter().copied().collect::<BTreeSet<_>>();
let encoded = encode_ars::<ArkField<Fr>>(&set).expect("Encoding should succeed.");
if let Some(set_ex) = seen.insert(encoded.clone(), set.clone()) {
assert_eq!(set, set_ex);
}
}
}
}