Skip to content

Commit 74d7030

Browse files
committed
Added file structure for Schnorr signature module.
1 parent 2ad7d5e commit 74d7030

File tree

7 files changed

+407
-1
lines changed

7 files changed

+407
-1
lines changed

mithril-stm/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ include = ["**/*.rs", "Cargo.toml", "README.md", ".gitignore"]
1414
crate-type = ["lib", "cdylib", "staticlib"]
1515

1616
[features]
17-
default = ["rug-backend"]
17+
default = ["rug-backend", "future_snark"]
1818
rug-backend = ["rug/default"]
1919
num-integer-backend = ["num-bigint", "num-rational", "num-traits"]
2020
benchmark-internals = [] # For benchmarking multi_sig
2121
future_proof_system = [] # For activating future proof systems
22+
future_snark = [] # For activating snark features
2223

2324
[dependencies]
2425
blake2 = "0.10.6"

mithril-stm/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ mod merkle_tree;
118118
mod parameters;
119119
mod participant;
120120
mod single_signature;
121+
#[cfg(feature = "future_snark")]
122+
mod schnorr_signatures;
121123

122124
pub use aggregate_signature::{
123125
AggregateSignature, AggregateSignatureType, AggregateVerificationKey, BasicVerifier, Clerk,
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use midnight_circuits::{
2+
ecc::{
3+
hash_to_curve::HashToCurveGadget,
4+
native::EccChip,
5+
},
6+
hash::poseidon::PoseidonChip,
7+
instructions::{
8+
HashToCurveCPU,
9+
hash::HashCPU,
10+
},
11+
types::AssignedNative,
12+
};
13+
14+
pub use midnight_curves::{
15+
Bls12, EDWARDS_D, Fq as JubjubBase, Fq as BlsScalar, Fr as JubjubScalar,
16+
G1Affine as BlstG1Affine, G1Projective as BlstG1, G2Affine as BlstG2Affine, JubjubAffine,
17+
JubjubExtended as Jubjub, JubjubExtended, JubjubSubgroup, MODULUS,
18+
};
19+
20+
21+
22+
use ff::Field;
23+
use group::Group;
24+
use rand_core::{CryptoRng, RngCore};
25+
use subtle::{Choice, ConstantTimeEq};
26+
use thiserror::Error;
27+
28+
// use crate::schnorr_signatures::{get_coordinates, jubjub_base_to_scalar, is_on_curve};
29+
30+
31+
32+
pub fn get_coordinates(point: JubjubSubgroup) -> (JubjubBase, JubjubBase) {
33+
let extended: JubjubExtended = point.into(); // Convert to JubjubExtended
34+
let affine: JubjubAffine = extended.into(); // Convert to JubjubAffine (affine coordinates)
35+
let x = affine.get_u(); // Get x-coordinate
36+
let y = affine.get_v(); // Get y-coordinate
37+
(x, y)
38+
}
39+
40+
pub fn jubjub_base_to_scalar(x: JubjubBase) -> JubjubScalar {
41+
let bytes = x.to_bytes_le();
42+
JubjubScalar::from_raw([
43+
u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
44+
u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
45+
u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
46+
u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
47+
])
48+
}
49+
50+
51+
pub fn is_on_curve(u: JubjubBase, v: JubjubBase) -> Choice {
52+
let u2 = u.square();
53+
let v2 = v.square();
54+
55+
// Left-hand side: v² - u²
56+
let lhs = v2 - u2;
57+
58+
// Right-hand side: 1 + EDWARDS_D * (u² * v²)
59+
let rhs = JubjubBase::ONE + EDWARDS_D * u2 * v2;
60+
61+
// Compare in constant time
62+
lhs.ct_eq(&rhs)
63+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
pub use midnight_curves::{
2+
Bls12, EDWARDS_D, Fq as JubjubBase, Fq as BlsScalar, Fr as JubjubScalar,
3+
G1Affine as BlstG1Affine, G1Projective as BlstG1, G2Affine as BlstG2Affine, JubjubAffine,
4+
JubjubExtended as Jubjub, JubjubExtended, JubjubSubgroup, MODULUS,
5+
};
6+
7+
use midnight_circuits::{
8+
ecc::{
9+
hash_to_curve::HashToCurveGadget,
10+
native::EccChip,
11+
},
12+
hash::poseidon::PoseidonChip,
13+
instructions::{
14+
HashToCurveCPU,
15+
hash::HashCPU,
16+
},
17+
types::AssignedNative,
18+
};
19+
20+
use ff::{Field};
21+
use group::Group;
22+
23+
use subtle::{Choice, ConstantTimeEq};
24+
use thiserror::Error;
25+
26+
pub mod helper;
27+
mod signature;
28+
mod signing_key;
29+
mod verification_key;
30+
31+
pub use signature::*;
32+
pub use signing_key::*;
33+
pub use verification_key::*;
34+
35+
36+
37+
type JubjubHashToCurve = HashToCurveGadget<
38+
JubjubBase,
39+
Jubjub,
40+
AssignedNative<JubjubBase>,
41+
PoseidonChip<JubjubBase>,
42+
EccChip<Jubjub>,
43+
>;
44+
45+
type PoseidonHash = PoseidonChip<JubjubBase>;
46+
47+
pub const DST_SIGNATURE: JubjubBase = JubjubBase::from_raw([2u64, 0, 0, 0]);
48+
49+
50+
#[derive(Debug, Error)]
51+
pub enum SignatureError {
52+
#[error("Verification failed: Signature is invalid.")]
53+
VerificationFailed,
54+
/// This error occurs when the serialization of the raw bytes failed
55+
#[error("Invalid bytes")]
56+
SerializationError,
57+
}
58+
59+
60+
61+
62+
#[cfg(test)]
63+
mod tests {
64+
use super::*;
65+
use rand_core::OsRng;
66+
67+
/// Test signing functionality.
68+
#[test]
69+
fn test_signature_verification_valid() {
70+
let mut rng = OsRng;
71+
let sk = SigningKey::generate(&mut rng);
72+
let msg = JubjubBase::random(&mut rng);
73+
74+
// Sign the message
75+
let signature = sk.sign(msg, &mut rng);
76+
77+
// Ensure the components of the signature are non-default values
78+
assert_ne!(
79+
signature.sigma,
80+
JubjubSubgroup::identity(),
81+
"Signature sigma should not be the identity element."
82+
);
83+
assert_ne!(
84+
signature.s,
85+
JubjubScalar::ZERO,
86+
"Signature s component should not be zero."
87+
);
88+
assert_ne!(
89+
signature.c,
90+
JubjubBase::ZERO,
91+
"Signature c component should not be zero."
92+
);
93+
94+
signature.verify(msg, &VerificationKey::from(&sk)).unwrap();
95+
}
96+
97+
#[test]
98+
fn test_signature_verification_invalid_signature() {
99+
let mut rng = OsRng;
100+
let sk = SigningKey::generate(&mut rng);
101+
let msg = JubjubBase::random(&mut rng);
102+
let vk: VerificationKey = (&sk).into();
103+
104+
// Generate signature and tamper with it
105+
let mut signature = sk.sign(msg, &mut rng);
106+
signature.s = JubjubScalar::random(&mut rng); // Modify `s` component
107+
108+
// Verify the modified signature
109+
let result = signature.verify(msg, &vk);
110+
assert!(
111+
result.is_err(),
112+
"Invalid signature should fail verification, but it passed."
113+
);
114+
}
115+
116+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
2+
use midnight_circuits::{
3+
instructions::{
4+
HashToCurveCPU,
5+
hash::HashCPU,
6+
},
7+
};
8+
9+
pub use midnight_curves::{Fq as JubjubBase, Fr as JubjubScalar,
10+
JubjubSubgroup,
11+
};
12+
13+
14+
use group::Group;
15+
16+
use crate::schnorr_signatures::helper::{get_coordinates, jubjub_base_to_scalar, is_on_curve};
17+
use crate::schnorr_signatures::verification_key::*;
18+
use crate::schnorr_signatures::{JubjubHashToCurve, SignatureError, PoseidonHash, DST_SIGNATURE};
19+
20+
21+
22+
/// Schnorr signature including the value sigma used for the lottery
23+
#[derive(Debug, Clone, PartialEq, Eq)]
24+
pub struct SchnorrSignature {
25+
pub sigma: JubjubSubgroup,
26+
pub s: JubjubScalar,
27+
pub c: JubjubBase,
28+
}
29+
30+
impl SchnorrSignature {
31+
/// Verify a signature against a verification key.
32+
pub fn verify(&self, msg: JubjubBase, vk: &VerificationKey) -> Result<(), SignatureError> {
33+
let g = JubjubSubgroup::generator();
34+
let hash = JubjubHashToCurve::hash_to_curve(&[msg]);
35+
let c_scalar = jubjub_base_to_scalar(self.c);
36+
37+
let (hx, hy) = get_coordinates(hash);
38+
let (vk_x, vk_y) = get_coordinates(vk.0);
39+
let (sigma_x, sigma_y) = get_coordinates(self.sigma);
40+
let cap_r_1_prime = &hash * &self.s + &self.sigma * &c_scalar;
41+
let cap_r_2_prime = &g * &self.s + &vk.0 * &c_scalar;
42+
let (cap_r_1_x_prime, cap_r_1_y_prime) = get_coordinates(cap_r_1_prime);
43+
let (cap_r_2_x_prime, cap_r_2_y_prime) = get_coordinates(cap_r_2_prime);
44+
let c_prime = PoseidonHash::hash(&[
45+
DST_SIGNATURE,
46+
hx,
47+
hy,
48+
vk_x,
49+
vk_y,
50+
sigma_x,
51+
sigma_y,
52+
cap_r_1_x_prime,
53+
cap_r_1_y_prime,
54+
cap_r_2_x_prime,
55+
cap_r_2_y_prime,
56+
]);
57+
58+
if c_prime != self.c {
59+
return Err(SignatureError::VerificationFailed);
60+
}
61+
62+
Ok(())
63+
}
64+
65+
pub fn sigma(&self) -> (JubjubBase, JubjubBase) {
66+
let (x, y) = get_coordinates(self.sigma);
67+
(x, y)
68+
}
69+
}
70+
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
2+
pub use midnight_curves::{Fq as JubjubBase, Fr as JubjubScalar,
3+
JubjubExtended as Jubjub, JubjubExtended, JubjubSubgroup,
4+
};
5+
use midnight_circuits::{
6+
instructions::{
7+
HashToCurveCPU,
8+
hash::HashCPU,
9+
},
10+
};
11+
12+
use ff::Field;
13+
use group::Group;
14+
use rand_core::{CryptoRng, RngCore};
15+
use thiserror::Error;
16+
17+
use crate::schnorr_signatures::helper::{get_coordinates, jubjub_base_to_scalar, is_on_curve};
18+
use crate::schnorr_signatures::verification_key::*;
19+
use crate::schnorr_signatures::signature::*;
20+
21+
use crate::schnorr_signatures::{JubjubHashToCurve, SignatureError, PoseidonHash, DST_SIGNATURE};
22+
23+
24+
25+
/// The signing key is a scalar from the Jubjub scalar field
26+
#[derive(Debug, Clone)]
27+
pub struct SigningKey(JubjubScalar);
28+
29+
/// Implementation of the Schnorr signature scheme using the Jubjub curve
30+
impl SigningKey {
31+
pub fn generate(rng: &mut (impl RngCore + CryptoRng)) -> Self {
32+
let sk = JubjubScalar::random(rng);
33+
SigningKey(sk)
34+
}
35+
36+
/// A slightly modified version of the regular Schnorr signature (I think)
37+
/// We include the computation of sigma, a value depending only on the msg
38+
/// and the secret key as it is used for the lottery process
39+
pub fn sign(&self, msg: JubjubBase, rng: &mut (impl RngCore + CryptoRng)) -> SchnorrSignature {
40+
let g = JubjubSubgroup::generator();
41+
let vk = &g * &self.0;
42+
43+
let hash = JubjubHashToCurve::hash_to_curve(&[msg]);
44+
let sigma = &hash * &self.0;
45+
let r = JubjubScalar::random(rng);
46+
let cap_r_1 = &hash * &r;
47+
let cap_r_2 = &g * &r;
48+
49+
let (hx, hy) = get_coordinates(hash);
50+
let (vk_x, vk_y) = get_coordinates(vk);
51+
let (sigma_x, sigma_y) = get_coordinates(sigma);
52+
let (cap_r_1_x, cap_r_1_y) = get_coordinates(cap_r_1);
53+
let (cap_r_2_x, cap_r_2_y) = get_coordinates(cap_r_2);
54+
55+
let c = PoseidonHash::hash(&[
56+
DST_SIGNATURE,
57+
hx,
58+
hy,
59+
vk_x,
60+
vk_y,
61+
sigma_x,
62+
sigma_y,
63+
cap_r_1_x,
64+
cap_r_1_y,
65+
cap_r_2_x,
66+
cap_r_2_y,
67+
]);
68+
let c_scalar = jubjub_base_to_scalar(c);
69+
let s = r - self.0 * c_scalar;
70+
71+
SchnorrSignature { sigma, s, c }
72+
}
73+
74+
}
75+
76+
77+
impl From<&SigningKey> for VerificationKey {
78+
fn from(sk: &SigningKey) -> Self {
79+
let g = JubjubSubgroup::generator();
80+
let vk = &g * &sk.0;
81+
VerificationKey(vk)
82+
}
83+
}

0 commit comments

Comments
 (0)