Module nimue::plugins::ark

source ·
Expand description

Arkworks’s algebra bindings. This module contains utilities for working with arkworks types and aid in the Fiat-Shamir heuristic for protocols dealing with field elements and group elements.

§Examples

Here’s a protocol that does Fiat-Shamir without caring about the hash function used or the serialization format.

use ark_ec::CurveGroup;
use ark_std::UniformRand;
use nimue::{IOPattern, Merlin, DuplexHash, ProofResult};
use nimue::plugins::ark::*;

fn prove<G: CurveGroup>(
    merlin: &mut Merlin,
    x: G::ScalarField,
) -> ProofResult<&[u8]>
{
    let k = G::ScalarField::rand(merlin.rng());
    merlin.add_points(&[G::generator() * k])?;
    let [c]: [G::ScalarField; 1] = merlin.challenge_scalars()?;
    merlin.add_scalars(&[k + c * x])?;
    Ok(merlin.transcript())
}

The type constraint on Merlin hints the compiler that we are going to be absorbing elements from the group G and squeezing challenges in the scalar field G::ScalarField. Similarly, we could have been squeezing out bytes.


fn prove<G: CurveGroup>(
    merlin: &mut Merlin,
    x: G::ScalarField,
) -> ProofResult<&[u8]>
where
    Merlin: GroupWriter<G> + ByteChallenges,
{
    let k = G::ScalarField::rand(merlin.rng());
    merlin.add_points(&[G::generator() * k])?;
    let c_bytes = merlin.challenge_bytes::<16>()?;
    let c = G::ScalarField::from_le_bytes_mod_order(&c_bytes);
    merlin.add_scalars(&[k + c * x])?;
    Ok(merlin.transcript())
}

Merlin is actually more general than this, and can be used with any hash function, over any field. Let’s for instance use sha2 on the above transcript instead of Keccak.


fn prove<G: CurveGroup, H: DuplexHash>(
    merlin: &mut Merlin<H>,
    x: G::ScalarField,
) -> ProofResult<&[u8]>

No change to the function body is needed. Now the proving function can be called with nimue::DigestBridge<sha2::Sha256>. As easy as that. More modern hash functions may want to operate over some some field different than $\mathbb{F}_8$, for instance over the base field of the sponge. Also in this case it’s sufficient to slightly change the proving function to specify the field over which the hash function operates, to something like:


fn prove<G, H, U>(
    merlin: &mut Merlin<H, U>,
    x: G::ScalarField,
) -> ProofResult<&[u8]>
where
    G: CurveGroup,
    G::BaseField: PrimeField,
    // Declares the type the hash function works on
    U: Unit,
    // Constrains the hash function to work over U, ...
    H: DuplexHash<U>,
    // ... and the prover to be able to absorb and squeeze elements from the group and the base field.
    // (normally would be the ScalarField but this is to make it work nicely with algebraic hashes)
    Merlin<H, U>: GroupWriter<G> + FieldWriter<G::BaseField> + ByteChallenges,
{
    let k = G::ScalarField::rand(merlin.rng());
    merlin.add_points(&[G::generator() * k])?;
    let c_bytes = merlin.challenge_bytes::<16>()?;
    let c = G::ScalarField::from_le_bytes_mod_order(&c_bytes);
    // XXX. very YOLO code, don't do this at home.
    // The resulting proof is malleable and could also not be correct if
    // G::BaseField::MODULUS < G::ScalarField::MODULUS
    let r = G::BaseField::from_le_bytes_mod_order(&(k + c * x).into_bigint().to_bytes_le());
    merlin.add_scalars(&[r])?;
    Ok(merlin.transcript())
}

Now the above code should work with algebraic hashes such as PoseidonHash just as fine as Keccak.

Re-exports§

Modules§

  • (WIP) Support for the Poseidon Hash function. This code has been blatantly stolen from ark-crypto-primitive::sponge from William Lin, with contributions from Pratyush Mishra, Weikeng Chen, Yuwen Zhang, Kristian Sosnin, Merlyn, Wilson Nguyen, Hossein Moghaddas, and others.

Traits§

  • Interpret verifier messages as uniformly distributed field elements.
  • Absorb and squeeze field elements to the IO pattern.
  • Add field elements as shared public information.
  • Retrieve field elements from the protocol trainscript.
  • Add field elements to the protocol transcript.
  • Send group elements in the IO pattern.
  • Add group elements to the protocol transcript.
  • Receive (and deserialize) group elements from the IO pattern.
  • Add points to the protocol transcript.

Functions§

  • Move a value from prime field F1 to prime field F2.