Skip to content

Commit 549d70e

Browse files
committed
add checked_neg, impl neg for borrowed types, define constants and improve comments
1 parent cfe74d4 commit 549d70e

File tree

2 files changed

+141
-62
lines changed

2 files changed

+141
-62
lines changed

soroban-sdk/src/bytes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ macro_rules! bytesn {
104104

105105
#[macro_export]
106106
macro_rules! impl_bytesn_repr {
107-
($elem: ident, $size: literal) => {
107+
($elem: ident, $size: expr) => {
108108
impl $elem {
109109
pub fn from_bytes(bytes: BytesN<$size>) -> Self {
110110
Self(bytes)

soroban-sdk/src/crypto/bls12_381.rs

Lines changed: 140 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,21 @@ use core::{
1010
ops::{Add, Mul, Neg, Sub},
1111
};
1212

13+
pub const FP_SERIALIZED_SIZE: usize = 48;
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.
1520
pub 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
1926
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);
27+
let tmp = (1u128 << 64) + u128::from(*a) - u128::from(b) - u128::from(borrow);
2128
*a = tmp as u64;
2229
u8::from(tmp >> 64 == 0)
2330
}
@@ -52,22 +59,20 @@ impl<const N: usize> BigInt<N> {
5259
}
5360
}
5461

55-
impl<const N: usize, const M: usize> Into<BigInt<N>> for &BytesN<M> {
56-
fn into(self) -> BigInt<N> {
57-
const {
58-
if M != N * 8 {
59-
panic!("BytesN::Into<BigInt> - length mismatch")
60-
}
62+
impl<const N: usize, const M: usize> From<&BytesN<M>> for BigInt<N> {
63+
fn from(bytes: &BytesN<M>) -> Self {
64+
if M != N * 8 {
65+
panic!("BytesN::Into<BigInt> - length mismatch")
6166
}
6267

63-
let array = self.to_array();
68+
let array = bytes.to_array();
6469
let mut limbs = [0u64; N];
6570
for i in 0..N {
6671
let start = i * 8;
6772
let end = start + 8;
68-
let mut bytes = [0u8; 8];
69-
bytes.copy_from_slice(&array[start..end]);
70-
limbs[N - 1 - i] = u64::from_be_bytes(bytes);
73+
let mut chunk = [0u8; 8];
74+
chunk.copy_from_slice(&array[start..end]);
75+
limbs[N - 1 - i] = u64::from_be_bytes(chunk);
7176
}
7277
BigInt(limbs)
7378
}
@@ -101,7 +106,7 @@ impl<const N: usize, const M: usize> Into<BigInt<N>> for &BytesN<M> {
101106
/// ```
102107
#[derive(Clone)]
103108
#[repr(transparent)]
104-
pub struct G1Affine(BytesN<96>);
109+
pub struct G1Affine(BytesN<G1_SERIALIZED_SIZE>);
105110

106111
/// `G2Affine` is a point in the G2 group (subgroup defined over the quadratic
107112
/// extension field `Fq2`) of the BLS12-381 elliptic curve
@@ -121,7 +126,7 @@ pub struct G1Affine(BytesN<96>);
121126
/// - sort_flag (bit 2): Must always be unset (0).
122127
#[derive(Clone)]
123128
#[repr(transparent)]
124-
pub struct G2Affine(BytesN<192>);
129+
pub struct G2Affine(BytesN<G2_SERIALIZED_SIZE>);
125130

126131
/// `Fp` represents an element of the base field `Fq` of the BLS12-381 elliptic
127132
/// curve
@@ -131,7 +136,7 @@ pub struct G2Affine(BytesN<192>);
131136
/// field `Fp`. The value is serialized as a big-endian integer.
132137
#[derive(Clone)]
133138
#[repr(transparent)]
134-
pub struct Fp(BytesN<48>);
139+
pub struct Fp(BytesN<FP_SERIALIZED_SIZE>);
135140

136141
/// `Fp2` represents an element of the quadratic extension field `Fq2` of the
137142
/// BLS12-381 elliptic curve
@@ -143,7 +148,7 @@ pub struct Fp(BytesN<48>);
143148
/// and imaginary components).
144149
#[derive(Clone)]
145150
#[repr(transparent)]
146-
pub struct Fp2(BytesN<96>);
151+
pub struct Fp2(BytesN<FP2_SERIALIZED_SIZE>);
147152

148153
/// `Fr` represents an element in the BLS12-381 scalar field, which is a prime
149154
/// field of order `r` (the order of the G1 and G2 groups). The struct is
@@ -153,26 +158,54 @@ pub struct Fp2(BytesN<96>);
153158
#[repr(transparent)]
154159
pub struct Fr(U256);
155160

156-
impl_bytesn_repr!(G1Affine, 96);
157-
impl_bytesn_repr!(G2Affine, 192);
158-
impl_bytesn_repr!(Fp, 48);
159-
impl_bytesn_repr!(Fp2, 96);
161+
impl_bytesn_repr!(G1Affine, G1_SERIALIZED_SIZE);
162+
impl_bytesn_repr!(G2Affine, G2_SERIALIZED_SIZE);
163+
impl_bytesn_repr!(Fp, FP_SERIALIZED_SIZE);
164+
impl_bytesn_repr!(Fp2, FP2_SERIALIZED_SIZE);
160165

161166
impl Fp {
162167
pub fn env(&self) -> &Env {
163168
self.0.env()
164169
}
165170

171+
fn checked_neg(&self) -> Option<Fp> {
172+
let fp_bigint: BigInt<6> = (&self.0).into();
173+
if fp_bigint.is_zero() {
174+
return Some(self.clone());
175+
}
176+
177+
// BLS12-381 base field modulus
178+
const BLS12_381_MODULUS: [u64; 6] = [
179+
13402431016077863595,
180+
2210141511517208575,
181+
7435674573564081700,
182+
7239337960414712511,
183+
5412103778470702295,
184+
1873798617647539866,
185+
];
186+
let mut res = BigInt(BLS12_381_MODULUS);
187+
188+
// Compute modulus - value
189+
let borrow = res.sub_with_borrow(&fp_bigint);
190+
if borrow {
191+
return None;
192+
}
193+
194+
let mut bytes = [0u8; FP_SERIALIZED_SIZE];
195+
res.copy_into_array(&mut bytes);
196+
Some(Fp::from_array(self.env(), &bytes))
197+
}
198+
166199
/// Maps to a `G1Affine` point via [simplified SWU
167200
/// mapping](https://www.rfc-editor.org/rfc/rfc9380.html#name-simplified-swu-for-ab-0)
168201
pub fn map_to_g1(&self) -> G1Affine {
169202
self.env().crypto().bls12_381().map_fp_to_g1(self)
170203
}
171204
}
172205

173-
impl Into<BigInt<6>> for Fp {
174-
fn into(self) -> BigInt<6> {
175-
let inner: Bytes = self.0.into();
206+
impl From<Fp> for BigInt<6> {
207+
fn from(fp: Fp) -> Self {
208+
let inner: Bytes = fp.0.into();
176209
let mut limbs = [0u64; 6];
177210
for i in 0..6u32 {
178211
let start = i * 8;
@@ -184,32 +217,22 @@ impl Into<BigInt<6>> for Fp {
184217
}
185218
}
186219

187-
impl Neg for Fp {
220+
impl Neg for &Fp {
188221
type Output = Fp;
189222

190223
fn neg(self) -> Self::Output {
191-
let fp_bigint: BigInt<6> = (&self.0).into();
192-
if fp_bigint.is_zero() {
193-
return self;
224+
match self.checked_neg() {
225+
Some(v) => v,
226+
None => sdk_panic!("invalid input - Fp is larger than the field modulus"),
194227
}
228+
}
229+
}
195230

196-
// BLS12-381 base field modulus
197-
let mut res = BigInt([
198-
13402431016077863595,
199-
2210141511517208575,
200-
7435674573564081700,
201-
7239337960414712511,
202-
5412103778470702295,
203-
1873798617647539866,
204-
]);
205-
// Compute modulus - value
206-
let borrow = res.sub_with_borrow(&fp_bigint);
207-
if borrow {
208-
sdk_panic!("invalid input - Fp is larger than the field modulus")
209-
}
210-
let mut bytes = [0u8; 48];
211-
res.copy_into_array(&mut bytes);
212-
Fp::from_array(&self.env(), &bytes)
231+
impl Neg for Fp {
232+
type Output = Fp;
233+
234+
fn neg(self) -> Self::Output {
235+
(&self).neg()
213236
}
214237
}
215238

@@ -243,42 +266,84 @@ impl Mul<Fr> for G1Affine {
243266
}
244267
}
245268

246-
impl Neg for G1Affine {
269+
// G1Affine represents a point (X, Y) on the BLS12-381 curve where X, Y ∈ Fp
270+
// Negation of (X, Y) is defined as (X, -Y)
271+
impl Neg for &G1Affine {
247272
type Output = G1Affine;
248273

249274
fn neg(self) -> Self::Output {
250-
let mut inner: Bytes = self.0.into();
251-
let y = Fp::try_from_val(inner.env(), inner.slice(48..).as_val()).unwrap_optimized();
275+
let mut inner: Bytes = (&self.0).into();
276+
let y = Fp::try_from_val(
277+
inner.env(),
278+
inner.slice(FP_SERIALIZED_SIZE as u32..).as_val(),
279+
)
280+
.unwrap_optimized();
252281
let neg_y = -y;
253-
inner.copy_from_slice(48, &neg_y.to_array());
282+
inner.copy_from_slice(FP_SERIALIZED_SIZE as u32, &neg_y.to_array());
254283
G1Affine::from_bytes(BytesN::try_from_val(inner.env(), inner.as_val()).unwrap_optimized())
255284
}
256285
}
257286

287+
impl Neg for G1Affine {
288+
type Output = G1Affine;
289+
290+
fn neg(self) -> Self::Output {
291+
(&self).neg()
292+
}
293+
}
294+
258295
impl Fp2 {
259296
pub fn env(&self) -> &Env {
260297
self.0.env()
261298
}
262299

300+
// An Fp2 element is represented as c0 + c1 * X, where:
301+
// - c0, c1 are base field elements (Fp)
302+
// - X is the quadratic non-residue used to construct the field extension
303+
// The negation of c0 + c1 * X is (-c0) + (-c1) * X.
304+
fn checked_neg(&self) -> Option<Fp2> {
305+
let mut inner = self.to_array();
306+
let mut slice0 = [0; FP_SERIALIZED_SIZE];
307+
let mut slice1 = [0; FP_SERIALIZED_SIZE];
308+
slice0.copy_from_slice(&inner[0..FP_SERIALIZED_SIZE]);
309+
slice1.copy_from_slice(&inner[FP_SERIALIZED_SIZE..FP2_SERIALIZED_SIZE]);
310+
311+
// Convert both components to Fp and negate them
312+
let c0 = Fp::from_array(self.env(), &slice0);
313+
let c1 = Fp::from_array(self.env(), &slice1);
314+
315+
// If either component's negation fails, the whole operation fails
316+
let neg_c0 = c0.checked_neg()?;
317+
let neg_c1 = c1.checked_neg()?;
318+
319+
// Reconstruct the Fp2 element from negated components
320+
inner[0..FP_SERIALIZED_SIZE].copy_from_slice(&neg_c0.to_array());
321+
inner[FP_SERIALIZED_SIZE..FP2_SERIALIZED_SIZE].copy_from_slice(&neg_c1.to_array());
322+
323+
Some(Fp2::from_array(self.env(), &inner))
324+
}
325+
263326
pub fn map_to_g2(&self) -> G2Affine {
264327
self.env().crypto().bls12_381().map_fp2_to_g2(self)
265328
}
266329
}
267330

331+
impl Neg for &Fp2 {
332+
type Output = Fp2;
333+
334+
fn neg(self) -> Self::Output {
335+
match self.checked_neg() {
336+
Some(v) => v,
337+
None => sdk_panic!("invalid input - Fp2 component is larger than the field modulus"),
338+
}
339+
}
340+
}
341+
268342
impl Neg for Fp2 {
269343
type Output = Fp2;
270344

271345
fn neg(self) -> Self::Output {
272-
let mut inner = self.to_array();
273-
let mut slice0 = [0; 48];
274-
let mut slice1 = [0; 48];
275-
slice0.copy_from_slice(&inner[0..48]);
276-
slice1.copy_from_slice(&inner[48..96]);
277-
let c0 = -Fp::from_array(self.env(), &slice0);
278-
let c1 = -Fp::from_array(self.env(), &slice1);
279-
inner[0..48].copy_from_slice(&c0.to_array());
280-
inner[48..96].copy_from_slice(&c1.to_array());
281-
Fp2::from_array(self.env(), &inner)
346+
(&self).neg()
282347
}
283348
}
284349

@@ -312,18 +377,32 @@ impl Mul<Fr> for G2Affine {
312377
}
313378
}
314379

315-
impl Neg for G2Affine {
380+
// G2Affine represents a point (X, Y) on the BLS12-381 quadratic extension curve where X, Y ∈ Fp2
381+
// Negation of (X, Y) is defined as (X, -Y)
382+
impl Neg for &G2Affine {
316383
type Output = G2Affine;
317384

318385
fn neg(self) -> Self::Output {
319-
let mut inner: Bytes = self.0.into();
320-
let y = Fp2::try_from_val(inner.env(), inner.slice(96..).as_val()).unwrap_optimized();
386+
let mut inner: Bytes = (&self.0).into();
387+
let y = Fp2::try_from_val(
388+
inner.env(),
389+
inner.slice(FP2_SERIALIZED_SIZE as u32..).as_val(),
390+
)
391+
.unwrap_optimized();
321392
let neg_y = -y;
322-
inner.copy_from_slice(96, &neg_y.to_array());
393+
inner.copy_from_slice(FP2_SERIALIZED_SIZE as u32, &neg_y.to_array());
323394
G2Affine::from_bytes(BytesN::try_from_val(inner.env(), inner.as_val()).unwrap_optimized())
324395
}
325396
}
326397

398+
impl Neg for G2Affine {
399+
type Output = G2Affine;
400+
401+
fn neg(self) -> Self::Output {
402+
(&self).neg()
403+
}
404+
}
405+
327406
impl Fr {
328407
pub fn env(&self) -> &Env {
329408
self.0.env()

0 commit comments

Comments
 (0)