openvm_ecc_guest/
ecdsa.rs

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//
33// Signing implementations are placeholders to support patching compilation
34//
35
36/// This is placeholder struct for compatibility purposes with the `ecdsa` crate.
37/// Signing from private keys is not supported yet.
38#[derive(Clone)]
39pub struct SigningKey<C: IntrinsicCurve> {
40    /// ECDSA signing keys are non-zero elements of a given curve's scalar field.
41    #[allow(dead_code)]
42    secret_scalar: NonZeroScalar<C>,
43
44    /// Verifying key which corresponds to this signing key.
45    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// This struct is public because it is used by the VerifyPrimitive impl in the k256 and p256 guest
74// libraries.
75#[repr(C)]
76#[derive(Clone)]
77pub struct VerifyingKey<C: IntrinsicCurve> {
78    pub(crate) inner: PublicKey<C>,
79}
80
81// This struct is public because it is used by the VerifyPrimitive impl in the k256 and p256 guest
82#[repr(C)]
83#[derive(Clone)]
84pub struct PublicKey<C: IntrinsicCurve> {
85    /// Affine point
86    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    /// Convert an `AffinePoint` into a [`PublicKey`].
95    /// In addition, for `Coordinate<C>` implementing `IntMod`, this function will assert that the
96    /// affine coordinates of `point` are both in canonical form.
97    ///
98    /// This does not check that `point` is on the curve. Use this only with points that have
99    /// already been checked. For public keys from untrusted bytes, use [`Self::from_sec1_bytes`].
100    pub fn from_affine(point: AffinePoint<C>) -> Result<Self> {
101        // Internally this calls `is_eq` on `x` and `y` coordinates, which will assert `x, y` are
102        // reduced.
103        if point.is_identity() {
104            Err(Error::new())
105        } else {
106            Ok(Self { point })
107        }
108    }
109
110    /// # Safety
111    /// - The uncompressed deserialization checks that the point is on the curve but does not
112    ///   perform any additional subgroup checks.
113    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        // Validate tag
122        let tag = Tag::from_u8(bytes[0]).map_err(|_| Error::new())?;
123
124        // Validate length
125        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                // Reject identity point, a PublicKey must be non-identity
133                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                // Decompressed point will never be identity
141                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    /// Convert an affine point into a verifying key.
208    ///
209    /// This has the same validation behavior as [`PublicKey::from_affine`].
210    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
228// Functions for compatibility with `ecdsa` crate
229impl<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    /// Recover a [`VerifyingKey`] from the given message, signature, and
241    /// [`RecoveryId`].
242    ///
243    /// The message is first hashed using this curve's [`DigestPrimitive`].
244    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    /// Recover a [`VerifyingKey`] from the given message [`Digest`],
256    /// signature, and [`RecoveryId`].
257    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    /// Recover a [`VerifyingKey`] from the given `prehash` of a message, the
269    /// signature over that prehashed message, and a [`RecoveryId`].
270    /// Note that this function does not verify the signature with the recovered key.
271    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
283/// To match the RustCrypto trait `VerifyPrimitive`. Certain curves have special verification logic
284/// outside of the general ECDSA verification algorithm. This trait provides a hook for such logic.
285///
286/// This trait is intended to be implemented on type which can access
287/// the affine point representing the public key via `&self`, such as a
288/// particular curve's `AffinePoint` type.
289pub trait VerifyCustomHook<C>: WeierstrassPoint
290where
291    C: IntrinsicCurve + PrimeCurve,
292    SignatureSize<C>: ArrayLength<u8>,
293{
294    /// This is **NOT** the full ECDSA signature verification algorithm. The implementer should only
295    /// add additional verification logic not contained in [verify_prehashed]. The default
296    /// implementation does nothing.
297    ///
298    /// Accepts the following arguments:
299    ///
300    /// - `z`: message digest to be verified. MUST BE OUTPUT OF A CRYPTOGRAPHICALLY SECURE DIGEST
301    ///   ALGORITHM!!!
302    /// - `sig`: signature to be verified against the key and message
303    fn verify_hook(&self, _z: &[u8], _sig: &Signature<C>) -> Result<()> {
304        Ok(())
305    }
306}
307
308//
309// `*Verifier` trait impls
310//
311
312impl<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
367//
368// copied from `ecdsa`
369//
370impl<C> VerifyingKey<C>
371where
372    C: CurveArithmetic + IntrinsicCurve,
373    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C> + Default + ConditionallySelectable,
374    FieldBytesSize<C>: ModulusSize,
375{
376    /// Initialize [`VerifyingKey`] from an [`EncodedPoint`].
377    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    /// Serialize this [`VerifyingKey`] as a SEC1 [`EncodedPoint`], optionally
384    /// applying point compression.
385    pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
386        self.inner.to_encoded_point(compress)
387    }
388}
389
390//
391// sec1 traits copied from elliptic_curve
392//
393impl<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    /// Initialize [`PublicKey`] from an [`EncodedPoint`]
400    fn from_encoded_point(encoded_point: &EncodedPoint<C>) -> CtOption<Self> {
401        AffinePoint::<C>::from_encoded_point(encoded_point).and_then(|point| {
402            // Defeating the point of `subtle`, but the use case is specifically a public key
403            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    /// Serialize this [`PublicKey`] as a SEC1 [`EncodedPoint`], optionally applying
416    /// point compression
417    fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
418        self.point.to_encoded_point(compress)
419    }
420}
421
422// Custom openvm implementations
423impl<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    /// ## Assumption
431    /// To use this implementation, the `Signature<C>`, `Coordinate<C>`, and `FieldBytes<C>` should
432    /// all be encoded in big endian bytes. The implementation also assumes that
433    /// `Scalar::<C>::NUM_LIMBS <= FieldBytesSize::<C>::USIZE <= Coordinate::<C>::NUM_LIMBS`.
434    ///
435    /// Ref: <https://github.com/RustCrypto/signatures/blob/85c984bcc9927c2ce70c7e15cbfe9c6936dd3521/ecdsa/src/recovery.rs#L297>
436    ///
437    /// Recovery does not require additional signature verification: <https://github.com/RustCrypto/signatures/pull/831>
438    #[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        // This should get compiled out:
449        assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
450        // IntMod limbs are currently always bytes
451        if sig.len() != <C as IntrinsicCurve>::Scalar::NUM_LIMBS * 2 {
452            return Err(Error::new());
453        }
454        // Signature is default encoded in big endian bytes
455        let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
456        // Note: Scalar internally stores using little endian
457        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        // Perf: don't use bits2field from ::ecdsa
464        let prehash_bytes = bits2field::<C>(prehash)?;
465        // If prehash is longer than Scalar::NUM_LIMBS, take leftmost bytes
466        let trim = prehash_bytes.len().saturating_sub(Scalar::<C>::NUM_LIMBS);
467        // from_be_bytes_unchecked zero-pads if len < Scalar::NUM_LIMBS
468        // we don't need to reduce because IntMod is up to modular equivalence
469        let z = Scalar::<C>::from_be_bytes_unchecked(&prehash_bytes[..prehash_bytes.len() - trim]);
470
471        // `r` is in the Scalar field, we now possibly add C::ORDER to it to get `x`
472        // in the Coordinate field.
473        // We take some extra care for the case when FieldBytesSize<C> may be larger than
474        // Scalar::<C>::NUM_LIMBS.
475        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                // No reduction should happen here if r was reduced
488                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        // The point R decompressed from x-coordinate `r`
497        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/// Assumes that `sig` is proper encoding of `r, s`.
510// Ref: https://docs.rs/ecdsa/latest/src/ecdsa/hazmat.rs.html#270
511#[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    // This should get compiled out:
526    assert!(Scalar::<C>::NUM_LIMBS <= Coordinate::<C>::NUM_LIMBS);
527    // IntMod limbs are currently always bytes
528    if sig.len() != Scalar::<C>::NUM_LIMBS * 2 {
529        return Err(Error::new());
530    }
531    // Signature is default encoded in big endian bytes
532    let (r_be, s_be) = sig.split_at(<C as IntrinsicCurve>::Scalar::NUM_LIMBS);
533    // Note: Scalar internally stores using little endian
534    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    // Perf: don't use bits2field from ::ecdsa
541    let prehash_bytes = bits2field::<C>(prehash)?;
542    // If prehash is longer than Scalar::NUM_LIMBS, take leftmost bytes
543    let trim = prehash_bytes.len().saturating_sub(Scalar::<C>::NUM_LIMBS);
544    // from_be_bytes_unchecked zero-pads if len < Scalar::NUM_LIMBS
545    // we don't need to reduce because IntMod is up to modular equivalence
546    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    // public key
553    let Q = pubkey;
554    let R = <C as IntrinsicCurve>::msm(&[u1, u2], &[G, Q]);
555    // For Coordinate<C>: IntMod, the internal implementation of is_identity will assert x, y
556    // coordinates of R are both reduced.
557    if R.is_identity() {
558        return Err(Error::new());
559    }
560    let (x_1, _) = R.into_coords();
561    // Scalar and Coordinate may be different byte lengths, so we use an inefficient reduction
562    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}