@@ -7,15 +7,79 @@ use crate::{
77use core:: {
88 cmp:: Ordering ,
99 fmt:: Debug ,
10- ops:: { Add , Mul , Sub } ,
10+ ops:: { Add , Mul , Neg , Sub } ,
1111} ;
1212
13+ pub 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.
14+ pub const FP2_SERIALIZED_SIZE : usize = FP_SERIALIZED_SIZE * 2 ;
15+ pub const G1_SERIALIZED_SIZE : usize = FP_SERIALIZED_SIZE * 2 ;
16+ pub const G2_SERIALIZED_SIZE : usize = FP2_SERIALIZED_SIZE * 2 ;
17+
1318/// Bls12_381 provides access to curve and field arithmetics on the BLS12-381
1419/// curve.
1520pub struct Bls12_381 {
1621 env : Env ,
1722}
1823
24+ // This routine was copied with slight modification from the arkworks library:
25+ // https://github.com/arkworks-rs/algebra/blob/bf1c9b22b30325ef4df4f701dedcb6dea904c587/ff/src/biginteger/arithmetic.rs#L66-L79
26+ fn sbb_for_sub_with_borrow ( a : & mut u64 , b : u64 , borrow : u8 ) -> u8 {
27+ let tmp = ( 1u128 << 64 ) + u128:: from ( * a) - u128:: from ( b) - u128:: from ( borrow) ;
28+ // casting is safe here because `tmp` can only exceed u64 by a single
29+ // (borrow) bit, which we capture in the next line.
30+ * a = tmp as u64 ;
31+ u8:: from ( tmp >> 64 == 0 )
32+ }
33+
34+ #[ derive( Debug ) ]
35+ pub ( crate ) struct BigInt < const N : usize > ( pub [ u64 ; N ] ) ;
36+
37+ impl < const N : usize > BigInt < N > {
38+ pub fn sub_with_borrow ( & mut self , other : & Self ) -> bool {
39+ let mut borrow = 0 ;
40+ for i in 0 ..N {
41+ borrow = sbb_for_sub_with_borrow ( & mut self . 0 [ i] , other. 0 [ i] , borrow) ;
42+ }
43+ borrow != 0
44+ }
45+
46+ pub fn copy_into_array < const M : usize > ( & self , slice : & mut [ u8 ; M ] ) {
47+ const {
48+ if M != N * 8 {
49+ panic ! ( "BigInt::copy_into_array with mismatched array length" )
50+ }
51+ }
52+
53+ for i in 0 ..N {
54+ let limb_bytes = self . 0 [ N - 1 - i] . to_be_bytes ( ) ;
55+ slice[ i * 8 ..( i + 1 ) * 8 ] . copy_from_slice ( & limb_bytes) ;
56+ }
57+ }
58+
59+ pub fn is_zero ( & self ) -> bool {
60+ self . 0 == [ 0 ; N ]
61+ }
62+ }
63+
64+ impl < const N : usize , const M : usize > From < & BytesN < M > > for BigInt < N > {
65+ fn from ( bytes : & BytesN < M > ) -> Self {
66+ if M != N * 8 {
67+ panic ! ( "BytesN::Into<BigInt> - length mismatch" )
68+ }
69+
70+ let array = bytes. to_array ( ) ;
71+ let mut limbs = [ 0u64 ; N ] ;
72+ for i in 0 ..N {
73+ let start = i * 8 ;
74+ let end = start + 8 ;
75+ let mut chunk = [ 0u8 ; 8 ] ;
76+ chunk. copy_from_slice ( & array[ start..end] ) ;
77+ limbs[ N - 1 - i] = u64:: from_be_bytes ( chunk) ;
78+ }
79+ BigInt ( limbs)
80+ }
81+ }
82+
1983/// `G1Affine` is a point in the G1 group (subgroup defined over the base field
2084/// `Fq`) of the BLS12-381 elliptic curve
2185///
@@ -44,7 +108,7 @@ pub struct Bls12_381 {
44108/// ```
45109#[ derive( Clone ) ]
46110#[ repr( transparent) ]
47- pub struct G1Affine ( BytesN < 96 > ) ;
111+ pub struct G1Affine ( BytesN < G1_SERIALIZED_SIZE > ) ;
48112
49113/// `G2Affine` is a point in the G2 group (subgroup defined over the quadratic
50114/// extension field `Fq2`) of the BLS12-381 elliptic curve
@@ -64,7 +128,7 @@ pub struct G1Affine(BytesN<96>);
64128/// - sort_flag (bit 2): Must always be unset (0).
65129#[ derive( Clone ) ]
66130#[ repr( transparent) ]
67- pub struct G2Affine ( BytesN < 192 > ) ;
131+ pub struct G2Affine ( BytesN < G2_SERIALIZED_SIZE > ) ;
68132
69133/// `Fp` represents an element of the base field `Fq` of the BLS12-381 elliptic
70134/// curve
@@ -74,7 +138,7 @@ pub struct G2Affine(BytesN<192>);
74138/// field `Fp`. The value is serialized as a big-endian integer.
75139#[ derive( Clone ) ]
76140#[ repr( transparent) ]
77- pub struct Fp ( BytesN < 48 > ) ;
141+ pub struct Fp ( BytesN < FP_SERIALIZED_SIZE > ) ;
78142
79143/// `Fp2` represents an element of the quadratic extension field `Fq2` of the
80144/// BLS12-381 elliptic curve
@@ -86,7 +150,7 @@ pub struct Fp(BytesN<48>);
86150/// and imaginary components).
87151#[ derive( Clone ) ]
88152#[ repr( transparent) ]
89- pub struct Fp2 ( BytesN < 96 > ) ;
153+ pub struct Fp2 ( BytesN < FP2_SERIALIZED_SIZE > ) ;
90154
91155/// `Fr` represents an element in the BLS12-381 scalar field, which is a prime
92156/// field of order `r` (the order of the G1 and G2 groups). The struct is
@@ -96,10 +160,88 @@ pub struct Fp2(BytesN<96>);
96160#[ repr( transparent) ]
97161pub struct Fr ( U256 ) ;
98162
99- impl_bytesn_repr ! ( G1Affine , 96 ) ;
100- impl_bytesn_repr ! ( G2Affine , 192 ) ;
101- impl_bytesn_repr ! ( Fp , 48 ) ;
102- impl_bytesn_repr ! ( Fp2 , 96 ) ;
163+ impl_bytesn_repr ! ( G1Affine , G1_SERIALIZED_SIZE ) ;
164+ impl_bytesn_repr ! ( G2Affine , G2_SERIALIZED_SIZE ) ;
165+ impl_bytesn_repr ! ( Fp , FP_SERIALIZED_SIZE ) ;
166+ impl_bytesn_repr ! ( Fp2 , FP2_SERIALIZED_SIZE ) ;
167+
168+ impl Fp {
169+ pub fn env ( & self ) -> & Env {
170+ self . 0 . env ( )
171+ }
172+
173+ // `Fp` represents an element in the base field of the BLS12-381 elliptic curve.
174+ // For an element a ∈ Fp, its negation `-a` is defined as:
175+ // a + (-a) = 0 (mod p)
176+ // where `p` is the field modulus, and to make a valid point coordinate on the
177+ // curve, `a` also must be within the field range (i.e., 0 ≤ a < p).
178+ fn checked_neg ( & self ) -> Option < Fp > {
179+ let fp_bigint: BigInt < 6 > = ( & self . 0 ) . into ( ) ;
180+ if fp_bigint. is_zero ( ) {
181+ return Some ( self . clone ( ) ) ;
182+ }
183+
184+ // BLS12-381 base field modulus
185+ const BLS12_381_MODULUS : [ u64 ; 6 ] = [
186+ 13402431016077863595 ,
187+ 2210141511517208575 ,
188+ 7435674573564081700 ,
189+ 7239337960414712511 ,
190+ 5412103778470702295 ,
191+ 1873798617647539866 ,
192+ ] ;
193+ let mut res = BigInt ( BLS12_381_MODULUS ) ;
194+
195+ // Compute modulus - value
196+ let borrow = res. sub_with_borrow ( & fp_bigint) ;
197+ if borrow {
198+ return None ;
199+ }
200+
201+ let mut bytes = [ 0u8 ; FP_SERIALIZED_SIZE ] ;
202+ res. copy_into_array ( & mut bytes) ;
203+ Some ( Fp :: from_array ( self . env ( ) , & bytes) )
204+ }
205+
206+ /// Maps to a `G1Affine` point via [simplified SWU
207+ /// mapping](https://www.rfc-editor.org/rfc/rfc9380.html#name-simplified-swu-for-ab-0)
208+ pub fn map_to_g1 ( & self ) -> G1Affine {
209+ self . env ( ) . crypto ( ) . bls12_381 ( ) . map_fp_to_g1 ( self )
210+ }
211+ }
212+
213+ impl From < Fp > for BigInt < 6 > {
214+ fn from ( fp : Fp ) -> Self {
215+ let inner: Bytes = fp. 0 . into ( ) ;
216+ let mut limbs = [ 0u64 ; 6 ] ;
217+ for i in 0 ..6u32 {
218+ let start = i * 8 ;
219+ let mut slice = [ 0u8 ; 8 ] ;
220+ inner. slice ( start..start + 8 ) . copy_into_slice ( & mut slice) ;
221+ limbs[ 5 - i as usize ] = u64:: from_be_bytes ( slice) ;
222+ }
223+ BigInt ( limbs)
224+ }
225+ }
226+
227+ impl Neg for & Fp {
228+ type Output = Fp ;
229+
230+ fn neg ( self ) -> Self :: Output {
231+ match self . checked_neg ( ) {
232+ Some ( v) => v,
233+ None => sdk_panic ! ( "invalid input - Fp is larger than the field modulus" ) ,
234+ }
235+ }
236+ }
237+
238+ impl Neg for Fp {
239+ type Output = Fp ;
240+
241+ fn neg ( self ) -> Self :: Output {
242+ ( & self ) . neg ( )
243+ }
244+ }
103245
104246impl G1Affine {
105247 pub fn env ( & self ) -> & Env {
@@ -131,6 +273,87 @@ impl Mul<Fr> for G1Affine {
131273 }
132274}
133275
276+ // G1Affine represents a point (X, Y) on the BLS12-381 curve where X, Y ∈ Fp
277+ // Negation of (X, Y) is defined as (X, -Y)
278+ impl Neg for & G1Affine {
279+ type Output = G1Affine ;
280+
281+ fn neg ( self ) -> Self :: Output {
282+ let mut inner: Bytes = ( & self . 0 ) . into ( ) ;
283+ let y = Fp :: try_from_val (
284+ inner. env ( ) ,
285+ inner. slice ( FP_SERIALIZED_SIZE as u32 ..) . as_val ( ) ,
286+ )
287+ . unwrap_optimized ( ) ;
288+ let neg_y = -y;
289+ inner. copy_from_slice ( FP_SERIALIZED_SIZE as u32 , & neg_y. to_array ( ) ) ;
290+ G1Affine :: from_bytes ( BytesN :: try_from_val ( inner. env ( ) , inner. as_val ( ) ) . unwrap_optimized ( ) )
291+ }
292+ }
293+
294+ impl Neg for G1Affine {
295+ type Output = G1Affine ;
296+
297+ fn neg ( self ) -> Self :: Output {
298+ ( & self ) . neg ( )
299+ }
300+ }
301+
302+ impl Fp2 {
303+ pub fn env ( & self ) -> & Env {
304+ self . 0 . env ( )
305+ }
306+
307+ // An Fp2 element is represented as c0 + c1 * X, where:
308+ // - c0, c1 are base field elements (Fp)
309+ // - X is the quadratic non-residue used to construct the field extension
310+ // The negation of c0 + c1 * X is (-c0) + (-c1) * X.
311+ fn checked_neg ( & self ) -> Option < Fp2 > {
312+ let mut inner = self . to_array ( ) ;
313+ let mut slice0 = [ 0 ; FP_SERIALIZED_SIZE ] ;
314+ let mut slice1 = [ 0 ; FP_SERIALIZED_SIZE ] ;
315+ slice0. copy_from_slice ( & inner[ 0 ..FP_SERIALIZED_SIZE ] ) ;
316+ slice1. copy_from_slice ( & inner[ FP_SERIALIZED_SIZE ..FP2_SERIALIZED_SIZE ] ) ;
317+
318+ // Convert both components to Fp and negate them
319+ let c0 = Fp :: from_array ( self . env ( ) , & slice0) ;
320+ let c1 = Fp :: from_array ( self . env ( ) , & slice1) ;
321+
322+ // If either component's negation fails, the whole operation fails
323+ let neg_c0 = c0. checked_neg ( ) ?;
324+ let neg_c1 = c1. checked_neg ( ) ?;
325+
326+ // Reconstruct the Fp2 element from negated components
327+ inner[ 0 ..FP_SERIALIZED_SIZE ] . copy_from_slice ( & neg_c0. to_array ( ) ) ;
328+ inner[ FP_SERIALIZED_SIZE ..FP2_SERIALIZED_SIZE ] . copy_from_slice ( & neg_c1. to_array ( ) ) ;
329+
330+ Some ( Fp2 :: from_array ( self . env ( ) , & inner) )
331+ }
332+
333+ pub fn map_to_g2 ( & self ) -> G2Affine {
334+ self . env ( ) . crypto ( ) . bls12_381 ( ) . map_fp2_to_g2 ( self )
335+ }
336+ }
337+
338+ impl Neg for & Fp2 {
339+ type Output = Fp2 ;
340+
341+ fn neg ( self ) -> Self :: Output {
342+ match self . checked_neg ( ) {
343+ Some ( v) => v,
344+ None => sdk_panic ! ( "invalid input - Fp2 component is larger than the field modulus" ) ,
345+ }
346+ }
347+ }
348+
349+ impl Neg for Fp2 {
350+ type Output = Fp2 ;
351+
352+ fn neg ( self ) -> Self :: Output {
353+ ( & self ) . neg ( )
354+ }
355+ }
356+
134357impl G2Affine {
135358 pub fn env ( & self ) -> & Env {
136359 self . 0 . env ( )
@@ -161,23 +384,29 @@ impl Mul<Fr> for G2Affine {
161384 }
162385}
163386
164- impl Fp {
165- pub fn env ( & self ) -> & Env {
166- self . 0 . env ( )
167- }
387+ // G2Affine represents a point (X, Y) on the BLS12-381 quadratic extension curve where X, Y ∈ Fp2
388+ // Negation of (X, Y) is defined as (X, -Y)
389+ impl Neg for & G2Affine {
390+ type Output = G2Affine ;
168391
169- pub fn map_to_g1 ( & self ) -> G1Affine {
170- self . env ( ) . crypto ( ) . bls12_381 ( ) . map_fp_to_g1 ( self )
392+ fn neg ( self ) -> Self :: Output {
393+ let mut inner: Bytes = ( & self . 0 ) . into ( ) ;
394+ let y = Fp2 :: try_from_val (
395+ inner. env ( ) ,
396+ inner. slice ( FP2_SERIALIZED_SIZE as u32 ..) . as_val ( ) ,
397+ )
398+ . unwrap_optimized ( ) ;
399+ let neg_y = -y;
400+ inner. copy_from_slice ( FP2_SERIALIZED_SIZE as u32 , & neg_y. to_array ( ) ) ;
401+ G2Affine :: from_bytes ( BytesN :: try_from_val ( inner. env ( ) , inner. as_val ( ) ) . unwrap_optimized ( ) )
171402 }
172403}
173404
174- impl Fp2 {
175- pub fn env ( & self ) -> & Env {
176- self . 0 . env ( )
177- }
405+ impl Neg for G2Affine {
406+ type Output = G2Affine ;
178407
179- pub fn map_to_g2 ( & self ) -> G2Affine {
180- self . env ( ) . crypto ( ) . bls12_381 ( ) . map_fp2_to_g2 ( self )
408+ fn neg ( self ) -> Self :: Output {
409+ ( & self ) . neg ( )
181410 }
182411}
183412
0 commit comments