Skip to main content

soroban_sdk/crypto/
bls12_381.rs

1#[cfg(not(target_family = "wasm"))]
2use crate::xdr::ScVal;
3use crate::{
4    crypto::utils::BigInt,
5    env::internal::{self, BytesObject, U256Val, U64Val},
6    impl_bytesn_repr_without_from_bytes,
7    unwrap::{UnwrapInfallible, UnwrapOptimized},
8    Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val, Vec, U256,
9};
10use core::{
11    cmp::Ordering,
12    fmt::Debug,
13    ops::{Add, Mul, Neg, Sub},
14};
15
16pub const FP_SERIALIZED_SIZE: usize = 48; // Size in bytes of a serialized Fp element in BLS12-381. The field modulus is 381 bits, requiring 48 bytes (384 bits) with 3 bits reserved for flags.
17pub const FP2_SERIALIZED_SIZE: usize = FP_SERIALIZED_SIZE * 2;
18pub const G1_SERIALIZED_SIZE: usize = FP_SERIALIZED_SIZE * 2; // Must match soroban_sdk_macro::map_type::G1_SERIALIZED_SIZE.
19pub const G2_SERIALIZED_SIZE: usize = FP2_SERIALIZED_SIZE * 2; // Must match soroban_sdk_macro::map_type::G2_SERIALIZED_SIZE.
20
21/// Bls12_381 provides access to curve and field arithmetics on the BLS12-381
22/// curve.
23pub struct Bls12_381 {
24    env: Env,
25}
26
27/// `Bls12381G1Affine` is a point in the G1 group (subgroup defined over the base field
28///  `Fq`) of the BLS12-381 elliptic curve
29///
30/// # Serialization:
31/// - The 96 bytes represent the **uncompressed encoding** of a point in G1. The
32///   Bytes consist of `be_byte(X) || be_byte(Y)`  (`||` is concatenation),
33///   where 'X' and 'Y' are the two coordinates, each being a base field element
34///   `Fp`
35/// - The most significant three bits (bits 0-3) of the first byte are reserved
36///   for encoding flags:
37///   - compression_flag (bit 0): Must always be unset (0), as only uncompressed
38///     points are supported.
39///   - infinity_flag (bit 1): Set if the point is the point at infinity (zero
40///     point), in which case all other bits must be zero.
41///   - sort_flag (bit 2): Must always be unset (0).
42///
43/// # Example Usage:
44/// ```rust
45/// use soroban_sdk::{Env, bytesn, crypto::bls12_381::{Bls12_381, Bls12381G1Affine}};
46/// let env = Env::default();
47/// let bls12_381 = env.crypto().bls12_381();
48/// let zero = Bls12381G1Affine::from_bytes(bytesn!(&env, 0x400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000));
49/// let one = Bls12381G1Affine::from_bytes(bytesn!(&env, 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1));
50/// let res = bls12_381.g1_add(&zero, &one);
51/// assert_eq!(res, one);
52/// ```
53#[derive(Clone)]
54#[repr(transparent)]
55pub struct Bls12381G1Affine(BytesN<G1_SERIALIZED_SIZE>);
56
57/// Type alias for `Bls12381G1Affine` for convenience
58pub type G1Affine = Bls12381G1Affine;
59
60/// `Bls12381G2Affine` is a point in the G2 group (subgroup defined over the quadratic
61/// extension field `Fq2`) of the BLS12-381 elliptic curve
62///
63/// # Serialization:
64/// - The 192 bytes represent the **uncompressed encoding** of a point in G2.
65///   The bytes consist of `be_bytes(X_c1) || be_bytes(X_c0) || be_bytes(Y_c1)
66///   || be_bytes(Y_c0)` (`||` is concatenation), where 'X' and 'Y' are the two
67///   coordinates, each being an extension field element `Fp2` and `c0`, `c1`
68///   are components of `Fp2` (each being `Fp`).
69/// - The most significant three bits (bits 0-3) of the first byte are reserved
70///   for encoding flags:
71///   - compression_flag (bit 0): Must always be unset (0), as only uncompressed
72///     points are supported.
73///   - infinity_flag (bit 1): Set if the point is the point at infinity (zero
74///     point), in which case all other bits must be zero.
75///   - sort_flag (bit 2): Must always be unset (0).
76#[derive(Clone)]
77#[repr(transparent)]
78pub struct Bls12381G2Affine(BytesN<G2_SERIALIZED_SIZE>);
79
80/// Type alias for `Bls12381G2Affine` for convenience
81pub type G2Affine = Bls12381G2Affine;
82
83/// `Bls12381Fp` represents an element of the base field `Fq` of the BLS12-381 elliptic
84/// curve
85///
86/// # Serialization:
87/// - The 48 bytes represent the **big-endian encoding** of an element in the
88///   field `Fp`. The value is serialized as a big-endian integer.
89#[derive(Clone)]
90#[repr(transparent)]
91pub struct Bls12381Fp(BytesN<FP_SERIALIZED_SIZE>);
92
93/// Type alias for `Bls12381Fp` for convenience
94pub type Fp = Bls12381Fp;
95
96/// `Bls12381Fp2` represents an element of the quadratic extension field `Fq2` of the
97/// BLS12-381 elliptic curve
98///
99/// # Serialization:
100/// - The 96 bytes represent the **big-endian encoding** of an element in the
101///   field `Fp2`. The bytes consist of `be_bytes(c1) || be_bytes(c0)` (`||` is
102///   concatenation), where `c0` and `c1` are the two `Fp` elements (the real
103///   and imaginary components).
104#[derive(Clone)]
105#[repr(transparent)]
106pub struct Bls12381Fp2(BytesN<FP2_SERIALIZED_SIZE>);
107
108/// Type alias for `Bls12381Fp2` for convenience
109pub type Fp2 = Bls12381Fp2;
110
111/// `Fr` represents an element in the BLS12-381 scalar field, which is a prime
112/// field of order `r` (the order of the G1 and G2 groups). The struct is
113/// internally represented with an `U256`, all arithmetic operations follow
114/// modulo `r`.
115#[derive(Clone)]
116#[repr(transparent)]
117pub struct Fr(U256);
118
119impl_bytesn_repr_without_from_bytes!(Bls12381G1Affine, G1_SERIALIZED_SIZE);
120impl_bytesn_repr_without_from_bytes!(Bls12381G2Affine, G2_SERIALIZED_SIZE);
121impl_bytesn_repr_without_from_bytes!(Bls12381Fp, FP_SERIALIZED_SIZE);
122impl_bytesn_repr_without_from_bytes!(Bls12381Fp2, FP2_SERIALIZED_SIZE);
123
124// BLS12-381 base field modulus p in big-endian bytes.
125// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
126const BLS12_381_FP_MODULUS_BE: [u8; FP_SERIALIZED_SIZE] = [
127    0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7,
128    0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24,
129    0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab,
130];
131
132fn validate_fp(bytes: &[u8; FP_SERIALIZED_SIZE]) {
133    if bytes >= &BLS12_381_FP_MODULUS_BE {
134        sdk_panic!("Bls12-381: Invalid Fp");
135    }
136}
137
138fn validate_fp2(bytes: &[u8; FP2_SERIALIZED_SIZE]) {
139    validate_fp(bytes[0..FP_SERIALIZED_SIZE].try_into().unwrap());
140    validate_fp(bytes[FP_SERIALIZED_SIZE..].try_into().unwrap());
141}
142
143impl Bls12381G1Affine {
144    pub fn from_bytes(bytes: BytesN<G1_SERIALIZED_SIZE>) -> Self {
145        Self(bytes)
146    }
147}
148
149impl Bls12381G2Affine {
150    pub fn from_bytes(bytes: BytesN<G2_SERIALIZED_SIZE>) -> Self {
151        Self(bytes)
152    }
153}
154
155impl Bls12381Fp {
156    pub fn from_bytes(bytes: BytesN<FP_SERIALIZED_SIZE>) -> Self {
157        validate_fp(&bytes.to_array());
158        Self(bytes)
159    }
160}
161
162impl Bls12381Fp2 {
163    pub fn from_bytes(bytes: BytesN<FP2_SERIALIZED_SIZE>) -> Self {
164        validate_fp2(&bytes.to_array());
165        Self(bytes)
166    }
167}
168
169impl Fp {
170    pub fn env(&self) -> &Env {
171        self.0.env()
172    }
173
174    // `Fp` represents an element in the base field of the BLS12-381 elliptic curve.
175    // For an element a ∈ Fp, its negation `-a` is defined as:
176    //   a + (-a) = 0 (mod p)
177    // where `p` is the field modulus, and to make a valid point coordinate on the
178    // curve, `a` also must be within the field range (i.e., 0 ≤ a < p).
179    fn checked_neg(&self) -> Option<Fp> {
180        let fp_bigint: BigInt<6> = (&self.0).into();
181        if fp_bigint.is_zero() {
182            return Some(self.clone());
183        }
184
185        // BLS12-381 base field modulus
186        const BLS12_381_MODULUS: [u64; 6] = [
187            13402431016077863595,
188            2210141511517208575,
189            7435674573564081700,
190            7239337960414712511,
191            5412103778470702295,
192            1873798617647539866,
193        ];
194        let mut res = BigInt(BLS12_381_MODULUS);
195
196        // Compute modulus - value
197        let borrow = res.sub_with_borrow(&fp_bigint);
198        if borrow {
199            return None;
200        }
201
202        let mut bytes = [0u8; FP_SERIALIZED_SIZE];
203        res.copy_into_array(&mut bytes);
204        Some(Fp::from_array(self.env(), &bytes))
205    }
206
207    /// Maps this `Fp` element to a `G1Affine` point using the [simplified SWU
208    /// mapping](https://www.rfc-editor.org/rfc/rfc9380.html#name-simplified-swu-for-ab-0).
209    ///
210    /// <div class="warning">
211    /// <h6>Warning</h6>
212    /// The resulting point is on the curve but may not be in the prime-order subgroup (operations
213    /// like pairing may fail). To ensure the point is in the prime-order subgroup, cofactor
214    /// clearing must be performed on the output.
215    ///
216    /// For applications requiring a point directly in the prime-order subgroup, consider using
217    /// `hash_to_g1`, which handles subgroup checks and cofactor clearing internally.
218    /// </div>
219    pub fn map_to_g1(&self) -> G1Affine {
220        self.env().crypto().bls12_381().map_fp_to_g1(self)
221    }
222}
223
224impl From<Fp> for BigInt<6> {
225    fn from(fp: Fp) -> Self {
226        let inner: Bytes = fp.0.into();
227        let mut limbs = [0u64; 6];
228        for i in 0..6u32 {
229            let start = i * 8;
230            let mut slice = [0u8; 8];
231            inner.slice(start..start + 8).copy_into_slice(&mut slice);
232            limbs[5 - i as usize] = u64::from_be_bytes(slice);
233        }
234        BigInt(limbs)
235    }
236}
237
238impl Neg for &Fp {
239    type Output = Fp;
240
241    fn neg(self) -> Self::Output {
242        match self.checked_neg() {
243            Some(v) => v,
244            None => sdk_panic!("invalid input - Fp is larger than the field modulus"),
245        }
246    }
247}
248
249impl Neg for Fp {
250    type Output = Fp;
251
252    fn neg(self) -> Self::Output {
253        (&self).neg()
254    }
255}
256
257impl G1Affine {
258    pub fn env(&self) -> &Env {
259        self.0.env()
260    }
261
262    pub fn is_in_subgroup(&self) -> bool {
263        self.env().crypto().bls12_381().g1_is_in_subgroup(self)
264    }
265
266    pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
267        self.env().crypto().bls12_381().g1_checked_add(self, rhs)
268    }
269}
270
271impl Add for G1Affine {
272    type Output = G1Affine;
273
274    fn add(self, rhs: Self) -> Self::Output {
275        self.env().crypto().bls12_381().g1_add(&self, &rhs)
276    }
277}
278
279impl Mul<Fr> for G1Affine {
280    type Output = G1Affine;
281
282    fn mul(self, rhs: Fr) -> Self::Output {
283        self.env().crypto().bls12_381().g1_mul(&self, &rhs)
284    }
285}
286
287// G1Affine represents a point (X, Y) on the BLS12-381 curve where X, Y ∈ Fp
288// Negation of (X, Y) is defined as (X, -Y)
289impl Neg for &G1Affine {
290    type Output = G1Affine;
291
292    fn neg(self) -> Self::Output {
293        let mut inner: Bytes = (&self.0).into();
294        let y = Fp::try_from_val(
295            inner.env(),
296            inner.slice(FP_SERIALIZED_SIZE as u32..).as_val(),
297        )
298        .unwrap_optimized();
299        let neg_y = -y;
300        inner.copy_from_slice(FP_SERIALIZED_SIZE as u32, &neg_y.to_array());
301        G1Affine::from_bytes(BytesN::try_from_val(inner.env(), inner.as_val()).unwrap_optimized())
302    }
303}
304
305impl Neg for G1Affine {
306    type Output = G1Affine;
307
308    fn neg(self) -> Self::Output {
309        (&self).neg()
310    }
311}
312
313impl Fp2 {
314    pub fn env(&self) -> &Env {
315        self.0.env()
316    }
317
318    // An Fp2 element is represented as c0 + c1 * X, where:
319    // - c0, c1 are base field elements (Fp)
320    // - X is the quadratic non-residue used to construct the field extension
321    // The negation of c0 + c1 * X is (-c0) + (-c1) * X.
322    fn checked_neg(&self) -> Option<Fp2> {
323        let mut inner = self.to_array();
324        let mut slice0 = [0; FP_SERIALIZED_SIZE];
325        let mut slice1 = [0; FP_SERIALIZED_SIZE];
326        slice0.copy_from_slice(&inner[0..FP_SERIALIZED_SIZE]);
327        slice1.copy_from_slice(&inner[FP_SERIALIZED_SIZE..FP2_SERIALIZED_SIZE]);
328
329        // Convert both components to Fp and negate them
330        let c0 = Fp::from_array(self.env(), &slice0);
331        let c1 = Fp::from_array(self.env(), &slice1);
332
333        // If either component's negation fails, the whole operation fails
334        let neg_c0 = c0.checked_neg()?;
335        let neg_c1 = c1.checked_neg()?;
336
337        // Reconstruct the Fp2 element from negated components
338        inner[0..FP_SERIALIZED_SIZE].copy_from_slice(&neg_c0.to_array());
339        inner[FP_SERIALIZED_SIZE..FP2_SERIALIZED_SIZE].copy_from_slice(&neg_c1.to_array());
340
341        Some(Fp2::from_array(self.env(), &inner))
342    }
343
344    /// Maps this `Fp2` element to a `G2Affine` point using the [simplified SWU
345    /// mapping](https://www.rfc-editor.org/rfc/rfc9380.html#name-simplified-swu-for-ab-0).
346    ///
347    /// <div class="warning">
348    /// <h6>Warning</h6>
349    /// The resulting point is on the curve but may not be in the prime-order subgroup (operations
350    /// like pairing may fail). To ensure the point is in the prime-order subgroup, cofactor
351    /// clearing must be performed on the output.
352    ///
353    /// For applications requiring a point directly in the prime-order subgroup, consider using
354    /// `hash_to_g2`, which handles subgroup checks and cofactor clearing internally.
355    /// </div>
356    pub fn map_to_g2(&self) -> G2Affine {
357        self.env().crypto().bls12_381().map_fp2_to_g2(self)
358    }
359}
360
361impl Neg for &Fp2 {
362    type Output = Fp2;
363
364    fn neg(self) -> Self::Output {
365        match self.checked_neg() {
366            Some(v) => v,
367            None => sdk_panic!("invalid input - Fp2 component is larger than the field modulus"),
368        }
369    }
370}
371
372impl Neg for Fp2 {
373    type Output = Fp2;
374
375    fn neg(self) -> Self::Output {
376        (&self).neg()
377    }
378}
379
380impl G2Affine {
381    pub fn env(&self) -> &Env {
382        self.0.env()
383    }
384
385    pub fn is_in_subgroup(&self) -> bool {
386        self.env().crypto().bls12_381().g2_is_in_subgroup(self)
387    }
388
389    pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
390        self.env().crypto().bls12_381().g2_checked_add(self, rhs)
391    }
392}
393
394impl Add for G2Affine {
395    type Output = G2Affine;
396
397    fn add(self, rhs: Self) -> Self::Output {
398        self.env().crypto().bls12_381().g2_add(&self, &rhs)
399    }
400}
401
402impl Mul<Fr> for G2Affine {
403    type Output = G2Affine;
404
405    fn mul(self, rhs: Fr) -> Self::Output {
406        self.env().crypto().bls12_381().g2_mul(&self, &rhs)
407    }
408}
409
410// G2Affine represents a point (X, Y) on the BLS12-381 quadratic extension curve where X, Y ∈ Fp2
411// Negation of (X, Y) is defined as (X, -Y)
412impl Neg for &G2Affine {
413    type Output = G2Affine;
414
415    fn neg(self) -> Self::Output {
416        let mut inner: Bytes = (&self.0).into();
417        let y = Fp2::try_from_val(
418            inner.env(),
419            inner.slice(FP2_SERIALIZED_SIZE as u32..).as_val(),
420        )
421        .unwrap_optimized();
422        let neg_y = -y;
423        inner.copy_from_slice(FP2_SERIALIZED_SIZE as u32, &neg_y.to_array());
424        G2Affine::from_bytes(BytesN::try_from_val(inner.env(), inner.as_val()).unwrap_optimized())
425    }
426}
427
428impl Neg for G2Affine {
429    type Output = G2Affine;
430
431    fn neg(self) -> Self::Output {
432        (&self).neg()
433    }
434}
435
436impl Fr {
437    pub fn env(&self) -> &Env {
438        self.0.env()
439    }
440
441    pub fn from_u256(value: U256) -> Self {
442        value.into()
443    }
444
445    pub fn to_u256(&self) -> U256 {
446        self.0.clone()
447    }
448
449    pub fn as_u256(&self) -> &U256 {
450        &self.0
451    }
452
453    pub fn from_bytes(bytes: BytesN<32>) -> Self {
454        U256::from_be_bytes(bytes.env(), bytes.as_ref()).into()
455    }
456
457    pub fn to_bytes(&self) -> BytesN<32> {
458        self.as_u256().to_be_bytes().try_into().unwrap_optimized()
459    }
460
461    pub fn as_val(&self) -> &Val {
462        self.0.as_val()
463    }
464
465    pub fn to_val(&self) -> Val {
466        self.0.to_val()
467    }
468
469    pub fn pow(&self, rhs: u64) -> Self {
470        self.env().crypto().bls12_381().fr_pow(self, rhs)
471    }
472
473    pub fn inv(&self) -> Self {
474        self.env().crypto().bls12_381().fr_inv(self)
475    }
476}
477
478// BLS12-381 scalar field modulus r in big-endian bytes.
479// r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
480const BLS12_381_FR_MODULUS_BE: [u8; 32] = [
481    0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05,
482    0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
483];
484
485fn fr_modulus(env: &Env) -> U256 {
486    U256::from_be_bytes(env, &Bytes::from_array(env, &BLS12_381_FR_MODULUS_BE))
487}
488
489impl From<U256> for Fr {
490    fn from(value: U256) -> Self {
491        // Keep all Fr construction paths canonical by reducing modulo r here.
492        // Constructors and deserialization paths should route through this impl.
493        // Skip the expensive rem_euclid when value is already canonical (< r),
494        // which is always the case for host-returned arithmetic results.
495        let modulus = fr_modulus(value.env());
496        if value >= modulus {
497            Self(value.rem_euclid(&modulus))
498        } else {
499            Self(value)
500        }
501    }
502}
503
504impl From<&Fr> for U256Val {
505    fn from(value: &Fr) -> Self {
506        value.as_u256().into()
507    }
508}
509
510impl TryFromVal<Env, Val> for Fr {
511    type Error = ConversionError;
512
513    fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
514        let u = U256::try_from_val(env, val)?;
515        Ok(u.into())
516    }
517}
518
519impl TryFromVal<Env, Fr> for Val {
520    type Error = ConversionError;
521
522    fn try_from_val(_env: &Env, fr: &Fr) -> Result<Self, Self::Error> {
523        Ok(fr.to_val())
524    }
525}
526
527impl TryFromVal<Env, &Fr> for Val {
528    type Error = ConversionError;
529
530    fn try_from_val(_env: &Env, fr: &&Fr) -> Result<Self, Self::Error> {
531        Ok(fr.to_val())
532    }
533}
534
535#[cfg(not(target_family = "wasm"))]
536impl From<&Fr> for ScVal {
537    fn from(v: &Fr) -> Self {
538        Self::from(&v.0)
539    }
540}
541
542#[cfg(not(target_family = "wasm"))]
543impl From<Fr> for ScVal {
544    fn from(v: Fr) -> Self {
545        (&v).into()
546    }
547}
548
549impl Eq for Fr {}
550
551impl PartialEq for Fr {
552    fn eq(&self, other: &Self) -> bool {
553        self.as_u256().partial_cmp(other.as_u256()) == Some(Ordering::Equal)
554    }
555}
556
557impl Debug for Fr {
558    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
559        write!(f, "Fr({:?})", self.as_u256())
560    }
561}
562
563impl Add for Fr {
564    type Output = Fr;
565
566    fn add(self, rhs: Self) -> Self::Output {
567        self.env().crypto().bls12_381().fr_add(&self, &rhs)
568    }
569}
570
571impl Sub for Fr {
572    type Output = Fr;
573
574    fn sub(self, rhs: Self) -> Self::Output {
575        self.env().crypto().bls12_381().fr_sub(&self, &rhs)
576    }
577}
578
579impl Mul for Fr {
580    type Output = Fr;
581
582    fn mul(self, rhs: Self) -> Self::Output {
583        self.env().crypto().bls12_381().fr_mul(&self, &rhs)
584    }
585}
586
587impl Bls12_381 {
588    pub(crate) fn new(env: &Env) -> Bls12_381 {
589        Bls12_381 { env: env.clone() }
590    }
591
592    pub fn env(&self) -> &Env {
593        &self.env
594    }
595
596    // g1
597
598    /// Checks if a point `p` in G1 is in the correct subgroup.
599    pub fn g1_is_in_subgroup(&self, p: &G1Affine) -> bool {
600        let env = self.env();
601        let res = internal::Env::bls12_381_check_g1_is_in_subgroup(env, p.to_object())
602            .unwrap_infallible();
603        res.into()
604    }
605
606    /// Adds two points `p0` and `p1` in G1.
607    pub fn g1_add(&self, p0: &G1Affine, p1: &G1Affine) -> G1Affine {
608        let env = self.env();
609        let bin = internal::Env::bls12_381_g1_add(env, p0.to_object(), p1.to_object())
610            .unwrap_infallible();
611        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
612    }
613
614    /// Adds two points `p0` and `p1` in G1, ensuring that the result is in the
615    /// correct subgroup. Note the subgroup check is computationally expensive,
616    /// so if want to perform a series of additions i.e. `agg = p0 + p1 + .. + pn`,
617    /// it may make sense to only call g1_checked_add on the final addition,
618    /// while using `g1_add` (non-checked version) on the intermediate ones.
619    pub fn g1_checked_add(&self, p0: &G1Affine, p1: &G1Affine) -> Option<G1Affine> {
620        let env = self.env();
621        let bin = internal::Env::bls12_381_g1_add(env, p0.to_object(), p1.to_object())
622            .unwrap_infallible();
623        let res = unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) };
624        let is_in_correct_subgroup: bool =
625            internal::Env::bls12_381_check_g1_is_in_subgroup(env, res.to_object())
626                .unwrap_optimized()
627                .into();
628        match is_in_correct_subgroup {
629            true => Some(res),
630            false => None,
631        }
632    }
633
634    /// Multiplies a point `p0` in G1 by a scalar.
635    pub fn g1_mul(&self, p0: &G1Affine, scalar: &Fr) -> G1Affine {
636        let env = self.env();
637        let bin =
638            internal::Env::bls12_381_g1_mul(env, p0.to_object(), scalar.into()).unwrap_infallible();
639        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
640    }
641
642    /// Performs a multi-scalar multiplication (MSM) operation in G1.
643    pub fn g1_msm(&self, vp: Vec<G1Affine>, vs: Vec<Fr>) -> G1Affine {
644        let env = self.env();
645        let bin = internal::Env::bls12_381_g1_msm(env, vp.into(), vs.into()).unwrap_infallible();
646        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
647    }
648
649    /// Maps an element in the base field `Fp` to a point in G1.
650    pub fn map_fp_to_g1(&self, fp: &Fp) -> G1Affine {
651        let env = self.env();
652        let bin = internal::Env::bls12_381_map_fp_to_g1(env, fp.to_object()).unwrap_infallible();
653        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
654    }
655
656    /// Hashes a message `msg` to a point in G1, using a domain separation tag `dst`.
657    pub fn hash_to_g1(&self, msg: &Bytes, dst: &Bytes) -> G1Affine {
658        let env = self.env();
659        let bin = internal::Env::bls12_381_hash_to_g1(env, msg.into(), dst.to_object())
660            .unwrap_infallible();
661        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
662    }
663
664    // g2
665
666    /// Checks if a point `p` in G2 is in the correct subgroup.
667    pub fn g2_is_in_subgroup(&self, p: &G2Affine) -> bool {
668        let env = self.env();
669        let res = internal::Env::bls12_381_check_g2_is_in_subgroup(env, p.to_object())
670            .unwrap_infallible();
671        res.into()
672    }
673
674    /// Adds two points `p0` and `p1` in G2.
675    pub fn g2_add(&self, p0: &G2Affine, p1: &G2Affine) -> G2Affine {
676        let env = self.env();
677        let bin = internal::Env::bls12_381_g2_add(env, p0.to_object(), p1.to_object())
678            .unwrap_infallible();
679        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
680    }
681
682    /// Adds two points `p0` and `p1` in G2, ensuring that the result is in the
683    /// correct subgroup. Note the subgroup check is computationally expensive,
684    /// so if want to perform a series of additions i.e. `agg = p0 + p1 + .. +pn`,     
685    /// it may make sense to only call g2_checked_add on the final addition,
686    /// while using `g2_add` (non-checked version) on the intermediate ones.
687    pub fn g2_checked_add(&self, p0: &G2Affine, p1: &G2Affine) -> Option<G2Affine> {
688        let env = self.env();
689        let bin = internal::Env::bls12_381_g2_add(env, p0.to_object(), p1.to_object())
690            .unwrap_infallible();
691        let res = unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) };
692        let is_in_correct_subgroup: bool =
693            internal::Env::bls12_381_check_g2_is_in_subgroup(env, res.to_object())
694                .unwrap_optimized()
695                .into();
696        match is_in_correct_subgroup {
697            true => Some(res),
698            false => None,
699        }
700    }
701
702    /// Multiplies a point `p0` in G2 by a scalar.
703    pub fn g2_mul(&self, p0: &G2Affine, scalar: &Fr) -> G2Affine {
704        let env = self.env();
705        let bin =
706            internal::Env::bls12_381_g2_mul(env, p0.to_object(), scalar.into()).unwrap_infallible();
707        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
708    }
709
710    /// Performs a multi-scalar multiplication (MSM) operation in G2.
711    pub fn g2_msm(&self, vp: Vec<G2Affine>, vs: Vec<Fr>) -> G2Affine {
712        let env = self.env();
713        let bin = internal::Env::bls12_381_g2_msm(env, vp.into(), vs.into()).unwrap_infallible();
714        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
715    }
716
717    /// Maps an element in the base field `Fp2` to a point in G2.
718    pub fn map_fp2_to_g2(&self, fp2: &Fp2) -> G2Affine {
719        let env = self.env();
720        let bin = internal::Env::bls12_381_map_fp2_to_g2(env, fp2.to_object()).unwrap_infallible();
721        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
722    }
723
724    /// Hashes a message `msg` to a point in G2, using a domain separation tag `dst`.
725    pub fn hash_to_g2(&self, msg: &Bytes, dst: &Bytes) -> G2Affine {
726        let env = self.env();
727        let bin = internal::Env::bls12_381_hash_to_g2(env, msg.into(), dst.to_object())
728            .unwrap_infallible();
729        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
730    }
731
732    // pairing
733
734    /// Performs a pairing check between vectors of points in G1 and G2.
735    ///
736    /// This function computes the pairing for each pair of points in the
737    /// provided vectors `vp1` (G1 points) and `vp2` (G2 points) and verifies if
738    /// the overall pairing result is equal to the identity in the target group.
739    ///
740    /// # Returns:
741    /// - `true` if the pairing check holds (i.e., the pairing result is valid
742    ///   and equal to the identity element), otherwise `false`.
743    ///
744    /// # Panics:
745    /// - If the lengths of `vp1` and `vp2` are not equal or if they are empty.
746    pub fn pairing_check(&self, vp1: Vec<G1Affine>, vp2: Vec<G2Affine>) -> bool {
747        let env = self.env();
748        internal::Env::bls12_381_multi_pairing_check(env, vp1.into(), vp2.into())
749            .unwrap_infallible()
750            .into()
751    }
752
753    // scalar arithmetic
754
755    /// Adds two scalars in the BLS12-381 scalar field `Fr`.
756    pub fn fr_add(&self, lhs: &Fr, rhs: &Fr) -> Fr {
757        let env = self.env();
758        let v = internal::Env::bls12_381_fr_add(env, lhs.into(), rhs.into()).unwrap_infallible();
759        U256::try_from_val(env, &v).unwrap_infallible().into()
760    }
761
762    /// Subtracts one scalar from another in the BLS12-381 scalar field `Fr`.
763    pub fn fr_sub(&self, lhs: &Fr, rhs: &Fr) -> Fr {
764        let env = self.env();
765        let v = internal::Env::bls12_381_fr_sub(env, lhs.into(), rhs.into()).unwrap_infallible();
766        U256::try_from_val(env, &v).unwrap_infallible().into()
767    }
768
769    /// Multiplies two scalars in the BLS12-381 scalar field `Fr`.
770    pub fn fr_mul(&self, lhs: &Fr, rhs: &Fr) -> Fr {
771        let env = self.env();
772        let v = internal::Env::bls12_381_fr_mul(env, lhs.into(), rhs.into()).unwrap_infallible();
773        U256::try_from_val(env, &v).unwrap_infallible().into()
774    }
775
776    /// Raises a scalar to the power of a given exponent in the BLS12-381 scalar field `Fr`.
777    pub fn fr_pow(&self, lhs: &Fr, rhs: u64) -> Fr {
778        let env = self.env();
779        let rhs = U64Val::try_from_val(env, &rhs).unwrap_optimized();
780        let v = internal::Env::bls12_381_fr_pow(env, lhs.into(), rhs).unwrap_infallible();
781        U256::try_from_val(env, &v).unwrap_infallible().into()
782    }
783
784    /// Computes the multiplicative inverse of a scalar in the BLS12-381 scalar field `Fr`.
785    pub fn fr_inv(&self, lhs: &Fr) -> Fr {
786        let env = self.env();
787        let v = internal::Env::bls12_381_fr_inv(env, lhs.into()).unwrap_infallible();
788        U256::try_from_val(env, &v).unwrap_infallible().into()
789    }
790}
791
792#[cfg(test)]
793mod test {
794    use super::*;
795    use crate::bytesn;
796
797    #[test]
798    fn test_g1affine_to_val() {
799        let env = Env::default();
800
801        let g1 = G1Affine::from_bytes(BytesN::from_array(&env, &[1; 96]));
802        let val: Val = g1.clone().into_val(&env);
803        let rt: G1Affine = val.into_val(&env);
804
805        assert_eq!(g1, rt);
806    }
807
808    #[test]
809    fn test_ref_g1affine_to_val() {
810        let env = Env::default();
811
812        let g1 = G1Affine::from_bytes(BytesN::from_array(&env, &[1; 96]));
813        let val: Val = (&g1).into_val(&env);
814        let rt: G1Affine = val.into_val(&env);
815
816        assert_eq!(g1, rt);
817    }
818
819    #[test]
820    fn test_doule_ref_g1affine_to_val() {
821        let env = Env::default();
822
823        let g1 = G1Affine::from_bytes(BytesN::from_array(&env, &[1; 96]));
824        let val: Val = (&&g1).into_val(&env);
825        let rt: G1Affine = val.into_val(&env);
826
827        assert_eq!(g1, rt);
828    }
829
830    #[test]
831    fn test_fr_to_val() {
832        let env = Env::default();
833
834        let fr = Fr::from_bytes(BytesN::from_array(&env, &[1; 32]));
835        let val: Val = fr.clone().into_val(&env);
836        let rt: Fr = val.into_val(&env);
837
838        assert_eq!(fr, rt);
839    }
840
841    #[test]
842    fn test_ref_fr_to_val() {
843        let env = Env::default();
844
845        let fr = Fr::from_bytes(BytesN::from_array(&env, &[1; 32]));
846        let val: Val = (&fr).into_val(&env);
847        let rt: Fr = val.into_val(&env);
848
849        assert_eq!(fr, rt);
850    }
851
852    #[test]
853    fn test_double_ref_fr_to_val() {
854        let env = Env::default();
855
856        let fr = Fr::from_bytes(BytesN::from_array(&env, &[1; 32]));
857        let val: Val = (&&fr).into_val(&env);
858        let rt: Fr = val.into_val(&env);
859
860        assert_eq!(fr, rt);
861    }
862
863    #[test]
864    fn test_fr_eq_both_unreduced() {
865        let env = Env::default();
866        let r = fr_modulus(&env);
867        let one = U256::from_u32(&env, 1);
868
869        let a = Fr::from_u256(r.add(&one));
870        let b = Fr::from_u256(one.clone());
871        assert_eq!(a, b);
872
873        let two_r_plus_one = r.add(&r).add(&one);
874        let c = Fr::from_u256(two_r_plus_one);
875        assert_eq!(a, c);
876        assert_eq!(b, c);
877    }
878
879    #[test]
880    fn test_fr_eq_unreduced_vs_zero() {
881        let env = Env::default();
882        let r = fr_modulus(&env);
883        let zero = U256::from_u32(&env, 0);
884
885        let a = Fr::from_u256(r);
886        let b = Fr::from_u256(zero);
887        assert_eq!(a, b);
888    }
889
890    #[test]
891    fn test_fr_reduced_value_unchanged() {
892        let env = Env::default();
893        let r = fr_modulus(&env);
894        let val = r.sub(&U256::from_u32(&env, 1));
895
896        let fr = Fr::from_u256(val.clone());
897        assert_eq!(fr.to_u256(), val);
898
899        let fr42 = Fr::from_u256(U256::from_u32(&env, 42));
900        assert_eq!(fr42.to_u256(), U256::from_u32(&env, 42));
901    }
902
903    #[test]
904    fn test_fr_from_bytes_reduces() {
905        let env = Env::default();
906        let one_fr = Fr::from_u256(U256::from_u32(&env, 1));
907
908        // BLS12-381 r+1 as big-endian bytes
909        let fr_from_bytes = Fr::from_bytes(bytesn!(
910            &env,
911            0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002
912        ));
913        assert_eq!(fr_from_bytes, one_fr);
914    }
915
916    #[test]
917    fn test_fr_try_from_val_reduces() {
918        let env = Env::default();
919        let r = fr_modulus(&env);
920        let one = U256::from_u32(&env, 1);
921
922        let unreduced_u256 = r.add(&one);
923        let val: Val = unreduced_u256.into_val(&env);
924        let fr_from_val: Fr = val.into_val(&env);
925        let fr_one = Fr::from_u256(one);
926        assert_eq!(fr_from_val, fr_one);
927    }
928
929    #[test]
930    fn test_fr_u256_into_reduces() {
931        // Direct From<U256>::from / .into() path must reduce
932        let env = Env::default();
933        let r = fr_modulus(&env);
934        let one = U256::from_u32(&env, 1);
935
936        let fr: Fr = r.add(&one).into(); // r+1 via .into()
937        let fr_one: Fr = one.into();
938        assert_eq!(fr, fr_one);
939    }
940
941    #[test]
942    fn test_fr_eq_unreduced_vs_host_computed() {
943        // User-provided unreduced Fr vs host-computed Fr
944        let env = Env::default();
945        let bls = Bls12_381::new(&env);
946        let r = fr_modulus(&env);
947        let five = U256::from_u32(&env, 5);
948
949        // User provides r+5 (unreduced)
950        let user_fr = Fr::from_u256(r.add(&five));
951        // Host computes 2+3 = 5 (always reduced)
952        let host_fr = bls.fr_add(
953            &Fr::from_u256(U256::from_u32(&env, 2)),
954            &Fr::from_u256(U256::from_u32(&env, 3)),
955        );
956        assert_eq!(user_fr, host_fr);
957    }
958
959    // Fp validation tests
960
961    #[test]
962    fn test_fp_max_valid_accepted() {
963        let env = Env::default();
964        // p - 1 (last byte 0xaa instead of 0xab)
965        let mut p_minus_1 = BLS12_381_FP_MODULUS_BE;
966        p_minus_1[FP_SERIALIZED_SIZE - 1] -= 1;
967        let _ = Fp::from_array(&env, &p_minus_1);
968    }
969
970    #[test]
971    #[should_panic(expected = "Bls12-381: Invalid Fp")]
972    fn test_fp_at_modulus_panics() {
973        let env = Env::default();
974        let _ = Fp::from_array(&env, &BLS12_381_FP_MODULUS_BE);
975    }
976
977    #[test]
978    #[should_panic(expected = "Bls12-381: Invalid Fp")]
979    fn test_fp_above_modulus_panics() {
980        let env = Env::default();
981        let mut above = BLS12_381_FP_MODULUS_BE;
982        above[FP_SERIALIZED_SIZE - 1] += 1; // p + 1
983        let _ = Fp::from_array(&env, &above);
984    }
985
986    #[test]
987    fn test_fp_from_bytes_validates() {
988        let env = Env::default();
989        // Zero should be valid
990        let _ = Fp::from_bytes(BytesN::from_array(&env, &[0u8; FP_SERIALIZED_SIZE]));
991    }
992
993    #[test]
994    #[should_panic(expected = "Bls12-381: Invalid Fp")]
995    fn test_fp_from_bytes_rejects_modulus() {
996        let env = Env::default();
997        let _ = Fp::from_bytes(BytesN::from_array(&env, &BLS12_381_FP_MODULUS_BE));
998    }
999
1000    #[test]
1001    #[should_panic(expected = "Bls12-381: Invalid Fp")]
1002    fn test_fp_try_from_val_rejects_modulus() {
1003        let env = Env::default();
1004        let bytes = BytesN::from_array(&env, &BLS12_381_FP_MODULUS_BE);
1005        let val: Val = bytes.into_val(&env);
1006        let _: Fp = val.into_val(&env);
1007    }
1008
1009    #[test]
1010    #[should_panic(expected = "Bls12-381: Invalid Fp")]
1011    fn test_fp2_component_above_modulus_panics() {
1012        let env = Env::default();
1013        // First Fp component is the modulus (invalid), second is zero (valid)
1014        let mut fp2_bytes = [0u8; FP2_SERIALIZED_SIZE];
1015        fp2_bytes[0..FP_SERIALIZED_SIZE].copy_from_slice(&BLS12_381_FP_MODULUS_BE);
1016        let _ = Fp2::from_array(&env, &fp2_bytes);
1017    }
1018
1019    #[test]
1020    #[should_panic(expected = "Bls12-381: Invalid Fp")]
1021    fn test_fp2_second_component_above_modulus_panics() {
1022        let env = Env::default();
1023        // First Fp component is zero (valid), second is the modulus (invalid)
1024        let mut fp2_bytes = [0u8; FP2_SERIALIZED_SIZE];
1025        fp2_bytes[FP_SERIALIZED_SIZE..].copy_from_slice(&BLS12_381_FP_MODULUS_BE);
1026        let _ = Fp2::from_array(&env, &fp2_bytes);
1027    }
1028
1029    #[test]
1030    fn test_fp2_max_valid_accepted() {
1031        let env = Env::default();
1032        // Both components are p-1 (valid)
1033        let mut p_minus_1 = BLS12_381_FP_MODULUS_BE;
1034        p_minus_1[FP_SERIALIZED_SIZE - 1] -= 1;
1035        let mut fp2_bytes = [0u8; FP2_SERIALIZED_SIZE];
1036        fp2_bytes[0..FP_SERIALIZED_SIZE].copy_from_slice(&p_minus_1);
1037        fp2_bytes[FP_SERIALIZED_SIZE..].copy_from_slice(&p_minus_1);
1038        let _ = Fp2::from_array(&env, &fp2_bytes);
1039    }
1040
1041    #[test]
1042    fn test_bls12_381_fp_modulus_matches_arkworks() {
1043        use ark_bls12_381::Fq;
1044        use ark_ff::{BigInteger, PrimeField};
1045
1046        let be_bytes = Fq::MODULUS.to_bytes_be();
1047        assert_eq!(
1048            be_bytes.as_slice(),
1049            &BLS12_381_FP_MODULUS_BE,
1050            "BLS12-381 Fp modulus does not match arkworks"
1051        );
1052    }
1053
1054    #[test]
1055    fn test_bls12_381_fr_modulus_matches_arkworks() {
1056        use ark_bls12_381::Fr as ArkFr;
1057        use ark_ff::{BigInteger, PrimeField};
1058
1059        let be_bytes = ArkFr::MODULUS.to_bytes_be();
1060        assert_eq!(
1061            be_bytes.as_slice(),
1062            &BLS12_381_FR_MODULUS_BE,
1063            "BLS12-381 Fr modulus does not match arkworks"
1064        );
1065    }
1066}