mithril_stm/circuits/halo2/off_circuit/
unique_signature.rs1use ff::Field;
2use group::Group;
3use rand_core::{CryptoRng, RngCore};
4
5use crate::circuits::halo2::constants::DST_UNIQUE_SIGNATURE;
6use crate::circuits::halo2::hash::{HashCPU, HashToCurveCPU, JubjubHashToCurve, PoseidonHash};
7use crate::circuits::halo2::off_circuit::error::SignatureError;
8use crate::circuits::halo2::off_circuit::utils::{
9 get_coordinates, is_on_curve, jubjub_base_to_scalar,
10};
11use crate::circuits::halo2::types::{Jubjub, JubjubBase, JubjubScalar, JubjubSubgroup};
12
13#[derive(Debug, Clone)]
14pub struct SigningKey(JubjubScalar);
15
16impl SigningKey {
17 pub fn generate(rng: &mut (impl RngCore + CryptoRng)) -> Self {
18 let sk = JubjubScalar::random(rng);
19 SigningKey(sk)
20 }
21
22 pub fn sign(&self, msg: &[JubjubBase], rng: &mut (impl RngCore + CryptoRng)) -> Signature {
23 let g = JubjubSubgroup::generator();
24 let vk = g * self.0;
25
26 let hash = JubjubHashToCurve::hash_to_curve(msg);
27 let sigma = hash * self.0;
28 let r = JubjubScalar::random(rng);
29 let cap_r_1 = hash * r;
30 let cap_r_2 = g * r;
31
32 let (hx, hy) = get_coordinates(hash);
33 let (vk_x, vk_y) = get_coordinates(vk);
34 let (sigma_x, sigma_y) = get_coordinates(sigma);
35 let (cap_r_1_x, cap_r_1_y) = get_coordinates(cap_r_1);
36 let (cap_r_2_x, cap_r_2_y) = get_coordinates(cap_r_2);
37
38 let c = PoseidonHash::hash(&[
39 DST_UNIQUE_SIGNATURE,
40 hx,
41 hy,
42 vk_x,
43 vk_y,
44 sigma_x,
45 sigma_y,
46 cap_r_1_x,
47 cap_r_1_y,
48 cap_r_2_x,
49 cap_r_2_y,
50 ]);
51 let c_scalar = jubjub_base_to_scalar(c);
52 let s = r - self.0 * c_scalar;
53
54 Signature { sigma, s, c }
55 }
56
57 pub fn sign_long(
58 &self,
59 msg: &[JubjubBase],
60 rng: &mut (impl RngCore + CryptoRng),
61 ) -> LongSignature {
62 let g = JubjubSubgroup::generator();
63 let vk = g * self.0;
64
65 let hash = JubjubHashToCurve::hash_to_curve(msg);
66 let sigma = hash * self.0;
67 let r = JubjubScalar::random(rng);
68 let cap_r_1 = hash * r;
69 let cap_r_2 = g * r;
70
71 let (hx, hy) = get_coordinates(hash);
72 let (vk_x, vk_y) = get_coordinates(vk);
73 let (sigma_x, sigma_y) = get_coordinates(sigma);
74 let (cap_r_1_x, cap_r_1_y) = get_coordinates(cap_r_1);
75 let (cap_r_2_x, cap_r_2_y) = get_coordinates(cap_r_2);
76
77 let c = PoseidonHash::hash(&[
78 DST_UNIQUE_SIGNATURE,
79 hx,
80 hy,
81 vk_x,
82 vk_y,
83 sigma_x,
84 sigma_y,
85 cap_r_1_x,
86 cap_r_1_y,
87 cap_r_2_x,
88 cap_r_2_y,
89 ]);
90 let c_scalar = jubjub_base_to_scalar(c);
91 let s = r - self.0 * c_scalar;
92
93 LongSignature {
94 sigma,
95 cap_r_1,
96 cap_r_2,
97 s,
98 }
99 }
100}
101
102#[derive(Debug, Clone, Copy, Default)]
103pub struct VerificationKey(pub JubjubSubgroup);
104
105impl From<&SigningKey> for VerificationKey {
106 fn from(sk: &SigningKey) -> Self {
107 let g = JubjubSubgroup::generator();
108 let vk = g * sk.0;
109 VerificationKey(vk)
110 }
111}
112
113impl VerificationKey {
114 pub fn to_field(&self) -> [JubjubBase; 2] {
115 let (x, y) = get_coordinates(self.0);
116 [x, y]
117 }
118
119 pub fn to_bytes(&self) -> [u8; 64] {
120 let (x, y) = get_coordinates(self.0);
121 let mut bytes = [0u8; 64];
122 bytes[0..32].copy_from_slice(&x.to_bytes_le());
123 bytes[32..64].copy_from_slice(&y.to_bytes_le());
124 bytes
125 }
126
127 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignatureError> {
128 let bytes = bytes.get(0..64).ok_or(SignatureError::SerializationError)?;
129 let mut u_bytes = [0u8; 32];
130 u_bytes.copy_from_slice(&bytes[0..32]);
131 let mut v_bytes = [0u8; 32];
132 v_bytes.copy_from_slice(&bytes[32..64]);
133
134 let u = JubjubBase::from_bytes_le(&u_bytes)
135 .into_option()
136 .ok_or(SignatureError::SerializationError)?;
137 let v = JubjubBase::from_bytes_le(&v_bytes)
138 .into_option()
139 .ok_or(SignatureError::SerializationError)?;
140 if !bool::from(is_on_curve(u, v)) {
141 return Err(SignatureError::SerializationError);
142 }
143
144 let point = JubjubSubgroup::from_raw_unchecked(u, v);
145 if !bool::from(Jubjub::from(point).is_prime_order()) {
146 return Err(SignatureError::SerializationError);
147 }
148
149 Ok(VerificationKey(point))
150 }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq)]
154pub struct Signature {
155 pub sigma: JubjubSubgroup,
156 pub s: JubjubScalar,
157 pub c: JubjubBase,
158}
159
160impl Signature {
161 pub fn verify(&self, msg: &[JubjubBase], vk: &VerificationKey) -> Result<(), SignatureError> {
163 let g = JubjubSubgroup::generator();
164 let hash = JubjubHashToCurve::hash_to_curve(msg);
165 let c_scalar = jubjub_base_to_scalar(self.c);
166
167 let (hx, hy) = get_coordinates(hash);
168 let (vk_x, vk_y) = get_coordinates(vk.0);
169 let (sigma_x, sigma_y) = get_coordinates(self.sigma);
170 let cap_r_1_prime = hash * self.s + self.sigma * c_scalar;
171 let cap_r_2_prime = g * self.s + vk.0 * c_scalar;
172 let (cap_r_1_x_prime, cap_r_1_y_prime) = get_coordinates(cap_r_1_prime);
173 let (cap_r_2_x_prime, cap_r_2_y_prime) = get_coordinates(cap_r_2_prime);
174 let c_prime = PoseidonHash::hash(&[
175 DST_UNIQUE_SIGNATURE,
176 hx,
177 hy,
178 vk_x,
179 vk_y,
180 sigma_x,
181 sigma_y,
182 cap_r_1_x_prime,
183 cap_r_1_y_prime,
184 cap_r_2_x_prime,
185 cap_r_2_y_prime,
186 ]);
187
188 if c_prime != self.c {
189 return Err(SignatureError::VerificationFailed);
190 }
191
192 Ok(())
193 }
194
195 pub fn sigma(&self) -> (JubjubBase, JubjubBase) {
196 let (x, y) = get_coordinates(self.sigma);
197 (x, y)
198 }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct LongSignature {
203 pub sigma: JubjubSubgroup,
204 pub cap_r_1: JubjubSubgroup,
205 pub cap_r_2: JubjubSubgroup,
206 pub s: JubjubScalar,
207}
208
209impl LongSignature {
210 pub fn verify(&self, msg: &[JubjubBase], vk: &VerificationKey) -> Result<(), SignatureError> {
211 let g = JubjubSubgroup::generator();
212 let hash = JubjubHashToCurve::hash_to_curve(msg);
213 let (hx, hy) = get_coordinates(hash);
214 let (vk_x, vk_y) = get_coordinates(vk.0);
215 let (sigma_x, sigma_y) = get_coordinates(self.sigma);
216 let (cap_r_1_x, cap_r_1_y) = get_coordinates(self.cap_r_1);
217 let (cap_r_2_x, cap_r_2_y) = get_coordinates(self.cap_r_2);
218
219 let c_prime = PoseidonHash::hash(&[
220 DST_UNIQUE_SIGNATURE,
221 hx,
222 hy,
223 vk_x,
224 vk_y,
225 sigma_x,
226 sigma_y,
227 cap_r_1_x,
228 cap_r_1_y,
229 cap_r_2_x,
230 cap_r_2_y,
231 ]);
232 let c = jubjub_base_to_scalar(c_prime);
233
234 {
235 let right = hash * self.s + self.sigma * c;
236 if self.cap_r_1 != right {
237 return Err(SignatureError::VerificationFailed);
238 }
239 }
240
241 {
242 let right = g * self.s + vk.0 * c;
243 if self.cap_r_2 != right {
244 return Err(SignatureError::VerificationFailed);
245 }
246 }
247
248 Ok(())
249 }
250
251 pub fn to_short_signature(&self, hash_msg: &JubjubSubgroup, vk: &VerificationKey) -> Signature {
252 let (hx, hy) = get_coordinates(*hash_msg);
253 let (vk_x, vk_y) = get_coordinates(vk.0);
254 let (sigma_x, sigma_y) = get_coordinates(self.sigma);
255 let (cap_r_1_x, cap_r_1_y) = get_coordinates(self.cap_r_1);
256 let (cap_r_2_x, cap_r_2_y) = get_coordinates(self.cap_r_2);
257
258 let c = PoseidonHash::hash(&[
259 DST_UNIQUE_SIGNATURE,
260 hx,
261 hy,
262 vk_x,
263 vk_y,
264 sigma_x,
265 sigma_y,
266 cap_r_1_x,
267 cap_r_1_y,
268 cap_r_2_x,
269 cap_r_2_y,
270 ]);
271
272 Signature {
273 sigma: self.sigma,
274 s: self.s,
275 c,
276 }
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283 use rand_core::OsRng;
284
285 #[test]
287 fn test_signature_unique_verification_valid() {
288 let mut rng = OsRng;
289 let sk = SigningKey::generate(&mut rng);
290 let msg = JubjubBase::random(&mut rng);
291
292 let signature = sk.sign(&[msg], &mut rng);
294
295 assert_ne!(
297 signature.sigma,
298 JubjubSubgroup::identity(),
299 "Signature sigma should not be the identity element."
300 );
301 assert_ne!(
302 signature.s,
303 JubjubScalar::ZERO,
304 "Signature s component should not be zero."
305 );
306 assert_ne!(
307 signature.c,
308 JubjubBase::ZERO,
309 "Signature c component should not be zero."
310 );
311
312 signature.verify(&[msg], &VerificationKey::from(&sk)).unwrap();
313 }
314
315 #[test]
316 fn test_signature_unique_verification_invalid_signature() {
317 let mut rng = OsRng;
318 let sk = SigningKey::generate(&mut rng);
319 let msg = JubjubBase::random(&mut rng);
320 let vk: VerificationKey = (&sk).into();
321
322 let mut signature = sk.sign(&[msg], &mut rng);
324 signature.s = JubjubScalar::random(&mut rng); let result = signature.verify(&[msg], &vk);
328 assert!(
329 result.is_err(),
330 "Invalid signature should fail verification, but it passed."
331 );
332 }
333
334 #[test]
335 fn test_signature_unique_long_verification_valid() {
336 let mut rng = OsRng;
337 let sk = SigningKey::generate(&mut rng);
338 let vk: VerificationKey = (&sk).into();
339 let msg = JubjubBase::random(&mut rng);
340
341 let sig_long = sk.sign_long(&[msg], &mut rng);
343 assert!(sig_long.verify(&[msg], &vk).is_ok());
345
346 let hash_msg = JubjubHashToCurve::hash_to_curve(&[msg]);
347 let sig = sig_long.to_short_signature(&hash_msg, &vk);
348 assert!(sig.verify(&[msg], &vk).is_ok());
349 }
350}