Skip to content

Commit 8642dab

Browse files
committed
Implementation negation for G1/G2 affine
1 parent f8bec23 commit 8642dab

File tree

2 files changed

+209
-17
lines changed

2 files changed

+209
-17
lines changed

soroban-sdk/src/crypto/bls12_381.rs

Lines changed: 147 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
use core::{
88
cmp::Ordering,
99
fmt::Debug,
10-
ops::{Add, Mul, Sub},
10+
ops::{Add, Mul, Neg, Sub},
1111
};
1212

1313
/// Bls12_381 provides access to curve and field arithmetics on the BLS12-381
@@ -16,6 +16,54 @@ pub struct Bls12_381 {
1616
env: Env,
1717
}
1818

19+
fn sbb_for_sub_with_borrow(a: &mut u64, b: u64, borrow: u8) -> u8 {
20+
let tmp = (1u128 << 64) + (*a as u128) - (b as u128) - (borrow as u128);
21+
*a = tmp as u64;
22+
u8::from(tmp >> 64 == 0)
23+
}
24+
25+
#[derive(Debug)]
26+
pub(crate) struct BigInt<const N: usize>(pub [u64; N]);
27+
28+
impl<const N: usize> BigInt<N> {
29+
pub fn sub_with_borrow(&mut self, other: &Self) -> bool {
30+
let mut borrow = 0;
31+
for i in 0..N {
32+
borrow = sbb_for_sub_with_borrow(&mut self.0[i], other.0[i], borrow);
33+
}
34+
borrow != 0
35+
}
36+
37+
pub fn copy_into_slice(&self, slice: &mut [u8]) {
38+
if slice.len() != N * 8 {
39+
sdk_panic!("BigInt::copy_into_slice with mismatched slice length")
40+
}
41+
for i in 0..N {
42+
let limb_bytes = self.0[N - 1 - i].to_be_bytes();
43+
slice[i * 8..(i + 1) * 8].copy_from_slice(&limb_bytes);
44+
}
45+
}
46+
}
47+
48+
impl<const N: usize, const M: usize> Into<BigInt<N>> for BytesN<M> {
49+
fn into(self) -> BigInt<N> {
50+
if M != N * 8 {
51+
sdk_panic!("BytesN::Into<BigInt> - length mismatch")
52+
}
53+
54+
let array = self.to_array();
55+
let mut limbs = [0u64; N];
56+
for i in 0..N {
57+
let start = i * 8;
58+
let end = start + 8;
59+
let mut bytes = [0u8; 8];
60+
bytes.copy_from_slice(&array[start..end]);
61+
limbs[N - 1 - i] = u64::from_be_bytes(bytes);
62+
}
63+
BigInt(limbs)
64+
}
65+
}
66+
1967
/// `G1Affine` is a point in the G1 group (subgroup defined over the base field
2068
/// `Fq`) of the BLS12-381 elliptic curve
2169
///
@@ -101,6 +149,57 @@ impl_bytesn_repr!(G2Affine, 192);
101149
impl_bytesn_repr!(Fp, 48);
102150
impl_bytesn_repr!(Fp2, 96);
103151

