pathfinder_crypto/signature/
ecdsa.rs1use std::fmt::{Display, Formatter};
2
3use crate::algebra::curve::{AffinePoint, ProjectivePoint, CURVE_G};
4use crate::algebra::field::{CurveOrderMontFelt, Felt, MontFelt};
5
6pub const UPPER_BOUND: MontFelt = MontFelt::from_raw([
12 18446743986131435553,
13 160989183,
14 18446744073709255680,
15 576459263475450960,
16]);
17
18#[derive(Debug, Eq, PartialEq)]
20pub enum SignatureError {
21 Signature,
23
24 Randomness,
26
27 Message,
29
30 SecretKey,
32
33 PublicKey,
35}
36
37impl Display for SignatureError {
38 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
39 match self {
40 SignatureError::Signature => write!(f, "invalid signature"),
41 SignatureError::Message => write!(f, "invalid message"),
42 SignatureError::Randomness => write!(f, "invalid randomness"),
43 SignatureError::SecretKey => write!(f, "invalid secret key"),
44 SignatureError::PublicKey => write!(f, "invalid public key"),
45 }
46 }
47}
48
49impl std::error::Error for SignatureError {}
50
51pub fn get_pk(sk: Felt) -> Option<Felt> {
53 CurveOrderMontFelt::try_from(sk)
54 .map(ProjectivePoint::gen_multiply_elm)
55 .as_ref()
56 .map(AffinePoint::from)
57 .map(|ap| ap.x)
58 .map(Felt::from)
59 .ok()
60}
61
62pub fn ecdsa_sign(sk: Felt, z: Felt) -> Result<(Felt, Felt), SignatureError> {
69 let rng = &mut rand::thread_rng();
70 loop {
71 let k = Felt::random(rng);
72 match ecdsa_sign_k(sk, z, k) {
73 Ok(sig) => return Ok(sig),
74 Err(SignatureError::Randomness) => continue,
75 Err(e) => return Err(e),
76 }
77 }
78}
79
80pub fn ecdsa_sign_k(sk: Felt, z: Felt, k: Felt) -> Result<(Felt, Felt), SignatureError> {
87 let sk = CurveOrderMontFelt::try_from(sk).map_err(|_| SignatureError::SecretKey)?;
88 let z = CurveOrderMontFelt::try_from(z).map_err(|_| SignatureError::Message)?;
89 let k = CurveOrderMontFelt::try_from(k).map_err(|_| SignatureError::Randomness)?;
90 if k == CurveOrderMontFelt::ZERO {
91 return Err(SignatureError::Randomness);
92 }
93
94 let x = AffinePoint::gen_multiply_elm(k).x;
96 if x >= UPPER_BOUND {
97 return Err(SignatureError::Randomness);
98 }
99 let r = CurveOrderMontFelt::try_from(x).unwrap();
100
101 let kinv = k.inverse().unwrap();
103 let s = kinv * (z + r * sk);
104
105 let r = Felt::from(r);
106 let s = Felt::from(s);
107 Ok((r, s))
108}
109
110fn get_pk_point(pk: MontFelt) -> Option<AffinePoint> {
113 match AffinePoint::from_x(pk) {
114 Some(p) if !p.infinity => Some(p),
115 _ => None,
116 }
117}
118
119pub fn ecdsa_verify_partial(pk: Felt, z: Felt, r: Felt, s: Felt) -> Result<(), SignatureError> {
121 let montpk = MontFelt::from(pk);
122 let pk_point = get_pk_point(montpk).ok_or(SignatureError::PublicKey)?;
123 let pk_proj = ProjectivePoint::from(&pk_point);
124 ecdsa_verify_inner(pk_proj, z, r, s)
125}
126
127pub fn ecdsa_verify(pk: AffinePoint, z: Felt, r: Felt, s: Felt) -> Result<(), SignatureError> {
130 let pk_point = get_pk_point(pk.x).ok_or(SignatureError::PublicKey)?;
131 if pk_point.y != pk.y {
132 return Err(SignatureError::PublicKey);
133 }
134 let pk_proj = ProjectivePoint::from(&pk_point);
135 ecdsa_verify_inner(pk_proj, z, r, s)
136}
137
138pub fn ecdsa_verify_inner(
144 pk: ProjectivePoint,
145 z: Felt,
146 r: Felt,
147 s: Felt,
148) -> Result<(), SignatureError> {
149 let f_z = MontFelt::from(z);
151 let f_r = MontFelt::from(r);
152 let f_s = MontFelt::from(s);
153
154 let cf_z = CurveOrderMontFelt::try_from(f_z).unwrap();
155 let cf_r = CurveOrderMontFelt::try_from(f_r).unwrap();
156 let cf_s = CurveOrderMontFelt::try_from(f_s).unwrap();
157
158 if f_z >= UPPER_BOUND {
160 return Err(SignatureError::Message);
161 }
162 if f_r == MontFelt::ZERO || f_r >= UPPER_BOUND {
163 return Err(SignatureError::Signature);
164 }
165 if f_s == MontFelt::ZERO || f_s >= UPPER_BOUND {
166 return Err(SignatureError::Signature);
167 }
168
169 let cf_s = cf_s.inverse().unwrap();
171 let u1 = cf_z * cf_s;
172 let u2 = cf_r * cf_s;
173
174 let u1g = CURVE_G.multiply_elm(&u1);
176 let u2pk = pk.multiply_elm(&u2);
177 let r1 = {
178 let mut tmp = u1g.clone();
179 tmp.add(&u2pk);
180 AffinePoint::from(&tmp)
181 };
182 let r2 = {
183 let mut minus_u2pk = u2pk.clone();
184 minus_u2pk.negate();
185
186 let mut tmp = u1g;
187 tmp.add(&minus_u2pk);
188 AffinePoint::from(&tmp)
189 };
190
191 if r1.x == f_r || r2.x == f_r {
193 Ok(())
194 } else {
195 Err(SignatureError::Signature)
196 }
197}
198
199#[cfg(test)]
201mod tests {
202 use super::*;
203
204 fn felt_hex(x: &str) -> Felt {
205 Felt::from_hex_str(x).unwrap()
206 }
207 fn felm_hex(x: &str) -> MontFelt {
208 MontFelt::from(Felt::from_hex_str(x).unwrap())
209 }
210
211 #[test]
212 fn upper_bound() {
213 assert!(UPPER_BOUND <= crate::algebra::curve::CURVE_ORDER);
214 }
215
216 #[test]
217 fn test_get_pk() {
218 let sk = felt_hex("0x03c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc");
219
220 let pk_x = felm_hex("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43");
221 let pk_y = felm_hex("0x54d7beec5ec728223671c627557efc5c9a6508425dc6c900b7741bf60afec06");
222
223 let pk = get_pk(sk).expect("valid sk");
224 let pk_affine = AffinePoint::from_x(pk.into()).unwrap();
225
226 assert_eq!(pk_affine.x, pk_x);
227 assert_eq!(pk_affine.y, pk_y);
228 }
229
230 #[test]
231 fn from_x() {
232 let pk_x = felm_hex("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43");
233 let pk_y = felm_hex("0x54d7beec5ec728223671c627557efc5c9a6508425dc6c900b7741bf60afec06");
234 let pk_affine = AffinePoint::from_x(pk_x).unwrap();
235 assert_eq!(pk_affine.y, pk_y);
236 }
237
238 #[test]
239 fn get_partial_pk() {
240 let pk_x = felt_hex("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43");
241
242 let sk = felt_hex("0x03c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc");
243 let pk = get_pk(sk).unwrap();
244
245 assert_eq!(pk, pk_x);
246 }
247
248 #[test]
249 fn verify_partial() {
250 let sk = felt_hex("0x03c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc");
251 let msg = felt_hex("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f");
252 let sig = ecdsa_sign(sk, msg).expect("can sign");
253 let pk = get_pk(sk).expect("can get pk");
254 assert!(ecdsa_verify_partial(pk, msg, sig.0, sig.1).is_ok());
255 }
256
257 #[test]
258 fn verify_inner() {
259 let pk_x = felm_hex("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43");
261 let pk = ProjectivePoint::from_x(pk_x).unwrap();
262
263 let msg = felt_hex("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f");
264 let r = felt_hex("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882");
265 let w = felt_hex("0x1f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e");
266
267 let w = CurveOrderMontFelt::try_from(MontFelt::from(w)).unwrap();
269 let s = Felt::from(w.inverse().unwrap());
270
271 assert!(ecdsa_verify_inner(pk, msg, r, s).is_ok());
272 }
273
274 #[test]
275 fn verify_bad() {
276 let pk = felt_hex("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea44");
278 let msg = felt_hex("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f");
279 let r = felt_hex("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882");
280 let s = felt_hex("0x1f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e");
281 assert!(ecdsa_verify_partial(pk, msg, r, s).is_err());
282
283 let pk = felt_hex("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43");
285 let msg = felt_hex("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a30");
286 let r = felt_hex("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882");
287 let s = felt_hex("0x1f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e");
288 assert!(ecdsa_verify_partial(pk, msg, r, s).is_err());
289
290 let pk = felt_hex("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43");
292 let msg = felt_hex("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f");
293 let r = felt_hex("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e883");
294 let s = felt_hex("0x1f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e");
295 assert!(ecdsa_verify_partial(pk, msg, r, s).is_err());
296
297 let pk = felt_hex("0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43");
299 let msg = felt_hex("0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f");
300 let r = felt_hex("0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882");
301 let s = felt_hex("0x1f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286f");
302 assert!(ecdsa_verify_partial(pk, msg, r, s).is_err());
303 }
304
305 #[test]
306 fn sequencer() {
307 use crate::hash::poseidon::poseidon_hash_many;
310 let state_diff_commitment =
311 felm_hex("0x432e8e2ad833548e1c1077fc298991b055ba1e6f7a17dd332db98f4f428c56c");
312 let block_hash =
313 felm_hex("0x6f7342a680d7f99bdfdd859f587c75299e7ffabe62c071ded3a6d8a34cb132c");
314
315 let msg = Felt::from(poseidon_hash_many(&[block_hash, state_diff_commitment]));
316 let r = felt_hex("0x95e98f5b91d39ae2b1bf77447a4fc01725352ae8b0b2c0a3fe09d43d1d9e57");
317 let s = felt_hex("0x541b2db8dae6d5ae24b34e427d251edc2e94dcffddd85f207e1b51f2f4bb1ef");
318 let pk = felt_hex("0x48253ff2c3bed7af18bde0b611b083b39445959102d4947c51c4db6aa4f4e58");
319 assert!(ecdsa_verify_partial(pk, msg, r, s).is_ok());
320 }
321
322 #[test]
323 fn openzeppelin_signature() {
324 let pk = felt_hex("0x792c60ec4fdfea7ce6409db046b8dde11f595911cb74906be02a87ae6a4f70d");
326 let msg = felt_hex("0x0630a81628900577eea4b4a677767e57a56dabd730c2f64772ecbbde22ff485a");
327 let r = felt_hex("0x176846ea9b114f4f27f0d4d3cefacb5f830513fbd2d2f69a1a4d7552ae040be");
328 let s = felt_hex("0x601a87d6bc3e3a6513bafa1d449ffb3a0dd6cbe72f927a8e7271af0e8dd1302");
329 assert!(ecdsa_verify_partial(pk, msg, r, s).is_ok());
330 }
331}