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>
108 where
109 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
110 {
111 if bytes.is_empty() {
112 return Err(Error::new());
113 }
114
115 let tag = Tag::from_u8(bytes[0]).unwrap();
117
118 let expected_len = tag.message_len(Coordinate::<C>::NUM_LIMBS);
120 if bytes.len() != expected_len {
121 return Err(Error::new());
122 }
123
124 match tag {
125 Tag::Identity => {
126 let point = <<C as IntrinsicCurve>::Point as WeierstrassPoint>::IDENTITY;
127 Ok(Self { point })
128 }
129
130 Tag::CompressedEvenY | Tag::CompressedOddY => {
131 let x = Coordinate::<C>::from_be_bytes(&bytes[1..]).ok_or_else(Error::new)?;
132 let rec_id = bytes[0] & 1;
133 let point = FromCompressed::decompress(x, &rec_id).ok_or_else(Error::new)?;
134 Ok(Self { point })
136 }
137
138 Tag::Uncompressed => {
139 let (x_bytes, y_bytes) = bytes[1..].split_at(Coordinate::<C>::NUM_LIMBS);
140 let x = Coordinate::<C>::from_be_bytes(x_bytes).ok_or_else(Error::new)?;
141 let y = Coordinate::<C>::from_be_bytes(y_bytes).ok_or_else(Error::new)?;
142 let point = <C as IntrinsicCurve>::Point::from_xy(x, y).ok_or_else(Error::new)?;
143 Self::from_affine(point)
144 }
145
146 _ => Err(Error::new()),
147 }
148 }
149
150 pub fn to_sec1_bytes(&self, compress: bool) -> Vec<u8> {
151 if self.point.is_identity() {
152 return vec![0x00];
153 }
154
155 let (x, y) = self.point.clone().into_coords();
156
157 if compress {
158 let mut bytes = Vec::<u8>::with_capacity(1 + Coordinate::<C>::NUM_LIMBS);
159 let tag = if y.as_le_bytes()[0] & 1 == 1 {
160 Tag::CompressedOddY
161 } else {
162 Tag::CompressedEvenY
163 };
164 bytes.push(tag.into());
165 bytes.extend_from_slice(x.to_be_bytes().as_ref());
166 bytes
167 } else {
168 let mut bytes = Vec::<u8>::with_capacity(1 + Coordinate::<C>::NUM_LIMBS * 2);
169 bytes.push(Tag::Uncompressed.into());
170 bytes.extend_from_slice(x.to_be_bytes().as_ref());
171 bytes.extend_from_slice(y.to_be_bytes().as_ref());
172 bytes
173 }
174 }
175
176 pub fn as_affine(&self) -> &AffinePoint<C> {
177 &self.point
178 }
179
180 pub fn into_affine(self) -> AffinePoint<C> {
181 self.point
182 }
183}
184
185impl<C: IntrinsicCurve> VerifyingKey<C>
186where
187 C::Point: WeierstrassPoint + Group + FromCompressed<Coordinate<C>>,
188 Coordinate<C>: IntMod,
189 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
190{
191 pub fn new(public_key: PublicKey<C>) -> Self {
192 Self { inner: public_key }
193 }
194
195 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
196 let public_key = PublicKey::<C>::from_sec1_bytes(bytes)?;
197 Ok(Self::new(public_key))
198 }
199
200 pub fn from_affine(point: <C as IntrinsicCurve>::Point) -> Result<Self> {
201 let public_key = PublicKey::<C>::from_affine(point)?;
202 Ok(Self::new(public_key))
203 }
204
205 pub fn to_sec1_bytes(&self, compress: bool) -> Vec<u8> {
206 self.inner.to_sec1_bytes(compress)
207 }
208
209 pub fn as_affine(&self) -> &<C as IntrinsicCurve>::Point {
210 self.inner.as_affine()
211 }
212
213 pub fn into_affine(self) -> <C as IntrinsicCurve>::Point {
214 self.inner.into_affine()
215 }
216}
217
218impl<C> VerifyingKey<C>
220where
221 C: IntrinsicCurve + PrimeCurve,
222 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
223 Coordinate<C>: IntMod,
224 C::Scalar: IntMod + Reduce,
225 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
226 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
227 FieldBytesSize<C>: ModulusSize,
228 SignatureSize<C>: ArrayLength<u8>,
229{
230 pub fn recover_from_msg(
235 msg: &[u8],
236 signature: &Signature<C>,
237 recovery_id: RecoveryId,
238 ) -> Result<Self>
239 where
240 C: DigestPrimitive,
241 {
242 Self::recover_from_digest(C::Digest::new_with_prefix(msg), signature, recovery_id)
243 }
244
245 pub fn recover_from_digest<D>(
248 msg_digest: D,
249 signature: &Signature<C>,
250 recovery_id: RecoveryId,
251 ) -> Result<Self>
252 where
253 D: Digest,
254 {
255 Self::recover_from_prehash(&msg_digest.finalize(), signature, recovery_id)
256 }
257
258 pub fn recover_from_prehash(
262 prehash: &[u8],
263 signature: &Signature<C>,
264 recovery_id: RecoveryId,
265 ) -> Result<Self> {
266 let sig = signature.to_bytes();
267 let vk = Self::recover_from_prehash_noverify(prehash, &sig, recovery_id)?;
268 vk.inner.as_affine().verify_hook(prehash, signature)?;
269 Ok(vk)
270 }
271}
272
273pub trait VerifyCustomHook<C>: WeierstrassPoint
280where
281 C: IntrinsicCurve + PrimeCurve,
282 SignatureSize<C>: ArrayLength<u8>,
283{
284 fn verify_hook(&self, _z: &[u8], _sig: &Signature<C>) -> Result<()> {
294 Ok(())
295 }
296}
297
298impl<C, D> DigestVerifier<D, Signature<C>> for VerifyingKey<C>
303where
304 C: PrimeCurve + IntrinsicCurve,
305 D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
306 SignatureSize<C>: ArrayLength<u8>,
307 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
308 Coordinate<C>: IntMod,
309 <C as IntrinsicCurve>::Scalar: IntMod + Reduce,
310 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
311 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
312{
313 fn verify_digest(&self, msg_digest: D, signature: &Signature<C>) -> Result<()> {
314 PrehashVerifier::<Signature<C>>::verify_prehash(
315 self,
316 &msg_digest.finalize_fixed(),
317 signature,
318 )
319 }
320}
321
322impl<C> PrehashVerifier<Signature<C>> for VerifyingKey<C>
323where
324 C: PrimeCurve + IntrinsicCurve,
325 SignatureSize<C>: ArrayLength<u8>,
326 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
327 Coordinate<C>: IntMod,
328 C::Scalar: IntMod + Reduce,
329 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
330 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
331{
332 fn verify_prehash(&self, prehash: &[u8], signature: &Signature<C>) -> Result<()> {
333 self.inner.as_affine().verify_hook(prehash, signature)?;
334 verify_prehashed::<C>(
335 self.inner.as_affine().clone(),
336 prehash,
337 &signature.to_bytes(),
338 )
339 }
340}
341
342impl<C> Verifier<Signature<C>> for VerifyingKey<C>
343where
344 C: PrimeCurve + CurveArithmetic + DigestPrimitive + IntrinsicCurve,
345 SignatureSize<C>: ArrayLength<u8>,
346 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>> + VerifyCustomHook<C>,
347 Coordinate<C>: IntMod,
348 <C as IntrinsicCurve>::Scalar: IntMod + Reduce,
349 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
350 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
351{
352 fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<()> {
353 self.verify_digest(C::Digest::new_with_prefix(msg), signature)
354 }
355}
356
357impl<C> VerifyingKey<C>
361where
362 C: CurveArithmetic + IntrinsicCurve,
363 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C> + Default + ConditionallySelectable,
364 FieldBytesSize<C>: ModulusSize,
365{
366 pub fn from_encoded_point(public_key: &EncodedPoint<C>) -> Result<Self> {
368 Option::from(PublicKey::<C>::from_encoded_point(public_key))
369 .map(|public_key| Self { inner: public_key })
370 .ok_or_else(Error::new)
371 }
372
373 pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
376 self.inner.to_encoded_point(compress)
377 }
378}
379
380impl<C> FromEncodedPoint<C> for PublicKey<C>
384where
385 C: CurveArithmetic + IntrinsicCurve,
386 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C> + Default + ConditionallySelectable,
387 FieldBytesSize<C>: ModulusSize,
388{
389 fn from_encoded_point(encoded_point: &EncodedPoint<C>) -> CtOption<Self> {
391 AffinePoint::<C>::from_encoded_point(encoded_point).and_then(|point| {
392 let is_identity = Choice::from(u8::from(encoded_point.is_identity()));
394 CtOption::new(PublicKey { point }, !is_identity)
395 })
396 }
397}
398
399impl<C> ToEncodedPoint<C> for PublicKey<C>
400where
401 C: CurveArithmetic + IntrinsicCurve,
402 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
403 FieldBytesSize<C>: ModulusSize,
404{
405 fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
408 self.point.to_encoded_point(compress)
409 }
410}
411
412impl<C> VerifyingKey<C>
414where
415 C: IntrinsicCurve + PrimeCurve,
416 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>>,
417 Coordinate<C>: IntMod,
418 C::Scalar: IntMod + Reduce,
419{
420 #[allow(non_snake_case)]
429 pub fn recover_from_prehash_noverify(
430 prehash: &[u8],
431 sig: &[u8],
432 recovery_id: RecoveryId,
433 ) -> Result<Self>
434 where
435 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
436 for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
437 {
438 assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
440 assert_eq!(sig.len(), <C as IntrinsicCurve>::Scalar::NUM_LIMBS * 2);
442 let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
444 let r = Scalar::<C>::from_be_bytes(r_be).ok_or_else(Error::new)?;
446 let s = Scalar::<C>::from_be_bytes(s_be).ok_or_else(Error::new)?;
447 if r == Scalar::<C>::ZERO || s == Scalar::<C>::ZERO {
448 return Err(Error::new());
449 }
450
451 let prehash_bytes = bits2field::<C>(prehash)?;
453 let trim = prehash_bytes.len().saturating_sub(Scalar::<C>::NUM_LIMBS);
455 let z = Scalar::<C>::from_be_bytes_unchecked(&prehash_bytes[..prehash_bytes.len() - trim]);
458
459 let mut r_bytes = {
464 let mut r_bytes = FieldBytes::<C>::default();
465 assert!(FieldBytesSize::<C>::USIZE >= Scalar::<C>::NUM_LIMBS);
466 let offset = r_bytes.len().saturating_sub(r_be.len());
467 r_bytes[offset..].copy_from_slice(r_be);
468 r_bytes
469 };
470 if recovery_id.is_x_reduced() {
471 match Option::<C::Uint>::from(
472 C::Uint::decode_field_bytes(&r_bytes).checked_add(&C::ORDER),
473 ) {
474 Some(restored) => r_bytes = restored.encode_field_bytes(),
475 None => {
477 return Err(Error::new());
478 }
479 };
480 }
481 assert!(FieldBytesSize::<C>::USIZE <= Coordinate::<C>::NUM_LIMBS);
482 let x = Coordinate::<C>::from_be_bytes(&r_bytes).ok_or_else(Error::new)?;
483 let rec_id = recovery_id.to_byte();
484 let R: C::Point = FromCompressed::decompress(x, &rec_id).ok_or_else(Error::new)?;
486
487 let neg_u1 = z.div_unsafe(&r);
488 let u2 = s.div_unsafe(&r);
489 let NEG_G = C::Point::NEG_GENERATOR;
490 let point = <C as IntrinsicCurve>::msm(&[neg_u1, u2], &[NEG_G, R]);
491 let vk = VerifyingKey::from_affine(point)?;
492
493 Ok(vk)
494 }
495}
496
497#[allow(non_snake_case)]
500pub fn verify_prehashed<C>(pubkey: AffinePoint<C>, prehash: &[u8], sig: &[u8]) -> Result<()>
501where
502 C: IntrinsicCurve + PrimeCurve,
503 C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>>,
504 Coordinate<C>: IntMod,
505 C::Scalar: IntMod + Reduce,
506 for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
507 for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
508{
509 assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
511 assert_eq!(sig.len(), Scalar::<C>::NUM_LIMBS * 2);
513 let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
515 let r = Scalar::<C>::from_be_bytes(r_be).ok_or_else(Error::new)?;
517 let s = Scalar::<C>::from_be_bytes(s_be).ok_or_else(Error::new)?;
518 if r == Scalar::<C>::ZERO || s == Scalar::<C>::ZERO {
519 return Err(Error::new());
520 }
521
522 let prehash_bytes = bits2field::<C>(prehash)?;
524 let trim = prehash_bytes.len().saturating_sub(Scalar::<C>::NUM_LIMBS);
526 let z = Scalar::<C>::from_be_bytes_unchecked(&prehash_bytes[..prehash_bytes.len() - trim]);
529
530 let u1 = z.div_unsafe(&s);
531 let u2 = (&r).div_unsafe(&s);
532
533 let G = C::Point::GENERATOR;
534 let Q = pubkey;
536 let R = <C as IntrinsicCurve>::msm(&[u1, u2], &[G, Q]);
537 if R.is_identity() {
540 return Err(Error::new());
541 }
542 let (x_1, _) = R.into_coords();
543 let x_mod_n = Scalar::<C>::reduce_le_bytes(x_1.as_le_bytes());
545 if x_mod_n == r {
546 Ok(())
547 } else {
548 Err(Error::new())
549 }
550}
551
552impl<C: IntrinsicCurve> AsRef<AffinePoint<C>> for VerifyingKey<C> {
553 fn as_ref(&self) -> &AffinePoint<C> {
554 &self.inner.point
555 }
556}
557
558impl<C: IntrinsicCurve> AsRef<AffinePoint<C>> for PublicKey<C> {
559 fn as_ref(&self) -> &AffinePoint<C> {
560 &self.point
561 }
562}