152+
impl Fp {
153+
pub fn env(&self) -> &Env {
154+
self.0.env()
155+
}
156+
157+
pub fn map_to_g1(&self) -> G1Affine {
158+
self.env().crypto().bls12_381().map_fp_to_g1(self)
159+
}
160+
}
161+
162+
impl Into<BigInt<6>> for Fp {
163+
fn into(self) -> BigInt<6> {
164+
let inner: Bytes = self.0.into();
165+
let mut limbs = [0u64; 6];
166+
for i in 0..6u32 {
167+
let start = i * 8;
168+
let mut slice = [0u8; 8];
169+
inner.slice(start..start + 8).copy_into_slice(&mut slice);
170+
limbs[5 - i as usize] = u64::from_be_bytes(slice);
171+
}
172+
BigInt(limbs)
173+
}
174+
}
175+
176+
impl Neg for Fp {
177+
type Output = Fp;
178+
179+
fn neg(self) -> Self::Output {
180+
if self.to_array() == [0; 48] {
181+
return self;
182+
}
183+
184+
let env = self.env().clone();
185+
let fp_bigint: BigInt<6> = self.0.into();
186+
// BLS12-381 base field modulus
187+
let mut res = BigInt([
188+
13402431016077863595,
189+
2210141511517208575,
190+
7435674573564081700,
191+
7239337960414712511,
192+
5412103778470702295,
193+
1873798617647539866,
194+
]);
195+
// Compute modulus - value
196+
res.sub_with_borrow(&fp_bigint);
197+
let mut bytes = [0u8; 48];
198+
res.copy_into_slice(&mut bytes);
199+
Fp::from_array(&env, &bytes)
200+
}
201+
}
202+
104203
impl G1Affine {
105204
pub fn env(&self) -> &Env {
106205
self.0.env()
@@ -131,6 +230,45 @@ impl Mul<Fr> for G1Affine {
131230
}
132231
}
133232

233+
impl Neg for G1Affine {
234+
type Output = G1Affine;
235+
236+
fn neg(self) -> Self::Output {
237+
let mut inner: Bytes = self.0.into();
238+
let y = Fp::try_from_val(inner.env(), inner.slice(48..).as_val()).unwrap_optimized();
239+
let neg_y = -y;
240+
inner.copy_from_slice(48, &neg_y.to_array());
241+
G1Affine::from_bytes(BytesN::try_from_val(inner.env(), inner.as_val()).unwrap_optimized())
242+
}
243+
}
244+
245+
impl Fp2 {
246+
pub fn env(&self) -> &Env {
247+
self.0.env()
248+
}
249+
250+
pub fn map_to_g2(&self) -> G2Affine {
251+
self.env().crypto().bls12_381().map_fp2_to_g2(self)
252+
}
253+
}
254+
255+
impl Neg for Fp2 {
256+
type Output = Fp2;
257+
258+
fn neg(self) -> Self::Output {
259+
let mut inner = self.to_array();
260+
let mut slice0 = [0; 48];
261+
let mut slice1 = [0; 48];
262+
slice0.copy_from_slice(&inner[0..48]);
263+
slice1.copy_from_slice(&inner[48..96]);
264+
let c0 = -Fp::from_array(self.env(), &slice0);
265+
let c1 = -Fp::from_array(self.env(), &slice1);
266+
inner[0..48].copy_from_slice(&c0.to_array());
267+
inner[48..96].copy_from_slice(&c1.to_array());
268+
Fp2::from_array(self.env(), &inner)
269+
}
270+
}
271+
134272
impl G2Affine {
135273
pub fn env(&self) -> &Env {
136274
self.0.env()
@@ -161,23 +299,15 @@ impl Mul<Fr> for G2Affine {
161299
}
162300
}
163301

164-
impl 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-
174-
impl Fp2 {
175-
pub fn env(&self) -> &Env {
176-
self.0.env()
177-
}
302+
impl Neg for G2Affine {
303+
type Output = G2Affine;
178304

179-
pub fn map_to_g2(&self) -> G2Affine {
180-
self.env().crypto().bls12_381().map_fp2_to_g2(self)
305+
fn neg(self) -> Self::Output {
306+
let mut inner: Bytes = self.0.into();
307+
let y = Fp2::try_from_val(inner.env(), inner.slice(96..).as_val()).unwrap_optimized();
308+
let neg_y = -y;
309+
inner.copy_from_slice(96, &neg_y.to_array());
310+
G2Affine::from_bytes(BytesN::try_from_val(inner.env(), inner.as_val()).unwrap_optimized())
181311
}
182312
}
183313

soroban-sdk/src/tests/crypto_bls12_381.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,68 @@ use crate::{
44
vec, Bytes, Env, Vec, U256,
55
};
66

7+
#[test]
8+
fn test_g1_negation() {
9+
let env = Env::default();
10+
// Test case 1: Generator point (G1)
11+
let g1_generator = G1Affine::from_bytes(bytesn!(&env,
12+
0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1
13+
));
14+
let neg_g1_generator = G1Affine::from_bytes(bytesn!(&env,
15+
0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb114d1d6855d545a8aa7d76c8cf2e21f267816aef1db507c96655b9d5caac42364e6f38ba0ecb751bad54dcd6b939c2ca
16+
));
17+
assert_eq!(-g1_generator, neg_g1_generator);
18+
19+
// Test case 2: Identity/infinity point
20+
let identity = G1Affine::from_bytes(bytesn!(&env,
21+
0x40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
22+
));
23+
assert_eq!(-identity.clone(), identity);
24+
25+
// Test case 3: Random point
26+
let random_point = G1Affine::from_bytes(bytesn!(&env,
27+
0x00b2ea3a6c75f43ff24d0a93f4302b4e2cf8f5eb2980eb7bb0f65af703c72c19e293cfe7cd98d3645a5d7c5f467d629213b7a1d9aa5e09c52eb2d5590b3b74ebb42bf3631a022a283dcf4ec73087fce37725ba5d9d483f84f0ea725acc853b4e
28+
));
29+
let neg_random_point = G1Affine::from_bytes(bytesn!(&env,
30+
0x00b2ea3a6c75f43ff24d0a93f4302b4e2cf8f5eb2980eb7bb0f65af703c72c19e293cfe7cd98d3645a5d7c5f467d6292064970108f21dcd51c68d25d381037ebb04b5821d982e897296183d9c628f940a78645a1140bc07ac9148da5337a6f5d
31+
));
32+
assert_eq!(-random_point, neg_random_point);
33+
}
34+
35+
#[test]
36+
fn test_g2_negation() {
37+
let env = Env::default();
38+
39+
// Test case 1: Generator point (G2)
40+
let g2_generator = G2Affine::from_bytes(bytesn!(&env,
41+
0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801
42+
));
43+
44+
let neg_g2_generator = G2Affine::from_bytes(bytesn!(&env,
45+
0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb813fa4d4a0ad8b1ce186ed5061789213d993923066dddaf1040bc3ff59f825c78df74f2d75467e25e0f55f8a00fa030ed0d1b3cc2c7027888be51d9ef691d77bcb679afda66c73f17f9ee3837a55024f78c71363275a75d75d86bab79f74782aa
46+
));
47+
48+
assert_eq!(-g2_generator, neg_g2_generator);
49+
50+
// Test case 2: Identity/infinity point
51+
let identity = G2Affine::from_bytes(bytesn!(&env,
52+
0x400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
53+
));
54+
55+
assert_eq!(-identity.clone(), identity);
56+
57+
// Test case 3: Random point
58+
let random_point = G2Affine::from_bytes(bytesn!(&env,
59+
0x04de7359c488ccf4753d08b99f3fb44de6c4c46b5872c45ddd90a37442dc679591a40ab8ee539b2aa30c333774c71ed01676197052a7a651e5618151d8374fdf4c25ce3f57c06f65b81dbaf0373b67d7be97e7f4f1f87cf71d761ecf04c05b150499bf4cf6242da563d185c2bce880ae59e5f3816b34f70d0a6f30e9ca3064ba179dd4ccda8dd025cb3a2c9628fe0c0b09defb3faaae69a59510cd90badf0b87a82e52a4876837e29466fced187483012c900b4d4a693b2347cab3f9cdd86035
60+
));
61+
62+
let neg_random_point = G2Affine::from_bytes(bytesn!(&env,
63+
0x04de7359c488ccf4753d08b99f3fb44de6c4c46b5872c45ddd90a37442dc679591a40ab8ee539b2aa30c333774c71ed01676197052a7a651e5618151d8374fdf4c25ce3f57c06f65b81dbaf0373b67d7be97e7f4f1f87cf71d761ecf04c05b151567529d435bb8f4e74a21f386632c290a91580388501bb25cc1a1b72c80916a070e2b31d6c62fd9eec4d369d7019ea0102216aa8ed17cf4b60ada25886ca14fbc48f8e06c1cdadcd2c9d5b3de3c7322f21bf4b166eac4dc72344c0632274a76
64+
));
65+
66+
assert_eq!(-random_point, neg_random_point);
67+
}
68+
769
#[test]
870
fn test_bls_g1() {
971
let env = Env::default();

0 commit comments

Comments
 (0)