indy-crypto 0.1.6-dev-25

This is the shared crypto libirary for Hyperledger Indy components.
Documentation
use bn::BigNumber;
use cl::*;
use cl::constants::{LARGE_E_START, ITERATION};
use cl::helpers::*;
use errors::IndyCryptoError;

use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;

/// Party that wants to check that prover has some credentials provided by issuer.
pub struct Verifier {}

impl Verifier {
    /// Creates and returns sub proof request entity builder.
    /// Part of proof request related to a particular schema-key.
    ///
    /// The purpose of sub proof request builder is building of sub proof request entity that
    /// represents requested attributes and predicates.
    ///
    /// # Example
    /// ```
    /// use indy_crypto::cl::verifier::Verifier;
    /// let mut sub_proof_request_builder = Verifier::new_sub_proof_request_builder().unwrap();
    /// sub_proof_request_builder.add_revealed_attr("name").unwrap();
    /// sub_proof_request_builder.add_predicate("age", "GE", 18).unwrap();
    /// let _sub_proof_request = sub_proof_request_builder.finalize().unwrap();
    /// ```
    pub fn new_sub_proof_request_builder() -> Result<SubProofRequestBuilder, IndyCryptoError> {
        let res = SubProofRequestBuilder::new()?;
        Ok(res)
    }

    /// Creates and returns proof verifier.
    ///
    /// The purpose of `proof verifier` is check proof provided by Prover.
    ///
    /// # Example
    /// ```
    /// use indy_crypto::cl::verifier::Verifier;
    /// let _proof_verifier = Verifier::new_proof_verifier().unwrap();
    /// ```
    pub fn new_proof_verifier() -> Result<ProofVerifier, IndyCryptoError> {
        Ok(ProofVerifier {
            claims: HashMap::new(),
        })
    }
}


#[derive(Debug)]
pub struct ProofVerifier {
    claims: HashMap<String, VerifyClaim>,
}

impl ProofVerifier {
    /// Add sub proof request to proof verifier.
    ///
    /// # Arguments
    /// * `proof_verifier` - Proof verifier.
    /// * `key_id` - unique claim identifier.
    /// * `claim_schema` - Claim schema.
    /// * `issuer_pub_key` - Issuer public key.
    /// * `rev_reg_pub` - Public revocation registry.
    /// * `sub_proof_request` - Requested attributes and predicates instance pointer.
    pub fn add_sub_proof_request(&mut self,
                                 key_id: &str,
                                 sub_proof_request: &SubProofRequest,
                                 claim_schema: &ClaimSchema,
                                 issuer_pub_key: &IssuerPublicKey,
                                 rev_reg_pub: Option<&RevocationRegistryPublic>) -> Result<(), IndyCryptoError> {
        ProofVerifier::_check_add_sub_proof_request_params_consistency(sub_proof_request, claim_schema)?;

        self.claims.insert(key_id.to_string(), VerifyClaim {
            pub_key: issuer_pub_key.clone()?,
            r_reg: rev_reg_pub.map(Clone::clone),
            sub_proof_request: sub_proof_request.clone(),
            claim_schema: claim_schema.clone(),
        });
        Ok(())
    }

    fn _check_add_sub_proof_request_params_consistency(sub_proof_request: &SubProofRequest, claim_schema: &ClaimSchema) -> Result<(), IndyCryptoError> {
        trace!("ProofVerifier::_check_add_sub_proof_request_params_consistency: >>> sub_proof_request: {:?}, claim_schema: {:?}", sub_proof_request, claim_schema);

        if sub_proof_request.revealed_attrs.difference(&claim_schema.attrs).count() != 0 {
            return Err(IndyCryptoError::InvalidStructure(format!("Claim doesn't contain requested attribute")));
        }

        let predicates_attrs =
            sub_proof_request.predicates.iter()
                .map(|predicate| predicate.attr_name.clone())
                .collect::<HashSet<String>>();

        if predicates_attrs.difference(&claim_schema.attrs).count() != 0 {
            return Err(IndyCryptoError::InvalidStructure(format!("Claim doesn't contain attribute requested in predicate")));
        }

        trace!("ProofVerifier::_check_add_sub_proof_request_params_consistency: <<<");

        Ok(())
    }

