soroban_sdk/crypto/
bls12_381.rs

1use crate::{
2    env::internal::{self, BytesObject, U256Val, U64Val},
3    impl_bytesn_repr,
4    unwrap::{UnwrapInfallible, UnwrapOptimized},
5    Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val, Vec, U256,
6};
7use core::{
8    cmp::Ordering,
9    fmt::Debug,
10    ops::{Add, Mul, Sub},
11};
12
13/// Bls12_381 provides access to curve and field arithmetics on the BLS12-381
14/// curve.
15pub struct Bls12_381 {
16    env: Env,
17}
18
19/// `G1Affine` is a point in the G1 group (subgroup defined over the base field
20///  `Fq`) of the BLS12-381 elliptic curve
21///
22/// # Serialization:
23/// - The 96 bytes represent the **uncompressed encoding** of a point in G1. The
24///   Bytes consist of `be_byte(X) || be_byte(Y)`  (`||` is concatenation),
25///   where 'X' and 'Y' are the two coordinates, each being a base field element
26///   `Fp`
27/// - The most significant three bits (bits 0-3) of the first byte are reserved
28///   for encoding flags:
29///   - compression_flag (bit 0): Must always be set (1), as only uncompressed
30///     points are supported.
31///   - infinity_flag (bit 1): Set if the point is the point at infinity (zero
32///     point), in which case all other bits must be zero.
33///   - sort_flag (bit 2): Must always be unset (0).
34///
35/// # Example Usage:
36/// ```rust
37/// use soroban_sdk::{Env, bytesn, crypto::bls12_381::{Bls12_381, G1Affine}};
38/// let env = Env::default();
39/// let bls12_381 = env.crypto().bls12_381();
40/// let zero = G1Affine::from_bytes(bytesn!(&env, 0x400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000));
41/// let one = G1Affine::from_bytes(bytesn!(&env, 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1));
42/// let res = bls12_381.g1_add(&zero, &one);
43/// assert_eq!(res, one);
44/// ```
45#[derive(Clone)]
46#[repr(transparent)]
47pub struct G1Affine(BytesN<96>);
48
49/// `G2Affine` is a point in the G2 group (subgroup defined over the quadratic
50/// extension field `Fq2`) of the BLS12-381 elliptic curve
51///
52/// # Serialization:
53/// - The 192 bytes represent the **uncompressed encoding** of a point in G2.
54///   The bytes consist of `be_bytes(X_c1) || be_bytes(X_c0) || be_bytes(Y_c1)
55///   || be_bytes(Y_c0)` (`||` is concatenation), where 'X' and 'Y' are the two
56///   coordinates, each being an extension field element `Fp2` and `c0`, `c1`
57///   are components of `Fp2` (each being `Fp`).
58/// - The most significant three bits (bits 0-3) of the first byte are reserved
59///   for encoding flags:
60///   - compression_flag (bit 0): Must always be set (1), as only uncompressed
61///     points are supported.
62///   - infinity_flag (bit 1): Set if the point is the point at infinity (zero
63///     point), in which case all other bits must be zero.
64///   - sort_flag (bit 2): Must always be unset (0).
65#[derive(Clone)]
66#[repr(transparent)]
67pub struct G2Affine(BytesN<192>);
68
69/// `Fp` represents an element of the base field `Fq` of the BLS12-381 elliptic
70/// curve
71///
72/// # Serialization:
73/// - The 48 bytes represent the **big-endian encoding** of an element in the
74///   field `Fp`. The value is serialized as a big-endian integer.
75#[derive(Clone)]
76#[repr(transparent)]
77pub struct Fp(BytesN<48>);
78
79/// `Fp2` represents an element of the quadratic extension field `Fq2` of the
80/// BLS12-381 elliptic curve
81///
82/// # Serialization:
83/// - The 96 bytes represent the **big-endian encoding** of an element in the
84///   field `Fp2`. The bytes consist of `be_bytes(c1) || be_bytes(c0)` (`||` is
85///   concatenation), where `c0` and `c1` are the two `Fp` elements (the real
86///   and imaginary components).
87#[derive(Clone)]
88#[repr(transparent)]
89pub struct Fp2(BytesN<96>);
90
91/// `Fr` represents an element in the BLS12-381 scalar field, which is a prime
92/// field of order `r` (the order of the G1 and G2 groups). The struct is
93/// internally represented with an `U256`, all arithmetic operations follow
94/// modulo `r`.
95#[derive(Clone)]
96#[repr(transparent)]
97pub struct Fr(U256);
98
99impl_bytesn_repr!(G1Affine, 96);
100impl_bytesn_repr!(G2Affine, 192);
101impl_bytesn_repr!(Fp, 48);
102impl_bytesn_repr!(Fp2, 96);
103
104impl G1Affine {
105    pub fn env(&self) -> &Env {
106        self.0.env()
107    }
108
109    pub fn is_in_subgroup(&self) -> bool {
110        self.env().crypto().bls12_381().g1_is_in_subgroup(self)
111    }
112
113    pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
114        self.env().crypto().bls12_381().g1_checked_add(self, rhs)
115    }
116}
117
118impl Add for G1Affine {
119    type Output = G1Affine;
120
121    fn add(self, rhs: Self) -> Self::Output {
122        self.env().crypto().bls12_381().g1_add(&self, &rhs)
123    }
124}
125
126impl Mul<Fr> for G1Affine {
127    type Output = G1Affine;
128
129    fn mul(self, rhs: Fr) -> Self::Output {
130        self.env().crypto().bls12_381().g1_mul(&self, &rhs)
131    }
132}
133
134impl G2Affine {
135    pub fn env(&self) -> &Env {
136        self.0.env()
137    }
138
139    pub fn is_in_subgroup(&self) -> bool {
140        self.env().crypto().bls12_381().g2_is_in_subgroup(self)
141    }
142
143    pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
144        self.env().crypto().bls12_381().g2_checked_add(self, rhs)
145    }
146}
147
148impl Add for G2Affine {
149    type Output = G2Affine;
150
151    fn add(self, rhs: Self) -> Self::Output {
152        self.env().crypto().bls12_381().g2_add(&self, &rhs)
153    }
154}
155
156impl Mul<Fr> for G2Affine {
157    type Output = G2Affine;
158
159    fn mul(self, rhs: Fr) -> Self::Output {
160        self.env().crypto().bls12_381().g2_mul(&self, &rhs)
161    }
162}
163
164impl Fp {
165    pub fn env(&self) -> &Env {
166        self.0.env()
167    }
168
169    pub fn map_to_g1(&self) -> G1Affine {
170        self.env().crypto().bls12_381().map_fp_to_g1(self)
171    }
172}
173
174impl Fp2 {
175    pub fn env(&self) -> &Env {
176        self.0.env()
177    }
178
179    pub fn map_to_g2(&self) -> G2Affine {
180        self.env().crypto().bls12_381().map_fp2_to_g2(self)
181    }
182}
183
184impl Fr {
185    pub fn env(&self) -> &Env {
186        self.0.env()
187    }
188
189    pub fn from_u256(value: U256) -> Self {
190        value.into()
191    }
192
193    pub fn to_u256(&self) -> U256 {
194        self.0.clone()
195    }
196
197    pub fn as_u256(&self) -> &U256 {
198        &self.0
199    }
200
201    pub fn from_bytes(bytes: BytesN<32>) -> Self {
202        U256::from_be_bytes(bytes.env(), bytes.as_ref()).into()
203    }
204
205    pub fn to_bytes(&self) -> BytesN<32> {
206        self.as_u256().to_be_bytes().try_into().unwrap_optimized()
207    }
208
209    pub fn as_val(&self) -> &Val {
210        self.0.as_val()
211    }
212
213    pub fn to_val(&self) -> Val {
214        self.0.to_val()
215    }
216
217    pub fn pow(&self, rhs: u64) -> Self {
218        self.env().crypto().bls12_381().fr_pow(self, rhs)
219    }
220
221    pub fn inv(&self) -> Self {
222        self.env().crypto().bls12_381().fr_inv(self)
223    }
224}
225
226impl From<U256> for Fr {
227    fn from(value: U256) -> Self {
228        Self(value)
229    }
230}
231
232impl From<&Fr> for U256Val {
233    fn from(value: &Fr) -> Self {
234        value.as_u256().into()
235    }
236}
237
238impl IntoVal<Env, Val> for Fr {
239    fn into_val(&self, e: &Env) -> Val {
240        self.0.into_val(e)
241    }
242}
243
244impl TryFromVal<Env, Val> for Fr {
245    type Error = ConversionError;
246
247    fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
248        let u = U256::try_from_val(env, val)?;
249        Ok(Fr(u))
250    }
251}
252
253impl Eq for Fr {}
254
255impl PartialEq for Fr {
256    fn eq(&self, other: &Self) -> bool {
257        self.as_u256().partial_cmp(other.as_u256()) == Some(Ordering::Equal)
258    }
259}
260
261impl Debug for Fr {
262    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
263        write!(f, "Fr({:?})", self.as_u256())
264    }
265}
266
267impl Add for Fr {
268    type Output = Fr;
269
270    fn add(self, rhs: Self) -> Self::Output {
271        self.env().crypto().bls12_381().fr_add(&self, &rhs)
272    }
273}
274
275impl Sub for Fr {
276    type Output = Fr;
277
278    fn sub(self, rhs: Self) -> Self::Output {
279        self.env().crypto().bls12_381().fr_sub(&self, &rhs)
280    }
281}
282
283impl Mul for Fr {
284    type Output = Fr;
285
286    fn mul(self, rhs: Self) -> Self::Output {
287        self.env().crypto().bls12_381().fr_mul(&self, &rhs)
288    }
289}
290
291impl Bls12_381 {
292    pub(crate) fn new(env: &Env) -> Bls12_381 {
293        Bls12_381 { env: env.clone() }
294    }
295
296    pub fn env(&self) -> &Env {
297        &self.env
298    }
299
300    // g1
301
302    /// Checks if a point `p` in G1 is in the correct subgroup.
303    pub fn g1_is_in_subgroup(&self, p: &G1Affine) -> bool {
304        let env = self.env();
305        let res = internal::Env::bls12_381_check_g1_is_in_subgroup(env, p.to_object())
306            .unwrap_infallible();
307        res.into()
308    }
309
310    /// Adds two points `p0` and `p1` in G1.
311    pub fn g1_add(&self, p0: &G1Affine, p1: &G1Affine) -> G1Affine {
312        let env = self.env();
313        let bin = internal::Env::bls12_381_g1_add(env, p0.to_object(), p1.to_object())
314            .unwrap_infallible();
315        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
316    }
317
318    /// Adds two points `p0` and `p1` in G1, ensuring that the result is in the
319    /// correct subgroup. Note the subgroup check is computationally expensive,
320    /// so if want to perform a series of additions i.e. `agg = p0 + p1 + .. + pn`,
321    /// it may make sense to only call g1_checked_add on the final addition,
322    /// while using `g1_add` (non-checked version) on the intermediate ones.
323    pub fn g1_checked_add(&self, p0: &G1Affine, p1: &G1Affine) -> Option<G1Affine> {
324        let env = self.env();
325        let bin = internal::Env::bls12_381_g1_add(env, p0.to_object(), p1.to_object())
326            .unwrap_infallible();
327        let res = unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) };
328        let is_in_correct_subgroup: bool =
329            internal::Env::bls12_381_check_g1_is_in_subgroup(env, res.to_object())
330                .unwrap_optimized()
331                .into();
332        match is_in_correct_subgroup {
333            true => Some(res),
334            false => None,
335        }
336    }
337
338    /// Multiplies a point `p0` in G1 by a scalar.
339    pub fn g1_mul(&self, p0: &G1Affine, scalar: &Fr) -> G1Affine {
340        let env = self.env();
341        let bin =
342            internal::Env::bls12_381_g1_mul(env, p0.to_object(), scalar.into()).unwrap_infallible();
343        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
344    }
345
346    /// Performs a multi-scalar multiplication (MSM) operation in G1.
347    pub fn g1_msm(&self, vp: Vec<G1Affine>, vs: Vec<Fr>) -> G1Affine {
348        let env = self.env();
349        let bin = internal::Env::bls12_381_g1_msm(env, vp.into(), vs.into()).unwrap_infallible();
350        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
351    }
352
353    /// Maps an element in the base field `Fp` to a point in G1.
354    pub fn map_fp_to_g1(&self, fp: &Fp) -> G1Affine {
355        let env = self.env();
356        let bin = internal::Env::bls12_381_map_fp_to_g1(env, fp.to_object()).unwrap_infallible();
357        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
358    }
359
360    /// Hashes a message `msg` to a point in G1, using a domain separation tag `dst`.
361    pub fn hash_to_g1(&self, msg: &Bytes, dst: &Bytes) -> G1Affine {
362        let env = self.env();
363        let bin = internal::Env::bls12_381_hash_to_g1(env, msg.into(), dst.to_object())
364            .unwrap_infallible();
365        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
366    }
367
368    // g2
369
370    /// Checks if a point `p` in G2 is in the correct subgroup.
371    pub fn g2_is_in_subgroup(&self, p: &G2Affine) -> bool {
372        let env = self.env();
373        let res = internal::Env::bls12_381_check_g2_is_in_subgroup(env, p.to_object())
374            .unwrap_infallible();
375        res.into()
376    }
377
378    /// Adds two points `p0` and `p1` in G2.
379    pub fn g2_add(&self, p0: &G2Affine, p1: &G2Affine) -> G2Affine {
380        let env = self.env();
381        let bin = internal::Env::bls12_381_g2_add(env, p0.to_object(), p1.to_object())
382            .unwrap_infallible();
383        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
384    }
385
386    /// Adds two points `p0` and `p1` in G2, ensuring that the result is in the
387    /// correct subgroup. Note the subgroup check is computationally expensive,
388    /// so if want to perform a series of additions i.e. `agg = p0 + p1 + .. +pn`,     
389    /// it may make sense to only call g2_checked_add on the final addition,
390    /// while using `g2_add` (non-checked version) on the intermediate ones.
391    pub fn g2_checked_add(&self, p0: &G2Affine, p1: &G2Affine) -> Option<G2Affine> {
392        let env = self.env();
393        let bin = internal::Env::bls12_381_g2_add(env, p0.to_object(), p1.to_object())
394            .unwrap_infallible();
395        let res = unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) };
396        let is_in_correct_subgroup: bool =
397            internal::Env::bls12_381_check_g2_is_in_subgroup(env, res.to_object())
398                .unwrap_optimized()
399                .into();
400        match is_in_correct_subgroup {
401            true => Some(res),
402            false => None,
403        }
404    }
405
406    /// Multiplies a point `p0` in G2 by a scalar.
407    pub fn g2_mul(&self, p0: &G2Affine, scalar: &Fr) -> G2Affine {
408        let env = self.env();
409        let bin =
410            internal::Env::bls12_381_g2_mul(env, p0.to_object(), scalar.into()).unwrap_infallible();
411        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
412    }
413
414    /// Performs a multi-scalar multiplication (MSM) operation in G2.
415    pub fn g2_msm(&self, vp: Vec<G2Affine>, vs: Vec<Fr>) -> G2Affine {
416        let env = self.env();
417        let bin = internal::Env::bls12_381_g2_msm(env, vp.into(), vs.into()).unwrap_infallible();
418        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
419    }
420
421    /// Maps an element in the base field `Fp2` to a point in G2.
422    pub fn map_fp2_to_g2(&self, fp2: &Fp2) -> G2Affine {
423        let env = self.env();
424        let bin = internal::Env::bls12_381_map_fp2_to_g2(env, fp2.to_object()).unwrap_infallible();
425        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
426    }
427
428    /// Hashes a message `msg` to a point in G2, using a domain separation tag `dst`.
429    pub fn hash_to_g2(&self, msg: &Bytes, dst: &Bytes) -> G2Affine {
430        let env = self.env();
431        let bin = internal::Env::bls12_381_hash_to_g2(env, msg.into(), dst.to_object())
432            .unwrap_infallible();
433        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
434    }
435
436    // pairing
437
438    /// Performs a pairing check between vectors of points in G1 and G2.
439    ///
440    /// This function computes the pairing for each pair of points in the
441    /// provided vectors `vp1` (G1 points) and `vp2` (G2 points) and verifies if
442    /// the overall pairing result is equal to the identity in the target group.
443    ///
444    /// # Returns:
445    /// - `true` if the pairing check holds (i.e., the pairing result is valid
446    ///   and equal to the identity element), otherwise `false`.
447    ///
448    /// # Panics:
449    /// - If the lengths of `vp1` and `vp2` are not equal or if they are empty.
450    pub fn pairing_check(&self, vp1: Vec<G1Affine>, vp2: Vec<G2Affine>) -> bool {
451        let env = self.env();
452        internal::Env::bls12_381_multi_pairing_check(env, vp1.into(), vp2.into())
453            .unwrap_infallible()
454            .into()
455    }
456
457    // scalar arithmetic
458
459    /// Adds two scalars in the BLS12-381 scalar field `Fr`.
460    pub fn fr_add(&self, lhs: &Fr, rhs: &Fr) -> Fr {
461        let env = self.env();
462        let v = internal::Env::bls12_381_fr_add(env, lhs.into(), rhs.into()).unwrap_infallible();
463        U256::try_from_val(env, &v).unwrap_infallible().into()
464    }
465
466    /// Subtracts one scalar from another in the BLS12-381 scalar field `Fr`.
467    pub fn fr_sub(&self, lhs: &Fr, rhs: &Fr) -> Fr {
468        let env = self.env();
469        let v = internal::Env::bls12_381_fr_sub(env, lhs.into(), rhs.into()).unwrap_infallible();
470        U256::try_from_val(env, &v).unwrap_infallible().into()
471    }
472
473    /// Multiplies two scalars in the BLS12-381 scalar field `Fr`.
474    pub fn fr_mul(&self, lhs: &Fr, rhs: &Fr) -> Fr {
475        let env = self.env();
476        let v = internal::Env::bls12_381_fr_mul(env, lhs.into(), rhs.into()).unwrap_infallible();
477        U256::try_from_val(env, &v).unwrap_infallible().into()
478    }
479
480    /// Raises a scalar to the power of a given exponent in the BLS12-381 scalar field `Fr`.
481    pub fn fr_pow(&self, lhs: &Fr, rhs: u64) -> Fr {
482        let env = self.env();
483        let rhs = U64Val::try_from_val(env, &rhs).unwrap_optimized();
484        let v = internal::Env::bls12_381_fr_pow(env, lhs.into(), rhs).unwrap_infallible();
485        U256::try_from_val(env, &v).unwrap_infallible().into()
486    }
487
488    /// Computes the multiplicative inverse of a scalar in the BLS12-381 scalar field `Fr`.
489    pub fn fr_inv(&self, lhs: &Fr) -> Fr {
490        let env = self.env();
491        let v = internal::Env::bls12_381_fr_inv(env, lhs.into()).unwrap_infallible();
492        U256::try_from_val(env, &v).unwrap_infallible().into()
493    }
494}