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> {
98 if point.is_identity() {
101 Err(Error::new())
102 } else {
103 Ok(Self { point })
104 }
105 }
106
107 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self>
111 where
112 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
113 {
114 if bytes.is_empty() {
115 return Err(Error::new());
116 }
117
118 let tag = Tag::from_u8(bytes[0]).map_err(|_| Error::new())?;
120
121 let expected_len = tag.message_len(Coordinate::<C>::NUM_LIMBS);
123 if bytes.len() != expected_len {
124 return Err(Error::new());
125 }
126
127 match tag {
128 Tag::Identity => {
129 Err(Error::new())
131 }
132
133 Tag::CompressedEvenY | Tag::CompressedOddY => {
134 let x = Coordinate::<C>::from_be_bytes(&bytes[1..]).ok_or_else(Error::new)?;
135 let rec_id = bytes[0] & 1;
136 let point = FromCompressed::decompress(x, &rec_id).ok_or_else(Error::new)?;
137 Ok(Self { point })
139 }
140
141 Tag::Uncompressed => {
142 let (x_bytes, y_bytes) = bytes[1..].split_at(Coordinate::<C>::NUM_LIMBS);
143 let x = Coordinate::<C>::from_be_bytes(x_bytes).ok_or_else(Error::new)?;
144 let y = Coordinate::<C>::from_be_bytes(y_bytes).ok_or_else(Error::new)?;
145 let point =
146 unsafe { <C as IntrinsicCurve>::Point::from_xy(x, y).ok_or_else(Error::new)? };
147 Self::from_affine(point)
148 }
149
150 _ => Err(Error::new()),
151 }
152 }
153
154 pub fn to_sec1_bytes(&self, compress: bool) -> Vec<u8> {
155 if self.point.is_identity() {
156 return vec![0x00];
157 }
158
159 let (x, y) = self.point.clone().into_coords();
160
161 if compress {
162 let mut bytes = Vec::<u8>::with_capacity(1 + Coordinate::<C>::NUM_LIMBS);
163 let tag = if y.as_le_bytes()[0] & 1 == 1 {
164 Tag::CompressedOddY
165 } else {
166 Tag::CompressedEvenY
167 };
168 bytes.push(tag.into());
169 bytes.extend_from_slice(x.to_be_bytes().as_ref());
170 bytes
171 } else {
172 let mut bytes = Vec::<u8>::with_capacity(1 + Coordinate::<C>::NUM_LIMBS * 2);
173 bytes.push(Tag::Uncompressed.into());
174 bytes.extend_from_slice(x.to_be_bytes().as_ref());
175 bytes.extend_from_slice(y.to_be_bytes().as_ref());
176 bytes
177 }
178 }
179
180 pub fn as_affine(&self) -> &AffinePoint<C> {
181 &self.point
182 }
183
184 pub fn into_affine(self) -> AffinePoint<C> {
185 self.point
186 }
187}
188
189impl<C: IntrinsicCurve> VerifyingKey<C>
190where
191 C::Point: WeierstrassPoint + Group + FromCompressed<Coordinate<C>>,
192 Coordinate<C>: IntMod,
193 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
194{
195 pub fn new(public_key: PublicKey<C>) -> Self {
196 Self { inner: public_key }
197 }
198
199 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
200 let public_key = PublicKey::<C>::from_sec1_bytes(bytes)?;
201 Ok(Self::new(public_key))
202 }
203
204 pub fn from_affine(point: <C as IntrinsicCurve>::Point) -> Result<Self> {
205 let public_key = PublicKey::<C>::from_affine(point)?;
206 Ok(Self::new(public_key))
207 }
208
209 pub fn to_sec1_bytes(&self, compress: bool) -> Vec<u8> {
210 self.inner.to_sec1_bytes(compress)
211 }
212
213 pub fn as_affine(&self) -> &<C as IntrinsicCurve>::Point {
214 self.inner.as_affine()
215 }
216
217 pub fn into_affine(self) -> <C as IntrinsicCurve>::Point {
218 self.inner.into_affine()
219 }
220}
221
222impl<C> VerifyingKey<C>
224where
225 C: IntrinsicCurve + PrimeCurve,
226 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
227 Coordinate<C>: IntMod,
228 C::Scalar: IntMod + Reduce,
229 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
230 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
231 FieldBytesSize<C>: ModulusSize,
232 SignatureSize<C>: ArrayLength<u8>,
233{
234 pub fn recover_from_msg(
239 msg: &[u8],
240 signature: &Signature<C>,
241 recovery_id: RecoveryId,
242 ) -> Result<Self>
243 where
244 C: DigestPrimitive,
245 {
246 Self::recover_from_digest(C::Digest::new_with_prefix(msg), signature, recovery_id)
247 }
248
249 pub fn recover_from_digest<D>(
252 msg_digest: D,
253 signature: &Signature<C>,
254 recovery_id: RecoveryId,
255 ) -> Result<Self>
256 where
257 D: Digest,
258 {
259 Self::recover_from_prehash(&msg_digest.finalize(), signature, recovery_id)
260 }
261
262 pub fn recover_from_prehash(
266 prehash: &[u8],
267 signature: &Signature<C>,
268 recovery_id: RecoveryId,
269 ) -> Result<Self> {
270 let sig = signature.to_bytes();
271 let vk = Self::recover_from_prehash_noverify(prehash, &sig, recovery_id)?;
272 vk.inner.as_affine().verify_hook(prehash, signature)?;
273 Ok(vk)
274 }
275}
276
277pub trait VerifyCustomHook<C>: WeierstrassPoint
284where
285 C: IntrinsicCurve + PrimeCurve,
286 SignatureSize<C>: ArrayLength<u8>,
287{
288 fn verify_hook(&self, _z: &[u8], _sig: &Signature<C>) -> Result<()> {
298 Ok(())
299 }
300}
301
302impl<C, D> DigestVerifier<D, Signature<C>> for VerifyingKey<C>
307where
308 C: PrimeCurve + IntrinsicCurve,
309 D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
310 SignatureSize<C>: ArrayLength<u8>,
311 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
312 Coordinate<C>: IntMod,
313 <C as IntrinsicCurve>::Scalar: IntMod + Reduce,
314 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
315 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
316{
317 fn verify_digest(&self, msg_digest: D, signature: &Signature<C>) -> Result<()> {
318 PrehashVerifier::<Signature<C>>::verify_prehash(
319 self,
320 &msg_digest.finalize_fixed(),
321 signature,
322 )
323 }
324}
325
326impl<C> PrehashVerifier<Signature<C>> for VerifyingKey<C>
327where
328 C: PrimeCurve + IntrinsicCurve,
329 SignatureSize<C>: ArrayLength<u8>,
330 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
331 Coordinate<C>: IntMod,
332 C::Scalar: IntMod + Reduce,
333 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
334 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
335{
336 fn verify_prehash(&self, prehash: &[u8], signature: &Signature<C>) -> Result<()> {
337 self.inner.as_affine().verify_hook(prehash, signature)?;
338 verify_prehashed::<C>(
339 self.inner.as_affine().clone(),
340 prehash,
341 &signature.to_bytes(),
342 )
343 }
344}
345
346impl<C> Verifier<Signature<C>> for VerifyingKey<C>
347where
348 C: PrimeCurve + CurveArithmetic + DigestPrimitive + IntrinsicCurve,
349 SignatureSize<C>: ArrayLength<u8>,
350 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
351 Coordinate<C>: IntMod,
352 <C as IntrinsicCurve>::Scalar: IntMod + Reduce,
353 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
354 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
355{
356 fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<()> {
357 self.verify_digest(C::Digest::new_with_prefix(msg), signature)
358 }
359}
360
361impl<C> VerifyingKey<C>
365where
366 C: CurveArithmetic + IntrinsicCurve,
367 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C> + Default + ConditionallySelectable,
368 FieldBytesSize<C>: ModulusSize,
369{
370 pub fn from_encoded_point(public_key: &EncodedPoint<C>) -> Result<Self> {
372 Option::from(PublicKey::<C>::from_encoded_point(public_key))
373 .map(|public_key| Self { inner: public_key })
374 .ok_or_else(Error::new)
375 }
376
377 pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
380 self.inner.to_encoded_point(compress)
381 }
382}
383
384impl<C> FromEncodedPoint<C> for PublicKey<C>
388where
389 C: CurveArithmetic + IntrinsicCurve,
390 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C> + Default + ConditionallySelectable,
391 FieldBytesSize<C>: ModulusSize,
392{
393 fn from_encoded_point(encoded_point: &EncodedPoint<C>) -> CtOption<Self> {
395 AffinePoint::<C>::from_encoded_point(encoded_point).and_then(|point| {
396 let is_identity = Choice::from(u8::from(encoded_point.is_identity()));
398 CtOption::new(PublicKey { point }, !is_identity)
399 })
400 }
401}
402
403impl<C> ToEncodedPoint<C> for PublicKey<C>
404where
405 C: CurveArithmetic + IntrinsicCurve,
406 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
407 FieldBytesSize<C>: ModulusSize,
408{
409 fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
412 self.point.to_encoded_point(compress)
413 }
414}
415
416impl<C> VerifyingKey<C>
418where
419 C: IntrinsicCurve + PrimeCurve,
420 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>>,
421 Coordinate<C>: IntMod,
422 C::Scalar: IntMod + Reduce,
423{
424 #[allow(non_snake_case)]
433 pub fn recover_from_prehash_noverify(
434 prehash: &[u8],
435 sig: &[u8],
436 recovery_id: RecoveryId,
437 ) -> Result<Self>
438 where
439 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
440 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
441 {
442 assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
444 assert_eq!(sig.len(), <C as IntrinsicCurve>::Scalar::NUM_LIMBS * 2);
446 let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
448 let r = Scalar::<C>::from_be_bytes(r_be).ok_or_else(Error::new)?;
450 let s = Scalar::<C>::from_be_bytes(s_be).ok_or_else(Error::new)?;
451 if r == Scalar::<C>::ZERO || s == Scalar::<C>::ZERO {
452 return Err(Error::new());
453 }
454
455 let prehash_bytes = bits2field::<C>(prehash)?;
457 let trim = prehash_bytes.len().saturating_sub(Scalar::<C>::NUM_LIMBS);
459 let z = Scalar::<C>::from_be_bytes_unchecked(&prehash_bytes[..prehash_bytes.len() - trim]);
462
463 let mut r_bytes = {
468 let mut r_bytes = FieldBytes::<C>::default();
469 assert!(FieldBytesSize::<C>::USIZE >= Scalar::<C>::NUM_LIMBS);
470 let offset = r_bytes.len().saturating_sub(r_be.len());
471 r_bytes[offset..].copy_from_slice(r_be);
472 r_bytes
473 };
474 if recovery_id.is_x_reduced() {
475 match Option::<C::Uint>::from(
476 C::Uint::decode_field_bytes(&r_bytes).checked_add(&C::ORDER),
477 ) {
478 Some(restored) => r_bytes = restored.encode_field_bytes(),
479 None => {
481 return Err(Error::new());
482 }
483 };
484 }
485 assert!(FieldBytesSize::<C>::USIZE <= Coordinate::<C>::NUM_LIMBS);
486 let x = Coordinate::<C>::from_be_bytes(&r_bytes).ok_or_else(Error::new)?;
487 let rec_id = recovery_id.to_byte();
488 let R: C::Point = FromCompressed::decompress(x, &rec_id).ok_or_else(Error::new)?;
490
491 let neg_u1 = z.div_unsafe(&r);
492 let u2 = s.div_unsafe(&r);
493 let NEG_G = C::Point::NEG_GENERATOR;
494 let point = <C as IntrinsicCurve>::msm(&[neg_u1, u2], &[NEG_G, R]);
495 let vk = VerifyingKey::from_affine(point)?;
496
497 Ok(vk)
498 }
499}
500
501#[allow(non_snake_case)]
504pub fn verify_prehashed<C>(pubkey: AffinePoint<C>, prehash: &[u8], sig: &[u8]) -> Result<()>
505where
506 C: IntrinsicCurve + PrimeCurve,
507 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>>,
508 Coordinate<C>: IntMod,
509 C::Scalar: IntMod + Reduce,
510 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
511 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
512{
513 assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
515 assert_eq!(sig.len(), Scalar::<C>::NUM_LIMBS * 2);
517 let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
519 let r = Scalar::<C>::from_be_bytes(r_be).ok_or_else(Error::new)?;
521 let s = Scalar::<C>::from_be_bytes(s_be).ok_or_else(Error::new)?;
522 if r == Scalar::<C>::ZERO || s == Scalar::<C>::ZERO {
523 return Err(Error::new());
524 }
525
526 let prehash_bytes = bits2field::<C>(prehash)?;
528 let trim = prehash_bytes.len().saturating_sub(Scalar::<C>::NUM_LIMBS);
530 let z = Scalar::<C>::from_be_bytes_unchecked(&prehash_bytes[..prehash_bytes.len() - trim]);
533
534 let u1 = z.div_unsafe(&s);
535 let u2 = (&r).div_unsafe(&s);
536
537 let G = C::Point::GENERATOR;
538 let Q = pubkey;
540 let R = <C as IntrinsicCurve>::msm(&[u1, u2], &[G, Q]);
541 if R.is_identity() {
544 return Err(Error::new());
545 }
546 let (x_1, _) = R.into_coords();
547 let x_mod_n = Scalar::<C>::reduce_le_bytes(x_1.as_le_bytes());
549 if x_mod_n == r {
550 Ok(())
551 } else {
552 Err(Error::new())
553 }
554}
555
556impl<C: IntrinsicCurve> AsRef<AffinePoint<C>> for VerifyingKey<C> {
557 fn as_ref(&self) -> &AffinePoint<C> {
558 &self.inner.point
559 }
560}
561
562impl<C: IntrinsicCurve> AsRef<AffinePoint<C>> for PublicKey<C> {
563 fn as_ref(&self) -> &AffinePoint<C> {
564 &self.point
565 }
566}