    /// Verifies proof.
    ///
    /// # Arguments
    /// * `proof_verifier` - Proof verifier.
    /// * `proof` - Proof generated by Prover.
    /// * `nonce` - nonce.
    pub fn verify(self,
                  proof: &Proof,
                  nonce: &Nonce) -> Result<bool, IndyCryptoError> {
        trace!("ProofVerifier::verify: >>> proof: {:?}, nonce: {:?}", proof, nonce);

        ProofVerifier::_check_verify_params_consistency(&self.claims, proof)?;

        let mut tau_list: Vec<Vec<u8>> = Vec::new();

        for (issuer_key_id, proof_item) in &proof.proofs {
            let claim = self.claims.get(issuer_key_id)
                .ok_or(IndyCryptoError::AnoncredsProofRejected(format!("Schema is not found")))?;

            if let (Some(non_revocation_proof), Some(pkr), Some(revoc_reg)) = (proof_item.non_revoc_proof.as_ref(),
                                                                               claim.pub_key.r_key.as_ref(),
                                                                               claim.r_reg.as_ref()) {
                tau_list.extend_from_slice(
                    &ProofVerifier::_verify_non_revocation_proof(
                        &pkr,
                        &revoc_reg.acc,
                        &revoc_reg.key,
                        &proof.aggregated_proof.c_hash,
                        &non_revocation_proof)?.as_slice()?
                );
            };

            tau_list.append_vec(
                &ProofVerifier::_verify_primary_proof(&claim.pub_key.p_key,
                                                      &proof.aggregated_proof.c_hash,
                                                      &proof_item.primary_proof,
                                                      &claim.claim_schema,
                                                      &claim.sub_proof_request)?
            )?;
        }

        let mut values: Vec<Vec<u8>> = Vec::new();

        values.extend_from_slice(&tau_list);
        values.extend_from_slice(&proof.aggregated_proof.c_list);
        values.push(nonce.to_bytes()?);

        let c_hver = get_hash_as_int(&mut values)?;

        info!(target: "anoncreds_service", "Verifier verify proof -> done");

        let valid = c_hver == proof.aggregated_proof.c_hash;

        trace!("ProofVerifier::verify: <<< valid: {:?}", valid);

        Ok(valid)
    }

    fn _check_verify_params_consistency(claims: &HashMap<String, VerifyClaim>, proof: &Proof) -> Result<(), IndyCryptoError> {
        trace!("ProofVerifier::_check_verify_params_consistency: >>> claims: {:?}, proof: {:?}", claims, proof);

        for (key_id, claim) in claims {
            let proof_for_claim = proof.proofs.get(key_id.as_str()).
                ok_or(IndyCryptoError::AnoncredsProofRejected(format!("Proof not found")))?;

            let proof_revealed_attrs = HashSet::from_iter(proof_for_claim.primary_proof.eq_proof.revealed_attrs.keys().cloned());

            if proof_revealed_attrs != claim.sub_proof_request.revealed_attrs {
                return Err(IndyCryptoError::AnoncredsProofRejected(format!("Proof revealed attributes not correspond to requested attributes")));
            }

            let proof_predicates =
                proof_for_claim.primary_proof.ge_proofs.iter()
                    .map(|ge_proof| ge_proof.predicate.clone())
                    .collect::<HashSet<Predicate>>();

            if proof_predicates != claim.sub_proof_request.predicates {
                return Err(IndyCryptoError::AnoncredsProofRejected(format!("Proof predicates not correspond to requested predicates")));
            }
        }

        trace!("ProofVerifier::_check_verify_params_consistency: <<<");

        Ok(())
    }

