1use alloc::vec::Vec;
2use core::ops::{Add, Mul};
3
4use ecdsa_core::{
5 self,
6 hazmat::{bits2field, DigestPrimitive},
7 signature::{
8 digest::{Digest, FixedOutput},
9 hazmat::PrehashVerifier,
10 DigestVerifier, Verifier,
11 },
12 EncodedPoint, Error, RecoveryId, Result, Signature, SignatureSize,
13};
14use elliptic_curve::{
15 bigint::CheckedAdd,
16 generic_array::{typenum::Unsigned, ArrayLength},
17 sec1::{FromEncodedPoint, ModulusSize, Tag, ToEncodedPoint},
18 subtle::{Choice, ConditionallySelectable, CtOption},
19 CurveArithmetic, FieldBytes, FieldBytesEncoding, FieldBytesSize, PrimeCurve,
20};
21use openvm_algebra_guest::{DivUnsafe, IntMod, Reduce};
22
23use crate::{
24 weierstrass::{FromCompressed, IntrinsicCurve, WeierstrassPoint},
25 CyclicGroup, Group,
26};
27
28type Coordinate<C> = <<C as IntrinsicCurve>::Point as WeierstrassPoint>::Coordinate;
29type Scalar<C> = <C as IntrinsicCurve>::Scalar;
30type AffinePoint<C> = <C as IntrinsicCurve>::Point;
31
32#[derive(Clone)]
39pub struct SigningKey<C: IntrinsicCurve> {
40 #[allow(dead_code)]
42 secret_scalar: NonZeroScalar<C>,
43
44 verifying_key: VerifyingKey<C>,
46}
47
48#[allow(dead_code)]
49#[derive(Clone)]
50pub struct NonZeroScalar<C: IntrinsicCurve> {
51 scalar: Scalar<C>,
52}
53
54impl<C: IntrinsicCurve> SigningKey<C> {
55 pub fn from_slice(_bytes: &[u8]) -> Result<Self> {
56 todo!("signing is not yet implemented")
57 }
58
59 pub fn verifying_key(&self) -> &VerifyingKey<C> {
60 &self.verifying_key
61 }
62}
63
64impl<C> SigningKey<C>
65where
66 C: IntrinsicCurve + PrimeCurve,
67{
68 pub fn sign_prehash_recoverable(&self, _prehash: &[u8]) -> Result<(Signature<C>, RecoveryId)> {
69 todo!("signing is not yet implemented")
70 }
71}
72
73#[repr(C)]
76#[derive(Clone)]
77pub struct VerifyingKey<C: IntrinsicCurve> {
78 pub(crate) inner: PublicKey<C>,
79}
80
81#[repr(C)]
83#[derive(Clone)]
84pub struct PublicKey<C: IntrinsicCurve> {
85 point: AffinePoint<C>,
87}
88
89impl<C: IntrinsicCurve> PublicKey<C>
90where
91 C::Point: WeierstrassPoint + Group + FromCompressed<Coordinate<C>>,
92 Coordinate<C>: IntMod,
93{
94 pub fn from_affine(point: AffinePoint<C>) -> Result<Self> {
101 if point.is_identity() {
104 Err(Error::new())
105 } else {
106 Ok(Self { point })
107 }
108 }
109
110 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self>
114 where
115 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
116 {
117 if bytes.is_empty() {
118 return Err(Error::new());
119 }
120
121 let tag = Tag::from_u8(bytes[0]).map_err(|_| Error::new())?;
123
124 let expected_len = tag.message_len(Coordinate::<C>::NUM_LIMBS);
126 if bytes.len() != expected_len {
127 return Err(Error::new());
128 }
129
130 match tag {
131 Tag::Identity => {
132 Err(Error::new())
134 }
135
136 Tag::CompressedEvenY | Tag::CompressedOddY => {
137 let x = Coordinate::<C>::from_be_bytes(&bytes[1..]).ok_or_else(Error::new)?;
138 let rec_id = bytes[0] & 1;
139 let point = FromCompressed::decompress(x, &rec_id).ok_or_else(Error::new)?;
140 Ok(Self { point })
142 }
143
144 Tag::Uncompressed => {
145 let (x_bytes, y_bytes) = bytes[1..].split_at(Coordinate::<C>::NUM_LIMBS);
146 let x = Coordinate::<C>::from_be_bytes(x_bytes).ok_or_else(Error::new)?;
147 let y = Coordinate::<C>::from_be_bytes(y_bytes).ok_or_else(Error::new)?;
148 let point =
149 unsafe { <C as IntrinsicCurve>::Point::from_xy(x, y).ok_or_else(Error::new)? };
150 Self::from_affine(point)
151 }
152
153 _ => Err(Error::new()),
154 }
155 }
156
157 pub fn to_sec1_bytes(&self, compress: bool) -> Vec<u8> {
158 if self.point.is_identity() {
159 return vec![0x00];
160 }
161
162 let (x, y) = self.point.clone().into_coords();
163
164 if compress {
165 let mut bytes = Vec::<u8>::with_capacity(1 + Coordinate::<C>::NUM_LIMBS);
166 let tag = if y.as_le_bytes()[0] & 1 == 1 {
167 Tag::CompressedOddY
168 } else {
169 Tag::CompressedEvenY
170 };
171 bytes.push(tag.into());
172 bytes.extend_from_slice(x.to_be_bytes().as_ref());
173 bytes
174 } else {
175 let mut bytes = Vec::<u8>::with_capacity(1 + Coordinate::<C>::NUM_LIMBS * 2);
176 bytes.push(Tag::Uncompressed.into());
177 bytes.extend_from_slice(x.to_be_bytes().as_ref());
178 bytes.extend_from_slice(y.to_be_bytes().as_ref());
179 bytes
180 }
181 }
182
183 pub fn as_affine(&self) -> &AffinePoint<C> {
184 &self.point
185 }
186
187 pub fn into_affine(self) -> AffinePoint<C> {
188 self.point
189 }
190}
191
192impl<C: IntrinsicCurve> VerifyingKey<C>
193where
194 C::Point: WeierstrassPoint + Group + FromCompressed<Coordinate<C>>,
195 Coordinate<C>: IntMod,
196 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
197{
198 pub fn new(public_key: PublicKey<C>) -> Self {
199 Self { inner: public_key }
200 }
201
202 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
203 let public_key = PublicKey::<C>::from_sec1_bytes(bytes)?;
204 Ok(Self::new(public_key))
205 }
206
207 pub fn from_affine(point: <C as IntrinsicCurve>::Point) -> Result<Self> {
211 let public_key = PublicKey::<C>::from_affine(point)?;
212 Ok(Self::new(public_key))
213 }
214
215 pub fn to_sec1_bytes(&self, compress: bool) -> Vec<u8> {
216 self.inner.to_sec1_bytes(compress)
217 }
218
219 pub fn as_affine(&self) -> &<C as IntrinsicCurve>::Point {
220 self.inner.as_affine()
221 }
222
223 pub fn into_affine(self) -> <C as IntrinsicCurve>::Point {
224 self.inner.into_affine()
225 }
226}
227
228impl<C> VerifyingKey<C>
230where
231 C: IntrinsicCurve + PrimeCurve,
232 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
233 Coordinate<C>: IntMod,
234 C::Scalar: IntMod + Reduce,
235 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
236 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
237 FieldBytesSize<C>: ModulusSize,
238 SignatureSize<C>: ArrayLength<u8>,
239{
240 pub fn recover_from_msg(
245 msg: &[u8],
246 signature: &Signature<C>,
247 recovery_id: RecoveryId,
248 ) -> Result<Self>
249 where
250 C: DigestPrimitive,
251 {
252 Self::recover_from_digest(C::Digest::new_with_prefix(msg), signature, recovery_id)
253 }
254
255 pub fn recover_from_digest<D>(
258 msg_digest: D,
259 signature: &Signature<C>,
260 recovery_id: RecoveryId,
261 ) -> Result<Self>
262 where
263 D: Digest,
264 {
265 Self::recover_from_prehash(&msg_digest.finalize(), signature, recovery_id)
266 }
267
268 pub fn recover_from_prehash(
272 prehash: &[u8],
273 signature: &Signature<C>,
274 recovery_id: RecoveryId,
275 ) -> Result<Self> {
276 let sig = signature.to_bytes();
277 let vk = Self::recover_from_prehash_noverify(prehash, &sig, recovery_id)?;
278 vk.inner.as_affine().verify_hook(prehash, signature)?;
279 Ok(vk)
280 }
281}
282
283pub trait VerifyCustomHook<C>: WeierstrassPoint
290where
291 C: IntrinsicCurve + PrimeCurve,
292 SignatureSize<C>: ArrayLength<u8>,
293{
294 fn verify_hook(&self, _z: &[u8], _sig: &Signature<C>) -> Result<()> {
304 Ok(())
305 }
306}
307
308impl<C, D> DigestVerifier<D, Signature<C>> for VerifyingKey<C>
313where
314 C: PrimeCurve + IntrinsicCurve,
315 D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
316 SignatureSize<C>: ArrayLength<u8>,
317 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
318 Coordinate<C>: IntMod,
319 <C as IntrinsicCurve>::Scalar: IntMod + Reduce,
320 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
321 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
322{
323 fn verify_digest(&self, msg_digest: D, signature: &Signature<C>) -> Result<()> {
324 PrehashVerifier::<Signature<C>>::verify_prehash(
325 self,
326 &msg_digest.finalize_fixed(),
327 signature,
328 )
329 }
330}
331
332impl<C> PrehashVerifier<Signature<C>> for VerifyingKey<C>
333where
334 C: PrimeCurve + IntrinsicCurve,
335 SignatureSize<C>: ArrayLength<u8>,
336 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
337 Coordinate<C>: IntMod,
338 C::Scalar: IntMod + Reduce,
339 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
340 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
341{
342 fn verify_prehash(&self, prehash: &[u8], signature: &Signature<C>) -> Result<()> {
343 self.inner.as_affine().verify_hook(prehash, signature)?;
344 verify_prehashed::<C>(
345 self.inner.as_affine().clone(),
346 prehash,
347 &signature.to_bytes(),
348 )
349 }
350}
351
352impl<C> Verifier<Signature<C>> for VerifyingKey<C>
353where
354 C: PrimeCurve + CurveArithmetic + DigestPrimitive + IntrinsicCurve,
355 SignatureSize<C>: ArrayLength<u8>,
356 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
357 Coordinate<C>: IntMod,
358 <C as IntrinsicCurve>::Scalar: IntMod + Reduce,
359 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
360 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
361{
362 fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<()> {
363 self.verify_digest(C::Digest::new_with_prefix(msg), signature)
364 }
365}
366
367impl<C> VerifyingKey<C>
371where
372 C: CurveArithmetic + IntrinsicCurve,
373 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C> + Default + ConditionallySelectable,
374 FieldBytesSize<C>: ModulusSize,
375{
376 pub fn from_encoded_point(public_key: &EncodedPoint<C>) -> Result<Self> {
378 Option::from(PublicKey::<C>::from_encoded_point(public_key))
379 .map(|public_key| Self { inner: public_key })
380 .ok_or_else(Error::new)
381 }
382
383 pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
386 self.inner.to_encoded_point(compress)
387 }
388}
389
390impl<C> FromEncodedPoint<C> for PublicKey<C>
394where
395 C: CurveArithmetic + IntrinsicCurve,
396 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C> + Default + ConditionallySelectable,
397 FieldBytesSize<C>: ModulusSize,
398{
399 fn from_encoded_point(encoded_point: &EncodedPoint<C>) -> CtOption<Self> {
401 AffinePoint::<C>::from_encoded_point(encoded_point).and_then(|point| {
402 let is_identity = Choice::from(u8::from(encoded_point.is_identity()));
404 CtOption::new(PublicKey { point }, !is_identity)
405 })
406 }
407}
408
409impl<C> ToEncodedPoint<C> for PublicKey<C>
410where
411 C: CurveArithmetic + IntrinsicCurve,
412 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
413 FieldBytesSize<C>: ModulusSize,
414{
415 fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
418 self.point.to_encoded_point(compress)
419 }
420}
421
422impl<C> VerifyingKey<C>
424where
425 C: IntrinsicCurve + PrimeCurve,
426 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>>,
427 Coordinate<C>: IntMod,
428 C::Scalar: IntMod + Reduce,
429{
430 #[allow(non_snake_case)]
439 pub fn recover_from_prehash_noverify(
440 prehash: &[u8],
441 sig: &[u8],
442 recovery_id: RecoveryId,
443 ) -> Result<Self>
444 where
445 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
446 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
447 {
448 assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
450 if sig.len() != <C as IntrinsicCurve>::Scalar::NUM_LIMBS * 2 {
452 return Err(Error::new());
453 }
454 let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
456 let r = Scalar::<C>::from_be_bytes(r_be).ok_or_else(Error::new)?;
458 let s = Scalar::<C>::from_be_bytes(s_be).ok_or_else(Error::new)?;
459 if r == Scalar::<C>::ZERO || s == Scalar::<C>::ZERO {
460 return Err(Error::new());
461 }
462
463 let prehash_bytes = bits2field::<C>(prehash)?;
465 let trim = prehash_bytes.len().saturating_sub(Scalar::<C>::NUM_LIMBS);
467 let z = Scalar::<C>::from_be_bytes_unchecked(&prehash_bytes[..prehash_bytes.len() - trim]);
470
471 let mut r_bytes = {
476 let mut r_bytes = FieldBytes::<C>::default();
477 assert!(FieldBytesSize::<C>::USIZE >= Scalar::<C>::NUM_LIMBS);
478 let offset = r_bytes.len().saturating_sub(r_be.len());
479 r_bytes[offset..].copy_from_slice(r_be);
480 r_bytes
481 };
482 if recovery_id.is_x_reduced() {
483 match Option::<C::Uint>::from(
484 C::Uint::decode_field_bytes(&r_bytes).checked_add(&C::ORDER),
485 ) {
486 Some(restored) => r_bytes = restored.encode_field_bytes(),
487 None => {
489 return Err(Error::new());
490 }
491 };
492 }
493 assert!(FieldBytesSize::<C>::USIZE <= Coordinate::<C>::NUM_LIMBS);
494 let x = Coordinate::<C>::from_be_bytes(&r_bytes).ok_or_else(Error::new)?;
495 let rec_id = recovery_id.to_byte();
496 let R: C::Point = FromCompressed::decompress(x, &rec_id).ok_or_else(Error::new)?;
498
499 let neg_u1 = z.div_unsafe(&r);
500 let u2 = s.div_unsafe(&r);
501 let NEG_G = C::Point::NEG_GENERATOR;
502 let point = <C as IntrinsicCurve>::msm(&[neg_u1, u2], &[NEG_G, R]);
503 let vk = VerifyingKey::from_affine(point)?;
504
505 Ok(vk)
506 }
507}
508
509#[allow(non_snake_case)]
512pub fn verify_prehashed<C>(pubkey: AffinePoint<C>, prehash: &[u8], sig: &[u8]) -> Result<()>
513where
514 C: IntrinsicCurve + PrimeCurve,
515 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>>,
516 Coordinate<C>: IntMod,
517 C::Scalar: IntMod + Reduce,
518 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
519 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
520{
521 if pubkey.is_identity() {
522 return Err(Error::new());
523 }
524
525 assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
527 if sig.len() != Scalar::<C>::NUM_LIMBS * 2 {
529 return Err(Error::new());
530 }
531 let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
533 let r = Scalar::<C>::from_be_bytes(r_be).ok_or_else(Error::new)?;
535 let s = Scalar::<C>::from_be_bytes(s_be).ok_or_else(Error::new)?;
536 if r == Scalar::<C>::ZERO || s == Scalar::<C>::ZERO {
537 return Err(Error::new());
538 }
539
540 let prehash_bytes = bits2field::<C>(prehash)?;
542 let trim = prehash_bytes.len().saturating_sub(Scalar::<C>::NUM_LIMBS);
544 let z = Scalar::<C>::from_be_bytes_unchecked(&prehash_bytes[..prehash_bytes.len() - trim]);
547
548 let u1 = z.div_unsafe(&s);
549 let u2 = (&r).div_unsafe(&s);
550
551 let G = C::Point::GENERATOR;
552 let Q = pubkey;
554 let R = <C as IntrinsicCurve>::msm(&[u1, u2], &[G, Q]);
555 if R.is_identity() {
558 return Err(Error::new());
559 }
560 let (x_1, _) = R.into_coords();
561 let x_mod_n = Scalar::<C>::reduce_le_bytes(x_1.as_le_bytes());
563 if x_mod_n == r {
564 Ok(())
565 } else {
566 Err(Error::new())
567 }
568}
569
570impl<C: IntrinsicCurve> AsRef<AffinePoint<C>> for VerifyingKey<C> {
571 fn as_ref(&self) -> &AffinePoint<C> {
572 &self.inner.point
573 }
574}
575
576impl<C: IntrinsicCurve> AsRef<AffinePoint<C>> for PublicKey<C> {
577 fn as_ref(&self) -> &AffinePoint<C> {
578 &self.point
579 }
580}