openvm_ecc_guest/
ecdsa.rsuse core::ops::{Add, AddAssign, Mul};
use ecdsa::{self, hazmat::bits2field, Error, RecoveryId, Result};
use elliptic_curve::PrimeCurve;
use openvm_algebra_guest::{DivUnsafe, IntMod, Reduce};
use crate::{
weierstrass::{FromCompressed, IntrinsicCurve, WeierstrassPoint},
CyclicGroup, Group,
};
pub type Coordinate<C> = <<C as IntrinsicCurve>::Point as WeierstrassPoint>::Coordinate;
pub type Scalar<C> = <C as IntrinsicCurve>::Scalar;
#[repr(C)]
#[derive(Clone)]
pub struct VerifyingKey<C: IntrinsicCurve> {
pub(crate) inner: PublicKey<C>,
}
#[repr(C)]
#[derive(Clone)]
pub struct PublicKey<C: IntrinsicCurve> {
point: <C as IntrinsicCurve>::Point,
}
impl<C: IntrinsicCurve> PublicKey<C> {
pub fn into_inner(self) -> <C as IntrinsicCurve>::Point {
self.point
}
}
impl<C: IntrinsicCurve> VerifyingKey<C> {
pub fn as_affine(&self) -> &<C as IntrinsicCurve>::Point {
&self.inner.point
}
}
impl<C> VerifyingKey<C>
where
C: PrimeCurve + IntrinsicCurve,
C::Point: WeierstrassPoint + CyclicGroup + FromCompressed<Coordinate<C>>,
Coordinate<C>: IntMod,
C::Scalar: IntMod + Reduce,
{
#[allow(non_snake_case)]
pub fn recover_from_prehash_noverify(
prehash: &[u8],
sig: &[u8],
recovery_id: RecoveryId,
) -> VerifyingKey<C>
where
for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
{
assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
assert_eq!(sig.len(), <C as IntrinsicCurve>::Scalar::NUM_LIMBS * 2);
let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
let r = Scalar::<C>::from_be_bytes(r_be);
let s = Scalar::<C>::from_be_bytes(s_be);
assert_ne!(r, Scalar::<C>::ZERO);
assert_ne!(s, Scalar::<C>::ZERO);
let z = Scalar::<C>::from_be_bytes(bits2field::<C>(prehash).unwrap().as_ref());
let mut x = Coordinate::<C>::from_le_bytes(r.as_le_bytes());
if recovery_id.is_x_reduced() {
let order = Coordinate::<C>::from_le_bytes(Scalar::<C>::MODULUS.as_ref());
x.add_assign(order);
}
let rec_id = recovery_id.to_byte();
let R: C::Point = FromCompressed::decompress(x, &rec_id);
let neg_u1 = z.div_unsafe(&r);
let u2 = s.div_unsafe(&r);
let NEG_G = C::Point::NEG_GENERATOR;
let point = <C as IntrinsicCurve>::msm(&[neg_u1, u2], &[NEG_G, R]);
let public_key = PublicKey { point };
VerifyingKey { inner: public_key }
}
#[allow(non_snake_case)]
pub fn verify_prehashed(self, prehash: &[u8], sig: &[u8]) -> Result<()>
where
for<'a> &'a C::Point: Add<&'a C::Point, Output = C::Point>,
for<'a> &'a Scalar<C>: DivUnsafe<&'a Scalar<C>, Output = Scalar<C>>,
{
assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
assert_eq!(sig.len(), Scalar::<C>::NUM_LIMBS * 2);
let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
let r = Scalar::<C>::from_be_bytes(r_be);
let s = Scalar::<C>::from_be_bytes(s_be);
assert_ne!(r, Scalar::<C>::ZERO);
assert_ne!(s, Scalar::<C>::ZERO);
let z = <C as IntrinsicCurve>::Scalar::from_be_bytes(
bits2field::<C>(prehash).unwrap().as_ref(),
);
let u1 = z.div_unsafe(&s);
let u2 = (&r).div_unsafe(&s);
let G = C::Point::GENERATOR;
let Q = self.inner.point;
let R = <C as IntrinsicCurve>::msm(&[u1, u2], &[G, Q]);
if R.is_identity() {
return Err(Error::new());
}
let (x_1, _) = R.into_coords();
let x_mod_n = Scalar::<C>::reduce_le_bytes(x_1.as_le_bytes());
if x_mod_n == r {
Ok(())
} else {
Err(Error::new())
}
}
}