    fn _verify_primary_proof(issuer_pub_key: &IssuerPrimaryPublicKey, c_hash: &BigNumber,
                             primary_proof: &PrimaryProof, claim_schema: &ClaimSchema, sub_proof_request: &SubProofRequest) -> Result<Vec<BigNumber>, IndyCryptoError> {
        trace!("ProofVerifier::_verify_primary_proof: >>> issuer_pub_key: {:?}, c_hash: {:?}, primary_proof: {:?}, sub_proof_request: {:?}",
               issuer_pub_key, c_hash, primary_proof, sub_proof_request);

        let mut t_hat: Vec<BigNumber> = ProofVerifier::_verify_equality(issuer_pub_key, &primary_proof.eq_proof, c_hash, claim_schema, sub_proof_request)?;

        for ge_proof in primary_proof.ge_proofs.iter() {
            t_hat.append(&mut ProofVerifier::_verify_ge_predicate(issuer_pub_key, ge_proof, c_hash)?)
        }

        trace!("ProofVerifier::_verify_primary_proof: <<< t_hat: {:?}", t_hat);

        Ok(t_hat)
    }

    fn _verify_equality(issuer_pub_key: &IssuerPrimaryPublicKey, proof: &PrimaryEqualProof, c_hash: &BigNumber,
                        claim_schema: &ClaimSchema, sub_proof_request: &SubProofRequest) -> Result<Vec<BigNumber>, IndyCryptoError> {
        trace!("ProofVerifier::_verify_equality: >>> issuer_pub_key: {:?}, proof: {:?}, c_hash: {:?}, claim_schema: {:?}, sub_proof_request: {:?}",
               issuer_pub_key, proof, c_hash, claim_schema, sub_proof_request);

        let unrevealed_attrs: HashSet<String> =
            claim_schema.attrs
                .difference(&sub_proof_request.revealed_attrs)
                .map(|attr| attr.clone())
                .collect::<HashSet<String>>();

        let t1: BigNumber = calc_teq(&issuer_pub_key, &proof.a_prime, &proof.e, &proof.v, &proof.m, &proof.m1, &proof.m2, &unrevealed_attrs)?;

        let mut ctx = BigNumber::new_context()?;

        let degree: BigNumber =
            BigNumber::from_dec("2")?
                .exp(
                    &BigNumber::from_dec(&LARGE_E_START.to_string())?,
                    Some(&mut ctx)
                )?;

        let mut rar = proof.a_prime.mod_exp(&degree, &issuer_pub_key.n, Some(&mut ctx))?;

        for (attr, encoded_value) in &proof.revealed_attrs {
            let cur_r = issuer_pub_key.r.get(attr)
                .ok_or(IndyCryptoError::AnoncredsProofRejected(format!("Value by key '{}' not found in pk.r", attr)))?;

            rar = cur_r
                .mod_exp(encoded_value, &issuer_pub_key.n, Some(&mut ctx))?
                .mod_mul(&rar, &issuer_pub_key.n, Some(&mut ctx))?;
        }

        let t2: BigNumber = issuer_pub_key.z
            .mod_div(&rar, &issuer_pub_key.n)?
            .inverse(&issuer_pub_key.n, Some(&mut ctx))?
            .mod_exp(&c_hash, &issuer_pub_key.n, Some(&mut ctx))?;

        let t: BigNumber = t1.mod_mul(&t2, &issuer_pub_key.n, Some(&mut ctx))?;

        trace!("ProofVerifier::_verify_equality: <<< t: {:?}", t);

        Ok(vec![t])
    }

