Skip to main content

rings_core/ecc/
group.rs

1//! Algebraic carriers and elliptic-curve adapters.
2//!
3//! The generic algebraic vocabulary lives in [`crate::algebra`]. This module
4//! connects those traits to concrete elliptic-curve libraries:
5//!
6//! - [`Point<C>`] is the curve element carrier and implements the additive
7//!   abelian-group and module traits.
8//! - [`Scalar<C>`] is the curve scalar carrier and implements the field traits.
9//! - [`CurveGroup`] is the adapter boundary for elliptic-curve libraries. A
10//!   marker type such as [`Secp256k1`] or [`Bls12381G1`] supplies native point
11//!   operations and scalar action.
12//! - [`CurveScalarField`] is the separate adapter boundary for scalar-field
13//!   operations and non-zero scalar sampling.
14//! - [`CyclicModule`] is the algebraic capability used by cryptographic
15//!   algorithms that require a distinguished generator and fresh non-zero
16//!   scalars.
17//!
18//! All operations are written in additive notation. For a scalar `x` and
19//! generator `g`, `xg` is represented by [`CyclicModule::generator_mul`].
20//!
21//! This split gives the rest of the cryptographic code one stable vocabulary:
22//! algorithms depend on algebraic carrier laws, not on a concrete crate such as
23//! `libsecp256k1`, `p256`, `arkworks`, or `curve25519-dalek`. Adding a curve is
24//! therefore a matter of implementing the point and scalar adapter boundaries
25//! once; algorithms such as ElGamal do not need per-curve branches.
26
27use std::cell::RefCell;
28use std::convert::TryFrom;
29use std::ops::Add;
30use std::ops::Mul;
31use std::ops::Neg;
32use std::ops::Sub;
33use std::sync::OnceLock;
34
35use ark_bls12_381::Fr as Bls12381ScalarField;
36use ark_bls12_381::G1Projective;
37use ark_ec::Group as ArkGroup;
38use ark_ff::Field as _;
39use ark_ff::Zero as _;
40use ark_std::UniformRand;
41#[cfg(feature = "curve-ristretto255")]
42use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
43#[cfg(feature = "curve-ristretto255")]
44use curve25519_dalek::ristretto::RistrettoPoint;
45#[cfg(feature = "curve-ristretto255")]
46use curve25519_dalek::scalar::Scalar as Ristretto255ScalarField;
47#[cfg(feature = "curve-ristretto255")]
48use curve25519_dalek::traits::Identity as _;
49use elliptic_curve::ff::Field as _;
50use libsecp256k1::curve::Affine;
51use libsecp256k1::curve::ECMultContext;
52use libsecp256k1::curve::ECMultGenContext;
53use libsecp256k1::curve::Jacobian;
54use libsecp256k1::curve::Scalar as SecpK1FieldScalar;
55use p256::ProjectivePoint;
56use p256::Scalar as Secp256r1ScalarField;
57use rand::RngCore;
58use rand::SeedableRng;
59use rand_hc::Hc128Rng;
60
61use crate::algebra::AbelianGroup;
62use crate::algebra::CommutativeRing;
63use crate::algebra::Field as AlgebraField;
64use crate::algebra::Module;
65use crate::algebra::One as AlgebraOne;
66use crate::algebra::Zero as AlgebraZero;
67use crate::ecc::PublicKey;
68use crate::ecc::SecretKey;
69use crate::error::Error;
70use crate::error::Result;
71
72/// Curve-specific point-group and scalar-action operations implemented by curve markers.
73///
74/// This trait is the adapter boundary between algebraic point carriers and
75/// concrete elliptic-curve libraries. For a marker `C`, the native `Point` type
76/// must represent elements of one finite abelian group, and `Scalar` must be the
77/// native scalar type used for the right module action. Scalar field operations
78/// live in [`CurveScalarField`], not here.
79///
80/// Implementors must preserve the following laws after accounting for native
81/// representation details such as projective coordinates:
82///
83/// - `identity` is a left and right identity for `add`.
84/// - `add` is associative and commutative over the represented group.
85/// - `neg(p)` is the additive inverse of `p`.
86/// - `eq` is an equivalence relation over group elements, not merely raw
87///   representation equality; equivalent projective representatives must
88///   compare equal.
89/// - `eq` is compatible with `add`, `neg`, `mul`, and `generator_mul`.
90/// - `generator_mul(s)` is equivalent to `mul(generator(), s)`.
91/// - scalar multiplication is a right module action:
92///   `mul(add(p, q), s) == add(mul(p, s), mul(q, s))`.
93pub trait CurveGroup {
94    /// Native point representation for this curve group.
95    type Point: Clone;
96    /// Native scalar representation for this curve group.
97    type Scalar: Clone;
98
99    /// Additive identity.
100    fn identity() -> Self::Point;
101
102    /// Distinguished generator.
103    fn generator() -> Self::Point;
104
105    /// Multiply the distinguished generator by a scalar.
106    fn generator_mul(scalar: &Self::Scalar) -> Self::Point {
107        let generator = Self::generator();
108        Self::mul(&generator, scalar)
109    }
110
111    /// Group addition.
112    fn add(lhs: &Self::Point, rhs: &Self::Point) -> Self::Point;
113
114    /// Group inverse.
115    fn neg(point: &Self::Point) -> Self::Point;
116
117    /// Scalar multiplication.
118    fn mul(point: &Self::Point, scalar: &Self::Scalar) -> Self::Point;
119
120    /// Element equality.
121    fn eq(lhs: &Self::Point, rhs: &Self::Point) -> bool;
122}
123
124/// Curve-specific scalar-field operations implemented by curve markers.
125///
126/// This trait is intentionally separate from [`CurveGroup`]. A curve point
127/// group and its scalar field are related by the module action, but they are
128/// different carriers with different operations and different law obligations.
129///
130/// Implementors must preserve these laws:
131///
132/// - scalars form a finite [`Field`](crate::algebra::Field);
133/// - `scalar_eq` is total equality over canonical scalar values;
134/// - `random_scalar_with_rng` returns a non-zero scalar.
135pub trait CurveScalarField: CurveGroup {
136    /// Scalar additive identity.
137    fn scalar_zero() -> Self::Scalar;
138
139    /// Scalar multiplicative identity.
140    fn scalar_one() -> Self::Scalar;
141
142    /// Return whether the scalar is the additive identity.
143    fn scalar_is_zero(scalar: &Self::Scalar) -> bool;
144
145    /// Scalar addition.
146    fn scalar_add(lhs: &Self::Scalar, rhs: &Self::Scalar) -> Self::Scalar;
147
148    /// Scalar subtraction.
149    fn scalar_sub(lhs: &Self::Scalar, rhs: &Self::Scalar) -> Self::Scalar;
150
151    /// Scalar additive inverse.
152    fn scalar_neg(scalar: &Self::Scalar) -> Self::Scalar;
153
154    /// Scalar multiplication.
155    fn scalar_mul(lhs: &Self::Scalar, rhs: &Self::Scalar) -> Self::Scalar;
156
157    /// Scalar multiplicative inverse.
158    fn scalar_inverse(scalar: &Self::Scalar) -> Option<Self::Scalar>;
159
160    /// Scalar equality.
161    fn scalar_eq(lhs: &Self::Scalar, rhs: &Self::Scalar) -> bool;
162
163    /// Generate a fresh non-zero random scalar from an explicit RNG.
164    fn random_scalar_with_rng(rng: &mut impl RngCore) -> Self::Scalar;
165
166    /// Generate a fresh non-zero random scalar from the default thread-local RNG.
167    fn random_scalar() -> Self::Scalar {
168        with_group_rng(|rng| Self::random_scalar_with_rng(rng))
169    }
170}
171
172/// Algebraic carrier with a distinguished generator and non-zero scalar sampler.
173///
174/// This is not a replacement group hierarchy; it is the extra capability needed
175/// by cryptographic algorithms such as ElGamal after the carrier already
176/// implements [`AbelianGroup`] and [`Module`]. The implementation obligation is
177/// that `generator_mul(s)` equals `generator() * s` and that sampled scalars are
178/// non-zero field elements.
179///
180/// `Module<Self::Scalar>` stays as an explicit consumer bound instead of a
181/// supertrait here. `CyclicModule` introduces the associated scalar type, while
182/// [`Module`] proves the right scalar action for arbitrary elements; consumers
183/// that multiply elements by scalars should request both
184/// `CyclicModule` and `Module<Element::Scalar>`. This keeps the generator and
185/// sampling capability separate from the module-action proof while still
186/// requiring `generator_mul(s)` to be observationally equal to `generator() * s`.
187///
188/// Non-zero scalar sampling also appears in [`CurveScalarField`] intentionally.
189/// Curve adapters provide the native scalar-field sampler; `CyclicModule`
190/// exposes the same cryptographic capability through the element carrier so
191/// algorithms do not need to know the curve marker type.
192pub trait CyclicModule: AbelianGroup + Sized {
193    /// Scalar field for the module action.
194    type Scalar: AlgebraField;
195
196    /// Distinguished generator for the cyclic subgroup used by the algorithm.
197    fn generator() -> Self;
198
199    /// Multiply the distinguished generator by a scalar.
200    fn generator_mul(scalar: &Self::Scalar) -> Self;
201
202    /// Generate a fresh non-zero random scalar from an explicit RNG.
203    fn random_scalar_with_rng(rng: &mut impl RngCore) -> Self::Scalar;
204
205    /// Generate a fresh non-zero random scalar from the default thread-local RNG.
206    fn random_scalar() -> Self::Scalar {
207        with_group_rng(|rng| Self::random_scalar_with_rng(rng))
208    }
209}
210
211/// Generic group element for curve marker `C`.
212#[derive(Debug)]
213pub struct Point<C: CurveGroup> {
214    inner: C::Point,
215}
216
217/// Generic scalar for curve marker `C`.
218#[derive(Debug)]
219pub struct Scalar<C: CurveGroup> {
220    inner: C::Scalar,
221}
222
223/// secp256k1 curve marker.
224#[derive(Debug)]
225pub struct Secp256k1;
226
227/// secp256r1/P-256 curve marker.
228#[derive(Debug)]
229pub struct Secp256r1;
230
231/// BLS12-381 G1 curve marker.
232#[derive(Debug)]
233pub struct Bls12381G1;
234
235/// Ristretto255 group marker.
236#[cfg(feature = "curve-ristretto255")]
237#[derive(Debug)]
238pub struct Ristretto255;
239
240thread_local! {
241    static GROUP_RNG: RefCell<Hc128Rng> = RefCell::new(Hc128Rng::from_entropy());
242}
243
244static SECP256K1_GENERATOR: OnceLock<Jacobian> = OnceLock::new();
245
246impl<C: CurveGroup> Point<C> {
247    /// Build a group element from the curve-native point type.
248    pub fn new(inner: C::Point) -> Self {
249        Self { inner }
250    }
251
252    /// Borrow the curve-native point type.
253    pub fn as_inner(&self) -> &C::Point {
254        &self.inner
255    }
256
257    /// Unwrap into the curve-native point type.
258    pub fn into_inner(self) -> C::Point {
259        self.inner
260    }
261}
262
263impl<C: CurveGroup> Scalar<C> {
264    /// Build a scalar from the curve-native scalar type.
265    pub fn new(inner: C::Scalar) -> Self {
266        Self { inner }
267    }
268
269    /// Borrow the curve-native scalar type.
270    pub fn as_inner(&self) -> &C::Scalar {
271        &self.inner
272    }
273
274    /// Unwrap into the curve-native scalar type.
275    pub fn into_inner(self) -> C::Scalar {
276        self.inner
277    }
278}
279
280impl<C: CurveGroup> Clone for Point<C> {
281    fn clone(&self) -> Self {
282        Self::new(self.inner.clone())
283    }
284}
285
286impl<C> Copy for Point<C>
287where
288    C: CurveGroup,
289    C::Point: Copy,
290{
291}
292
293impl<C: CurveGroup> Clone for Scalar<C> {
294    fn clone(&self) -> Self {
295        Self::new(self.inner.clone())
296    }
297}
298
299impl<C> Copy for Scalar<C>
300where
301    C: CurveGroup,
302    C::Scalar: Copy,
303{
304}
305
306impl<C: CurveGroup> Add for Point<C> {
307    type Output = Self;
308
309    fn add(self, rhs: Self) -> Self::Output {
310        Self::new(C::add(&self.inner, &rhs.inner))
311    }
312}
313
314impl<C: CurveGroup> Neg for Point<C> {
315    type Output = Self;
316
317    fn neg(self) -> Self::Output {
318        Self::new(C::neg(&self.inner))
319    }
320}
321
322impl<C: CurveGroup> Sub for Point<C> {
323    type Output = Self;
324
325    fn sub(self, rhs: Self) -> Self::Output {
326        self + (-rhs)
327    }
328}
329
330impl<C: CurveGroup> Mul<Scalar<C>> for Point<C> {
331    type Output = Self;
332
333    fn mul(self, rhs: Scalar<C>) -> Self::Output {
334        Self::new(C::mul(&self.inner, &rhs.inner))
335    }
336}
337
338impl<C: CurveGroup> PartialEq for Point<C> {
339    fn eq(&self, other: &Self) -> bool {
340        C::eq(&self.inner, &other.inner)
341    }
342}
343
344impl<C: CurveGroup> Eq for Point<C> {}
345
346impl<C: CurveGroup> AlgebraZero for Point<C> {
347    fn zero() -> Self {
348        Self::new(C::identity())
349    }
350
351    fn is_zero(&self) -> bool {
352        C::eq(&self.inner, &C::identity())
353    }
354}
355
356impl<C: CurveGroup> AbelianGroup for Point<C> {}
357
358impl<C: CurveScalarField> Module<Scalar<C>> for Point<C> {}
359
360impl<C: CurveScalarField> CyclicModule for Point<C> {
361    type Scalar = Scalar<C>;
362
363    fn generator() -> Self {
364        Self::new(C::generator())
365    }
366
367    fn generator_mul(scalar: &Self::Scalar) -> Self {
368        Self::new(C::generator_mul(&scalar.inner))
369    }
370
371    fn random_scalar_with_rng(rng: &mut impl RngCore) -> Self::Scalar {
372        Scalar::new(C::random_scalar_with_rng(rng))
373    }
374}
375
376impl<C: CurveScalarField> Add for Scalar<C> {
377    type Output = Self;
378
379    fn add(self, rhs: Self) -> Self::Output {
380        Self::new(C::scalar_add(&self.inner, &rhs.inner))
381    }
382}
383
384impl<C: CurveScalarField> Sub for Scalar<C> {
385    type Output = Self;
386
387    fn sub(self, rhs: Self) -> Self::Output {
388        Self::new(C::scalar_sub(&self.inner, &rhs.inner))
389    }
390}
391
392impl<C: CurveScalarField> Neg for Scalar<C> {
393    type Output = Self;
394
395    fn neg(self) -> Self::Output {
396        Self::new(C::scalar_neg(&self.inner))
397    }
398}
399
400impl<C: CurveScalarField> Mul for Scalar<C> {
401    type Output = Self;
402
403    fn mul(self, rhs: Self) -> Self::Output {
404        Self::new(C::scalar_mul(&self.inner, &rhs.inner))
405    }
406}
407
408impl<C: CurveScalarField> PartialEq for Scalar<C> {
409    fn eq(&self, other: &Self) -> bool {
410        C::scalar_eq(&self.inner, &other.inner)
411    }
412}
413
414impl<C: CurveScalarField> Eq for Scalar<C> {}
415
416impl<C: CurveScalarField> AlgebraZero for Scalar<C> {
417    fn zero() -> Self {
418        Self::new(C::scalar_zero())
419    }
420
421    fn is_zero(&self) -> bool {
422        C::scalar_is_zero(&self.inner)
423    }
424}
425
426impl<C: CurveScalarField> AlgebraOne for Scalar<C> {
427    fn one() -> Self {
428        Self::new(C::scalar_one())
429    }
430}
431
432impl<C: CurveScalarField> AbelianGroup for Scalar<C> {}
433
434impl<C: CurveScalarField> CommutativeRing for Scalar<C> {}
435
436impl<C: CurveScalarField> AlgebraField for Scalar<C> {
437    fn try_inverse(&self) -> Option<Self> {
438        C::scalar_inverse(&self.inner).map(Self::new)
439    }
440}
441
442// The simple curve adapters below all have the same shape: the native library
443// already exposes identity, generator, addition, negation, scalar
444// multiplication, equality, and point conversion. Keeping that pattern in one
445// macro makes each supported curve a short declaration while preserving the
446// explicit algebraic operations at the trait boundary. secp256k1 remains
447// hand-written because it needs precomputed multiplication contexts and
448// explicit infinity handling from `libsecp256k1`.
449macro_rules! impl_curve_group_adapter {
450    (
451        $curve:ty {
452            point: $point:ty,
453            scalar: $scalar:ty,
454            identity: $identity:expr,
455            generator: $generator:expr,
456            random_scalar: |$rng:ident| $random_scalar:block,
457            add: $add:expr,
458            neg: $neg:expr,
459            mul: $mul:expr,
460            eq: $eq:expr,
461            scalar_zero: $scalar_zero:expr,
462            scalar_one: $scalar_one:expr,
463            scalar_is_zero: $scalar_is_zero:expr,
464            scalar_add: $scalar_add:expr,
465            scalar_sub: $scalar_sub:expr,
466            scalar_neg: $scalar_neg:expr,
467            scalar_mul: $scalar_mul:expr,
468            scalar_inverse: $scalar_inverse:expr,
469            scalar_eq: $scalar_eq:expr $(,)?
470        }
471    ) => {
472        impl CurveGroup for $curve {
473            type Point = $point;
474            type Scalar = $scalar;
475
476            fn identity() -> Self::Point {
477                $identity
478            }
479
480            fn generator() -> Self::Point {
481                $generator
482            }
483
484            fn add(lhs: &Self::Point, rhs: &Self::Point) -> Self::Point {
485                ($add)(lhs, rhs)
486            }
487
488            fn neg(point: &Self::Point) -> Self::Point {
489                ($neg)(point)
490            }
491
492            fn mul(point: &Self::Point, scalar: &Self::Scalar) -> Self::Point {
493                ($mul)(point, scalar)
494            }
495
496            fn eq(lhs: &Self::Point, rhs: &Self::Point) -> bool {
497                ($eq)(lhs, rhs)
498            }
499        }
500
501        impl CurveScalarField for $curve {
502            fn scalar_zero() -> Self::Scalar {
503                $scalar_zero
504            }
505
506            fn scalar_one() -> Self::Scalar {
507                $scalar_one
508            }
509
510            fn scalar_is_zero(scalar: &Self::Scalar) -> bool {
511                ($scalar_is_zero)(scalar)
512            }
513
514            fn scalar_add(lhs: &Self::Scalar, rhs: &Self::Scalar) -> Self::Scalar {
515                ($scalar_add)(lhs, rhs)
516            }
517
518            fn scalar_sub(lhs: &Self::Scalar, rhs: &Self::Scalar) -> Self::Scalar {
519                ($scalar_sub)(lhs, rhs)
520            }
521
522            fn scalar_neg(scalar: &Self::Scalar) -> Self::Scalar {
523                ($scalar_neg)(scalar)
524            }
525
526            fn scalar_mul(lhs: &Self::Scalar, rhs: &Self::Scalar) -> Self::Scalar {
527                ($scalar_mul)(lhs, rhs)
528            }
529
530            fn scalar_inverse(scalar: &Self::Scalar) -> Option<Self::Scalar> {
531                ($scalar_inverse)(scalar)
532            }
533
534            fn scalar_eq(lhs: &Self::Scalar, rhs: &Self::Scalar) -> bool {
535                ($scalar_eq)(lhs, rhs)
536            }
537
538            fn random_scalar_with_rng(rng: &mut impl RngCore) -> Self::Scalar {
539                let $rng = rng;
540                $random_scalar
541            }
542        }
543
544        impl From<$point> for Point<$curve> {
545            fn from(point: $point) -> Self {
546                Self::new(point)
547            }
548        }
549
550        impl From<Point<$curve>> for $point {
551            fn from(point: Point<$curve>) -> Self {
552                point.inner
553            }
554        }
555    };
556}
557
558impl CurveGroup for Secp256k1 {
559    type Point = Jacobian;
560    type Scalar = SecpK1FieldScalar;
561
562    fn identity() -> Self::Point {
563        secp256k1_identity()
564    }
565
566    fn generator() -> Self::Point {
567        *SECP256K1_GENERATOR.get_or_init(secp256k1_generator)
568    }
569
570    fn generator_mul(scalar: &Self::Scalar) -> Self::Point {
571        let mut result = Jacobian::default();
572        secp256k1_generator_context().ecmult_gen(&mut result, scalar);
573        result
574    }
575
576    fn add(lhs: &Self::Point, rhs: &Self::Point) -> Self::Point {
577        lhs.add_var(rhs, None)
578    }
579
580    fn neg(point: &Self::Point) -> Self::Point {
581        point.neg()
582    }
583
584    fn mul(point: &Self::Point, scalar: &Self::Scalar) -> Self::Point {
585        if point.is_infinity() {
586            return secp256k1_identity();
587        }
588        let mut result = Jacobian::default();
589        secp256k1_multiplication_context().ecmult_const(
590            &mut result,
591            &Affine::from_gej(point),
592            scalar,
593        );
594        result
595    }
596
597    fn eq(lhs: &Self::Point, rhs: &Self::Point) -> bool {
598        secp256k1_jacobian_bytes(*lhs) == secp256k1_jacobian_bytes(*rhs)
599    }
600}
601
602impl CurveScalarField for Secp256k1 {
603    fn scalar_zero() -> Self::Scalar {
604        SecpK1FieldScalar::from_int(0)
605    }
606
607    fn scalar_one() -> Self::Scalar {
608        SecpK1FieldScalar::from_int(1)
609    }
610
611    fn scalar_is_zero(scalar: &Self::Scalar) -> bool {
612        scalar.is_zero()
613    }
614
615    fn scalar_add(lhs: &Self::Scalar, rhs: &Self::Scalar) -> Self::Scalar {
616        *lhs + *rhs
617    }
618
619    fn scalar_sub(lhs: &Self::Scalar, rhs: &Self::Scalar) -> Self::Scalar {
620        *lhs + -*rhs
621    }
622
623    fn scalar_neg(scalar: &Self::Scalar) -> Self::Scalar {
624        -*scalar
625    }
626
627    fn scalar_mul(lhs: &Self::Scalar, rhs: &Self::Scalar) -> Self::Scalar {
628        *lhs * *rhs
629    }
630
631    fn scalar_inverse(scalar: &Self::Scalar) -> Option<Self::Scalar> {
632        if scalar.is_zero() {
633            None
634        } else {
635            Some(scalar.inv())
636        }
637    }
638
639    fn scalar_eq(lhs: &Self::Scalar, rhs: &Self::Scalar) -> bool {
640        lhs == rhs
641    }
642
643    fn random_scalar_with_rng(rng: &mut impl RngCore) -> Self::Scalar {
644        libsecp256k1::SecretKey::random(rng).into()
645    }
646}
647
648impl_curve_group_adapter! {
649    Secp256r1 {
650        point: ProjectivePoint,
651        scalar: Secp256r1ScalarField,
652        identity: ProjectivePoint::IDENTITY,
653        generator: ProjectivePoint::GENERATOR,
654        random_scalar: |rng| {
655            loop {
656                let scalar = Secp256r1ScalarField::random(&mut *rng);
657                if !bool::from(scalar.is_zero()) {
658                    break scalar;
659                }
660            }
661        },
662        add: |lhs: &ProjectivePoint, rhs: &ProjectivePoint| *lhs + *rhs,
663        neg: |point: &ProjectivePoint| -*point,
664        mul: |point: &ProjectivePoint, scalar: &Secp256r1ScalarField| *point * *scalar,
665        eq: |lhs: &ProjectivePoint, rhs: &ProjectivePoint| lhs == rhs,
666        scalar_zero: Secp256r1ScalarField::ZERO,
667        scalar_one: Secp256r1ScalarField::ONE,
668        scalar_is_zero: |scalar: &Secp256r1ScalarField| bool::from(scalar.is_zero()),
669        scalar_add: |lhs: &Secp256r1ScalarField, rhs: &Secp256r1ScalarField| *lhs + *rhs,
670        scalar_sub: |lhs: &Secp256r1ScalarField, rhs: &Secp256r1ScalarField| *lhs - *rhs,
671        scalar_neg: |scalar: &Secp256r1ScalarField| -*scalar,
672        scalar_mul: |lhs: &Secp256r1ScalarField, rhs: &Secp256r1ScalarField| *lhs * *rhs,
673        scalar_inverse: |scalar: &Secp256r1ScalarField| scalar.invert().into_option(),
674        scalar_eq: |lhs: &Secp256r1ScalarField, rhs: &Secp256r1ScalarField| lhs == rhs,
675    }
676}
677
678impl_curve_group_adapter! {
679    Bls12381G1 {
680        point: G1Projective,
681        scalar: Bls12381ScalarField,
682        identity: G1Projective::zero(),
683        generator: G1Projective::generator(),
684        random_scalar: |rng| {
685            loop {
686                let scalar = Bls12381ScalarField::rand(&mut *rng);
687                if !scalar.is_zero() {
688                    break scalar;
689                }
690            }
691        },
692        add: |lhs: &G1Projective, rhs: &G1Projective| *lhs + *rhs,
693        neg: |point: &G1Projective| -*point,
694        mul: |point: &G1Projective, scalar: &Bls12381ScalarField| *point * *scalar,
695        eq: |lhs: &G1Projective, rhs: &G1Projective| lhs == rhs,
696        scalar_zero: Bls12381ScalarField::ZERO,
697        scalar_one: Bls12381ScalarField::ONE,
698        scalar_is_zero: |scalar: &Bls12381ScalarField| scalar.is_zero(),
699        scalar_add: |lhs: &Bls12381ScalarField, rhs: &Bls12381ScalarField| *lhs + *rhs,
700        scalar_sub: |lhs: &Bls12381ScalarField, rhs: &Bls12381ScalarField| *lhs - *rhs,
701        scalar_neg: |scalar: &Bls12381ScalarField| -*scalar,
702        scalar_mul: |lhs: &Bls12381ScalarField, rhs: &Bls12381ScalarField| *lhs * *rhs,
703        scalar_inverse: |scalar: &Bls12381ScalarField| scalar.inverse(),
704        scalar_eq: |lhs: &Bls12381ScalarField, rhs: &Bls12381ScalarField| lhs == rhs,
705    }
706}
707
708#[cfg(feature = "curve-ristretto255")]
709impl_curve_group_adapter! {
710    Ristretto255 {
711        point: RistrettoPoint,
712        scalar: Ristretto255ScalarField,
713        identity: RistrettoPoint::identity(),
714        generator: RISTRETTO_BASEPOINT_POINT,
715        random_scalar: |rng| {
716            loop {
717                let mut bytes = [0u8; 64];
718                rng.fill_bytes(&mut bytes);
719                let scalar = Ristretto255ScalarField::from_bytes_mod_order_wide(&bytes);
720                if scalar != Ristretto255ScalarField::ZERO {
721                    break scalar;
722                }
723            }
724        },
725        add: |lhs: &RistrettoPoint, rhs: &RistrettoPoint| lhs + rhs,
726        neg: |point: &RistrettoPoint| -point,
727        mul: |point: &RistrettoPoint, scalar: &Ristretto255ScalarField| point * scalar,
728        eq: |lhs: &RistrettoPoint, rhs: &RistrettoPoint| lhs == rhs,
729        scalar_zero: Ristretto255ScalarField::ZERO,
730        scalar_one: Ristretto255ScalarField::ONE,
731        scalar_is_zero: |scalar: &Ristretto255ScalarField| *scalar == Ristretto255ScalarField::ZERO,
732        scalar_add: |lhs: &Ristretto255ScalarField, rhs: &Ristretto255ScalarField| *lhs + *rhs,
733        scalar_sub: |lhs: &Ristretto255ScalarField, rhs: &Ristretto255ScalarField| *lhs - *rhs,
734        scalar_neg: |scalar: &Ristretto255ScalarField| -*scalar,
735        scalar_mul: |lhs: &Ristretto255ScalarField, rhs: &Ristretto255ScalarField| *lhs * *rhs,
736        scalar_inverse: |scalar: &Ristretto255ScalarField| {
737            if *scalar == Ristretto255ScalarField::ZERO {
738                None
739            } else {
740                Some(scalar.invert())
741            }
742        },
743        scalar_eq: |lhs: &Ristretto255ScalarField, rhs: &Ristretto255ScalarField| lhs == rhs,
744    }
745}
746
747impl From<SecretKey> for Scalar<Secp256k1> {
748    fn from(secret_key: SecretKey) -> Self {
749        Self::new(secret_key.into())
750    }
751}
752
753impl From<Affine> for Point<Secp256k1> {
754    fn from(point: Affine) -> Self {
755        Self::new(Jacobian::from_ge(&normalize_affine(point)))
756    }
757}
758
759impl From<Point<Secp256k1>> for Affine {
760    fn from(point: Point<Secp256k1>) -> Self {
761        Affine::from_gej(&point.inner)
762    }
763}
764
765impl TryFrom<PublicKey<33>> for Point<Secp256k1> {
766    type Error = Error;
767
768    fn try_from(public_key: PublicKey<33>) -> Result<Self> {
769        let point: Affine = public_key.try_into()?;
770        Ok(point.into())
771    }
772}
773
774impl TryFrom<Point<Secp256k1>> for PublicKey<33> {
775    type Error = Error;
776
777    fn try_from(point: Point<Secp256k1>) -> Result<Self> {
778        if point.inner.is_infinity() {
779            return Err(Error::InvalidPublicKey);
780        }
781        Affine::from(point).try_into()
782    }
783}
784
785fn secp256k1_generator() -> Jacobian {
786    let scalar = SecpK1FieldScalar::from_int(1);
787    let mut point = Jacobian::default();
788    secp256k1_generator_context().ecmult_gen(&mut point, &scalar);
789    point
790}
791
792// Pre:
793// - libsecp256k1 exposes immutable precomputed tables under `static-context`.
794// - group randomness remains independent mutable state and stays in `GROUP_RNG`.
795// Invariant:
796// - every secp256k1 group operation borrows the same process-global contexts.
797// - no worker thread constructs its own `ECMultContext` or `ECMultGenContext`.
798// Post:
799// - secp256k1 arithmetic does not allocate per-thread precomputation tables.
800fn secp256k1_generator_context() -> &'static ECMultGenContext {
801    &libsecp256k1::ECMULT_GEN_CONTEXT
802}
803
804fn secp256k1_multiplication_context() -> &'static ECMultContext {
805    &libsecp256k1::ECMULT_CONTEXT
806}
807
808fn secp256k1_identity() -> Jacobian {
809    let mut point = Jacobian::default();
810    point.set_infinity();
811    point
812}
813
814fn with_group_rng<R>(f: impl FnOnce(&mut Hc128Rng) -> R) -> R {
815    GROUP_RNG.with(|rng| {
816        let mut rng = rng.borrow_mut();
817        f(&mut rng)
818    })
819}
820
821fn normalize_affine(mut point: Affine) -> Affine {
822    point.x.normalize();
823    point.y.normalize();
824    point
825}
826
827fn secp256k1_jacobian_bytes(point: Jacobian) -> Option<([u8; 32], [u8; 32])> {
828    if point.is_infinity() {
829        return None;
830    }
831    let mut affine = Affine::from_gej(&point);
832    affine.x.normalize();
833    affine.y.normalize();
834    Some((affine.x.b32(), affine.y.b32()))
835}
836
837#[cfg(test)]
838mod tests {
839    use super::*;
840    use crate::algebra::assert_field_laws;
841    use crate::algebra::assert_module_action_laws;
842    use crate::algebra::One;
843    use crate::algebra::Zero;
844
845    fn cyclic_module_laws<Element>()
846    where
847        Element: CyclicModule + Module<Element::Scalar> + Clone + Eq + std::fmt::Debug,
848        Element::Scalar: Clone + Eq + std::fmt::Debug,
849    {
850        let scalar_a = Element::random_scalar();
851        let scalar_b = Element::random_scalar();
852        let scalar_c = Element::random_scalar();
853        let a = Element::generator() * scalar_a.clone();
854        let b = Element::generator() * scalar_b;
855        let c = Element::generator() * scalar_c;
856
857        assert_eq!(a.clone() + Element::zero(), a);
858        assert_eq!(Element::zero() + a.clone(), a);
859        assert_eq!(a.clone() + -a.clone(), Element::zero());
860        assert_eq!((a.clone() + b.clone()) + c.clone(), a + (b + c));
861        assert_eq!(
862            Element::generator_mul(&scalar_a),
863            Element::generator() * scalar_a
864        );
865    }
866
867    fn algebra_laws<C>()
868    where
869        C: CurveScalarField,
870        Point<C>: Eq + std::fmt::Debug,
871        Scalar<C>: Eq + std::fmt::Debug,
872    {
873        let scalar_a = Scalar::<C>::new(C::random_scalar());
874        let scalar_b = Scalar::<C>::new(C::random_scalar());
875        let scalar_c = Scalar::<C>::new(C::random_scalar());
876        let scalars = vec![
877            Scalar::<C>::zero(),
878            Scalar::<C>::one(),
879            scalar_a.clone(),
880            scalar_b.clone(),
881            scalar_c.clone(),
882        ];
883
884        let generator = Point::<C>::new(C::generator());
885        let points = vec![
886            Point::<C>::zero(),
887            generator.clone(),
888            generator.clone() * scalar_a,
889            generator.clone() * scalar_b,
890            generator * scalar_c,
891        ];
892
893        assert_field_laws(&scalars);
894        assert_module_action_laws(&scalars, &points);
895    }
896
897    #[test]
898    fn supported_curve_groups_satisfy_basic_laws() {
899        cyclic_module_laws::<Point<Secp256k1>>();
900        cyclic_module_laws::<Point<Secp256r1>>();
901        cyclic_module_laws::<Point<Bls12381G1>>();
902        #[cfg(feature = "curve-ristretto255")]
903        cyclic_module_laws::<Point<Ristretto255>>();
904    }
905
906    #[test]
907    fn supported_curve_groups_satisfy_algebra_laws() {
908        algebra_laws::<Secp256k1>();
909        algebra_laws::<Secp256r1>();
910        algebra_laws::<Bls12381G1>();
911        #[cfg(feature = "curve-ristretto255")]
912        algebra_laws::<Ristretto255>();
913    }
914
915    #[test]
916    fn secp256k1_contexts_are_shared_across_threads() {
917        const THREAD_COUNT: usize = 4;
918
919        let context_addresses = std::thread::scope(|scope| {
920            let handles: Vec<_> = (0..THREAD_COUNT)
921                .map(|_| {
922                    scope.spawn(|| {
923                        let scalar = SecpK1FieldScalar::from_int(2);
924                        let generator = <Secp256k1 as CurveGroup>::generator();
925                        let _ = <Secp256k1 as CurveGroup>::generator_mul(&scalar);
926                        let _ = <Secp256k1 as CurveGroup>::mul(&generator, &scalar);
927
928                        (
929                            secp256k1_generator_context() as *const ECMultGenContext as usize,
930                            secp256k1_multiplication_context() as *const ECMultContext as usize,
931                        )
932                    })
933                })
934                .collect();
935
936            let mut addresses = std::collections::BTreeSet::new();
937            for handle in handles {
938                match handle.join() {
939                    Ok(address) => {
940                        addresses.insert(address);
941                    }
942                    Err(payload) => std::panic::resume_unwind(payload),
943                }
944            }
945            addresses
946        });
947
948        assert_eq!(
949            context_addresses.len(),
950            1,
951            "secp256k1 precomputed contexts must be process-global"
952        );
953    }
954}