arnaucube
arnaucube/ark-ec-blind-signaturesPublic Self-hosted repository
ark-ec-blind-signatures/src/lib.rs
src/lib.rsRaw
  1#[allow(non_snake_case)]
  2#[allow(clippy::many_single_char_names)]
  3
  4// pub type ConstraintF = ark_bn254::Fr;
  5// pub type ConstraintF = ark_ed_on_bn254::Fq; // base field
  6pub type ConstraintF = ark_ed_on_bn254::Fr; // scalar field
  7
  8use ark_ec::{AffineCurve, ProjectiveCurve, TEModelParameters};
  9use ark_ed_on_bn254::{EdwardsAffine, EdwardsParameters, EdwardsProjective, FqParameters, Fr};
 10use ark_ff::{
 11    to_bytes, BigInteger, BigInteger256, Field, Fp256, FpParameters, One, PrimeField, Zero,
 12};
 13
 14use ark_std::rand::{CryptoRng, RngCore};
 15use ark_std::UniformRand;
 16
 17// hash
 18use arkworks_native_gadgets::poseidon;
 19use arkworks_native_gadgets::poseidon::FieldHasher;
 20use arkworks_utils::{
 21    bytes_matrix_to_f, bytes_vec_to_f, parse_vec, poseidon_params::setup_poseidon_params, Curve,
 22};
 23
 24const GX: Fp256<FqParameters> = <EdwardsParameters as TEModelParameters>::AFFINE_GENERATOR_COEFFS.0;
 25const GY: Fp256<FqParameters> = <EdwardsParameters as TEModelParameters>::AFFINE_GENERATOR_COEFFS.1;
 26
 27#[macro_use]
 28extern crate lazy_static;
 29
 30lazy_static! {
 31    static ref G_AFFINE: EdwardsAffine = EdwardsAffine::new(GX, GY);
 32    static ref G: EdwardsProjective = G_AFFINE.into_projective();
 33}
 34
 35// Fr modulus (bigendian)
 36const FR_MODULUS: BigInteger256 = BigInteger256::new([
 37    0x677297DC392126F1,
 38    0xAB3EEDB83920EE0A,
 39    0x370A08B6D0302B0B,
 40    0x060C89CE5C263405,
 41]);
 42
 43// poseidon
 44pub fn poseidon_setup_params<F: PrimeField>(
 45    curve: Curve,
 46    exp: i8,
 47    width: u8,
 48) -> poseidon::PoseidonParameters<F> {
 49    let pos_data = setup_poseidon_params(curve, exp, width).unwrap();
 50
 51    let mds_f = bytes_matrix_to_f(&pos_data.mds);
 52    let rounds_f = bytes_vec_to_f(&pos_data.rounds);
 53
 54    poseidon::PoseidonParameters {
 55        mds_matrix: mds_f,
 56        round_keys: rounds_f,
 57        full_rounds: pos_data.full_rounds,
 58        partial_rounds: pos_data.partial_rounds,
 59        sbox: poseidon::sbox::PoseidonSbox(pos_data.exp),
 60        width: pos_data.width,
 61    }
 62}
 63
 64pub struct PrivateKey(ConstraintF);
 65pub type PublicKey = EdwardsAffine;
 66pub type BlindedSignature = ConstraintF;
 67pub struct Signature {
 68    s: ConstraintF,
 69    r: EdwardsAffine,
 70}
 71
 72#[derive(Debug)]
 73pub struct UserSecretData {
 74    a: ConstraintF,
 75    b: ConstraintF,
 76    r: EdwardsAffine,
 77}
 78impl UserSecretData {
 79    fn new_empty() -> Self {
 80        UserSecretData {
 81            a: ConstraintF::from(0),
 82            b: ConstraintF::from(0),
 83            r: G_AFFINE.clone(), // WIP
 84        }
 85    }
 86}
 87
 88pub fn new_sk<R: RngCore>(rng: &mut R) -> PrivateKey {
 89    let sk: PrivateKey = PrivateKey(ConstraintF::rand(rng));
 90    sk
 91}
 92
 93impl PrivateKey {
 94    pub fn public(&self) -> PublicKey {
 95        let pk: PublicKey = G.mul(self.0.into_repr()).into_affine();
 96        pk
 97    }
 98    pub fn blind_sign(&self, m_blinded: ConstraintF, k: ConstraintF) -> BlindedSignature {
 99        self.0 * m_blinded + k
100    }
101}
102
103pub fn new_request_params<R: RngCore>(rng: &mut R) -> (ConstraintF, EdwardsAffine) {
104    let k = ConstraintF::rand(rng);
105    let R = G.mul(k.into_repr()).into_affine();
106    (k, R)
107}
108
109fn new_blind_params<R: RngCore>(rng: &mut R, signer_r: EdwardsAffine) -> UserSecretData {
110    let mut u: UserSecretData = UserSecretData::new_empty();
111    u.a = ConstraintF::rand(rng);
112    u.b = ConstraintF::rand(rng);
113
114    // R = aR' + bG
115    let aR = signer_r.mul(u.a.into_repr());
116    let bG = G.mul(u.b.into_repr());
117    u.r = aR.into_affine() + bG.into_affine();
118
119    // check that u.r.x can be safely converted into Fr, and if not, choose other u.a & u.b values
120    let x_repr = u.r.x.into_repr();
121    if !(x_repr >= ConstraintF::one().into_repr() && x_repr < FR_MODULUS) {
122        return new_blind_params(rng, signer_r);
123    }
124    return u;
125}
126
127pub fn blind<R: RngCore>(
128    rng: &mut R,
129    poseidon_hash: &poseidon::Poseidon<ConstraintF>,
130    m: ConstraintF,
131    signer_r: EdwardsAffine,
132) -> Result<(ConstraintF, UserSecretData), ark_crypto_primitives::Error> {
133    let u = new_blind_params(rng, signer_r);
134    // use unwrap, as we already checked that R.x is inside Fr and will not give None
135    let x_fr = ConstraintF::from_repr(u.r.x.into_repr()).unwrap();
136
137    // m' = a^-1 rx h(m)
138    let h_m = poseidon_hash.hash(&[m])?;
139    let m_blinded = u.a.inverse().unwrap() * x_fr * h_m;
140
141    Ok((m_blinded, u))
142}
143
144pub fn unblind(s_blinded: ConstraintF, u: UserSecretData) -> Signature {
145    // s = a s' + b
146    let s = u.a * s_blinded + u.b;
147    Signature { s, r: u.r }
148}
149
150pub fn verify(
151    poseidon_hash: &poseidon::Poseidon<ConstraintF>,
152    m: ConstraintF,
153    s: Signature,
154    q: PublicKey,
155) -> bool {
156    let sG = G.mul(s.s.into_repr());
157
158    let h_m = poseidon_hash.hash(&[m]).unwrap();
159
160    let x_repr = s.r.x.into_repr();
161    if !(x_repr >= ConstraintF::one().into_repr() && x_repr < FR_MODULUS) {
162        return false; // error, s.r.x does not fit in Fr
163    }
164    let x_fr = ConstraintF::from_repr(s.r.x.into_repr()).unwrap();
165    let right = s.r + q.mul((x_fr * h_m).into_repr()).into_affine();
166
167    sG.into_affine() == right
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173
174    #[test]
175    fn test_blind() {
176        let poseidon_params = poseidon_setup_params::<ConstraintF>(Curve::Bn254, 5, 3);
177        let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
178
179        let mut rng = ark_std::test_rng();
180
181        let sk = new_sk(&mut rng);
182        let pk = sk.public();
183
184        let (k, signer_r) = new_request_params(&mut rng);
185        let m = ConstraintF::from(1234);
186
187        let (m_blinded, u) = blind(&mut rng, &poseidon_hash, m, signer_r).unwrap();
188
189        let s_blinded = sk.blind_sign(m_blinded, k);
190
191        let s = unblind(s_blinded, u);
192
193        let verified = verify(&poseidon_hash, m, s, pk);
194        assert!(verified);
195    }
196}