    fn _verify_ge_predicate(issuer_pub_key: &IssuerPrimaryPublicKey, proof: &PrimaryPredicateGEProof, c_hash: &BigNumber) -> Result<Vec<BigNumber>, IndyCryptoError> {
        trace!("ProofVerifier::_verify_ge_predicate: >>> issuer_pub_key: {:?}, proof: {:?}, c_hash: {:?}", issuer_pub_key, proof, c_hash);

        let mut ctx = BigNumber::new_context()?;
        let mut tau_list = calc_tge(&issuer_pub_key, &proof.u, &proof.r, &proof.mj,
                                    &proof.alpha, &proof.t)?;

        for i in 0..ITERATION {
            let cur_t = proof.t.get(&i.to_string())
                .ok_or(IndyCryptoError::AnoncredsProofRejected(format!("Value by key '{}' not found in proof.t", i)))?;

            tau_list[i] = cur_t
                .mod_exp(&c_hash, &issuer_pub_key.n, Some(&mut ctx))?
                .inverse(&issuer_pub_key.n, Some(&mut ctx))?
                .mod_mul(&tau_list[i], &issuer_pub_key.n, Some(&mut ctx))?;
        }

        let delta = proof.t.get("DELTA")
            .ok_or(IndyCryptoError::AnoncredsProofRejected(format!("Value by key '{}' not found in proof.t", "DELTA")))?;

        tau_list[ITERATION] = issuer_pub_key.z
            .mod_exp(
                &BigNumber::from_dec(&proof.predicate.value.to_string())?,
                &issuer_pub_key.n, Some(&mut ctx))?
            .mul(&delta, Some(&mut ctx))?
            .mod_exp(&c_hash, &issuer_pub_key.n, Some(&mut ctx))?
            .inverse(&issuer_pub_key.n, Some(&mut ctx))?
            .mod_mul(&tau_list[ITERATION], &issuer_pub_key.n, Some(&mut ctx))?;

        tau_list[ITERATION + 1] = delta
            .mod_exp(&c_hash, &issuer_pub_key.n, Some(&mut ctx))?
            .inverse(&issuer_pub_key.n, Some(&mut ctx))?
            .mod_mul(&tau_list[ITERATION + 1], &issuer_pub_key.n, Some(&mut ctx))?;

        trace!("ProofVerifier::_verify_ge_predicate: <<< tau_list: {:?},", tau_list);

        Ok(tau_list)
    }

    pub fn _verify_non_revocation_proof(issuer_r_pub_key: &IssuerRevocationPublicKey,
                                        accum: &RevocationAccumulator,
                                        accum_pk: &RevocationAccumulatorPublicKey,
                                        c_hash: &BigNumber, proof: &NonRevocProof)
                                        -> Result<NonRevocProofTauList, IndyCryptoError> {
        trace!("ProofVerifier::_verify_non_revocation_proof: >>> issuer_r_pub_key: {:?}, accum: {:?}, accum_pk: {:?}, c_hash: {:?}",
               issuer_r_pub_key, accum, accum_pk, c_hash);

        let ch_num_z = bignum_to_group_element(&c_hash)?;

        let t_hat_expected_values = create_tau_list_expected_values(issuer_r_pub_key, accum, accum_pk, &proof.c_list)?;
        let t_hat_calc_values = create_tau_list_values(&issuer_r_pub_key, &accum, &proof.x_list, &proof.c_list)?;


        let non_revoc_proof_tau_list = Ok(NonRevocProofTauList {
            t1: t_hat_expected_values.t1.mul(&ch_num_z)?.add(&t_hat_calc_values.t1)?,
            t2: t_hat_expected_values.t2.mul(&ch_num_z)?.add(&t_hat_calc_values.t2)?,
            t3: t_hat_expected_values.t3.pow(&ch_num_z)?.mul(&t_hat_calc_values.t3)?,
            t4: t_hat_expected_values.t4.pow(&ch_num_z)?.mul(&t_hat_calc_values.t4)?,
            t5: t_hat_expected_values.t5.mul(&ch_num_z)?.add(&t_hat_calc_values.t5)?,
            t6: t_hat_expected_values.t6.mul(&ch_num_z)?.add(&t_hat_calc_values.t6)?,
            t7: t_hat_expected_values.t7.pow(&ch_num_z)?.mul(&t_hat_calc_values.t7)?,
            t8: t_hat_expected_values.t8.pow(&ch_num_z)?.mul(&t_hat_calc_values.t8)?
        });

        trace!("ProofVerifier::_verify_non_revocation_proof: <<< non_revoc_proof_tau_list: {:?}", non_revoc_proof_tau_list);

        non_revoc_proof_tau_list
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use cl::prover;
    use cl::issuer;
    use cl::helpers::MockHelper;
    use cl::prover::mocks::*;

    #[test]
    fn sub_proof_request_builder_works() {
        let mut sub_proof_request_builder = Verifier::new_sub_proof_request_builder().unwrap();
        sub_proof_request_builder.add_revealed_attr("name").unwrap();
        sub_proof_request_builder.add_predicate("age", "GE", 18).unwrap();
        let sub_proof_request = sub_proof_request_builder.finalize().unwrap();

        assert!(sub_proof_request.revealed_attrs.contains("name"));
        assert!(sub_proof_request.predicates.contains(&predicate()));
    }

    #[test]
    fn verify_equlity_works() {
        MockHelper::inject();

        let proof = prover::mocks::eq_proof();
        let pk = issuer::mocks::issuer_primary_public_key();
        let c_h = prover::mocks::aggregated_proof().c_hash;
        let claim_schema = issuer::mocks::claim_schema();

        let mut sub_proof_request_builder = SubProofRequestBuilder::new().unwrap();
        sub_proof_request_builder.add_revealed_attr("name").unwrap();
        let sub_proof_request = sub_proof_request_builder.finalize().unwrap();

        let res: Vec<BigNumber> = ProofVerifier::_verify_equality(&pk,
                                                                  &proof,
                                                                  &c_h,
                                                                  &claim_schema,
                                                                  &sub_proof_request).unwrap();

        assert_eq!("7482119769466399985453964851245160858083213139582181384392546015391266465526851030277921408409258064077698298124641650660813004229404233357\
        6076617707349015173216066298469796629848379475742662270634373967221781036145898302079498084911189254205359080720863996359684543833466536415829603044441\
        7146442152319610488185863374642452252262537639796626459270165235687616182451932827771166420110852842258328296215336523679907850550512281026511049364855\
        8795042267540903229826875151183848757230364123606295208525165169665544252416050410779833876311911272856561624676976563557744602895129160488849098294437\
        5208275466186869182747994", res[0].to_dec().unwrap());
    }

    #[test]
    fn _verify_ge_predicate_works() {
        MockHelper::inject();

        let proof = prover::mocks::ge_proof();
        let c_h = prover::mocks::aggregated_proof().c_hash;
        let pk = issuer::mocks::issuer_primary_public_key();

        let res = ProofVerifier::_verify_ge_predicate(&pk, &proof, &c_h);

        assert!(res.is_ok());
        let res_data = res.unwrap();

        assert_eq!("376910366785000888640907068892773445290856982028553183426096623244555347257778101747799882436148347403830024840429617795354387295127009257238001847697728551\
        176536093973115809374401318141110098900739722767845936624708107236876761676800627172399726564255634308382367493256717024633900449205720018609556512423317410372608366135\
        066533236820567062263706984223659166559990463804265095415860347492428279789699722395246760391390256022639741018088870083311929296796590769109958556654779529301996928547\
        78469439162325030246066895851569630345729938981633504514117558420480144828304421708923356898912192737390539479512879411139535", res_data[0].to_dec().unwrap());

        assert_eq!("376910366785000888640907068892773445290856982028553183426096623244555347257778101747799882436148347403830024840429617795354387295127009257238001847697728551\
        176536093973115809374401318141110098900739722767845936624708107236876761676800627172399726564255634308382367493256717024633900449205720018609556512423317410372608366135\
        066533236820567062263706984223659166559990463804265095415860347492428279789699722395246760391390256022639741018088870083311929296796590769109958556654779529301996928547\
        78469439162325030246066895851569630345729938981633504514117558420480144828304421708923356898912192737390539479512879411139535", res_data[4].to_dec().unwrap());

        assert_eq!("4706530486660795807594696126453392843593312253601667969008027865938669831613255990876876168574341472858634191430502533997053787371484591516484310077682156120\
        0343390749927996265246866447155790487554483555192805709960222015718787293872197230832464704800887153568636866026153126587657548580608446574507279965440247754859129693686\
        1864273991033137371106324132550175224820164581900030456410773386740196083471393997554706544523739752281900419801521207994038554809091738654313973079882387597672518908535\
        80982844825639097363091181044515877489450972963624109587697097258041963985607958610791800500711857115582406526050626576194", res_data[5].to_dec().unwrap());
    }
}