Skip to content

Commit 00869a7

Browse files
Compression/Decompression of G2 Points for BLS12_381 (lambdaclass#909)
* add compress g2 point * refactor compress * add alloc * fix trait imp * fix y compare * fix function return * rm macro * avoid alloc * fix clippy * fmt * make clippy happy --------- Co-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com> Co-authored-by: diegokingston <dkingston@fi.uba.ar>
1 parent 94c2293 commit 00869a7

File tree

4 files changed

+167
-23
lines changed

4 files changed

+167
-23
lines changed

math/benches/elliptic_curves/bls12_381.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub fn bls12_381_elliptic_curve_benchmarks(c: &mut Criterion) {
7575

7676
// Decompress_G1_point
7777
group.bench_function("Decompress G1 Point", |bencher| {
78-
let a: [u8; 48] = BLS12381Curve::compress_g1_point(&a_g1).try_into().unwrap();
78+
let a: [u8; 48] = BLS12381Curve::compress_g1_point(&a_g1);
7979
bencher.iter(|| black_box(BLS12381Curve::decompress_g1_point(&mut black_box(a))).unwrap());
8080
});
8181

math/benches/elliptic_curves/iai_bls12_381.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ pub fn bls12_381_compress_g1() {
9696
#[allow(dead_code)]
9797
pub fn bls12_381_decompress_g1() {
9898
let (a, _, _, _) = rand_points_g1();
99-
let a: [u8; 48] = BLS12381Curve::compress_g1_point(&a).try_into().unwrap();
99+
let a: [u8; 48] = BLS12381Curve::compress_g1_point(&a);
100100
let _ = black_box(BLS12381Curve::decompress_g1_point(&mut black_box(a))).unwrap();
101101
}
102102

math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs

Lines changed: 159 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
use super::{field_extension::BLS12381PrimeField, twist::BLS12381TwistCurve};
22
use crate::{
3-
elliptic_curve::{
4-
short_weierstrass::{
5-
curves::bls12_381::{
6-
curve::BLS12381Curve, field_extension::Degree2ExtensionField, sqrt,
7-
},
8-
point::ShortWeierstrassProjectivePoint,
9-
traits::Compress,
10-
},
11-
traits::IsEllipticCurve,
3+
elliptic_curve::short_weierstrass::{
4+
curves::bls12_381::{curve::BLS12381Curve, field_extension::Degree2ExtensionField, sqrt},
5+
point::ShortWeierstrassProjectivePoint,
6+
traits::Compress,
127
},
138
field::element::FieldElement,
149
};
@@ -20,20 +15,25 @@ use crate::{
2015
};
2116

2217
type G1Point = ShortWeierstrassProjectivePoint<BLS12381Curve>;
18+
type G2Point = ShortWeierstrassProjectivePoint<BLS12381TwistCurve>;
2319
type BLS12381FieldElement = FieldElement<BLS12381PrimeField>;
2420

2521
impl Compress for BLS12381Curve {
2622
type G1Point = G1Point;
2723

28-
type G2Point = <BLS12381TwistCurve as IsEllipticCurve>::PointRepresentation;
24+
type G2Point = G2Point;
25+
26+
type G1Compressed = [u8; 48];
27+
28+
type G2Compressed = [u8; 96];
2929

3030
type Error = ByteConversionError;
3131

3232
#[cfg(feature = "alloc")]
33-
fn compress_g1_point(point: &Self::G1Point) -> alloc::vec::Vec<u8> {
33+
fn compress_g1_point(point: &Self::G1Point) -> Self::G1Compressed {
3434
if *point == G1Point::neutral_element() {
3535
// point is at infinity
36-
let mut x_bytes = alloc::vec![0_u8; 48];
36+
let mut x_bytes = [0_u8; 48];
3737
x_bytes[0] |= 1 << 7;
3838
x_bytes[0] |= 1 << 6;
3939
x_bytes
@@ -42,8 +42,9 @@ impl Compress for BLS12381Curve {
4242
let point_affine = point.to_affine();
4343
let x = point_affine.x();
4444
let y = point_affine.y();
45-
46-
let mut x_bytes = x.to_bytes_be();
45+
let mut x_bytes = [0u8; 48];
46+
let bytes = x.to_bytes_be();
47+
x_bytes.copy_from_slice(&bytes);
4748

4849
// Set first bit to to 1 indicate this is compressed element.
4950
x_bytes[0] |= 1 << 7;
@@ -106,7 +107,49 @@ impl Compress for BLS12381Curve {
106107
.ok_or(ByteConversionError::PointNotInSubgroup)
107108
}
108109

109-
#[allow(unused)]
110+
#[cfg(feature = "alloc")]
111+
fn compress_g2_point(point: &Self::G2Point) -> Self::G2Compressed {
112+
if *point == G2Point::neutral_element() {
113+
// point is at infinity
114+
let mut x_bytes = [0_u8; 96];
115+
x_bytes[0] |= 1 << 7;
116+
x_bytes[0] |= 1 << 6;
117+
x_bytes
118+
} else {
119+
// point is not at infinity
120+
let point_affine = point.to_affine();
121+
let x = point_affine.x();
122+
let y = point_affine.y();
123+
124+
let x_rev: FieldElement<Degree2ExtensionField> =
125+
FieldElement::new([x.value()[1].clone(), x.value()[0].clone()]);
126+
let mut x_bytes = [0u8; 96];
127+
let bytes = x_rev.to_bytes_be();
128+
x_bytes.copy_from_slice(&bytes);
129+
130+
// Set first bit to to 1 indicate this is compressed element.
131+
x_bytes[0] |= 1 << 7;
132+
133+
// Set the 3rd bit based on y value.
134+
let y_neg = -y;
135+
136+
match (
137+
y.value()[0]
138+
.representative()
139+
.cmp(&y_neg.value()[0].representative()),
140+
y.value()[1]
141+
.representative()
142+
.cmp(&y_neg.value()[1].representative()),
143+
) {
144+
(Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater) => {
145+
x_bytes[0] |= 1 << 5;
146+
}
147+
(_, _) => (),
148+
}
149+
x_bytes
150+
}
151+
}
152+
110153
fn decompress_g2_point(input_bytes: &mut [u8; 96]) -> Result<Self::G2Point, Self::Error> {
111154
let first_byte = input_bytes.first().unwrap();
112155

@@ -124,6 +167,8 @@ impl Compress for BLS12381Curve {
124167
return Ok(Self::G2Point::neutral_element());
125168
}
126169

170+
let third_bit = prefix_bits & 1_u8;
171+
127172
let first_byte_without_control_bits = (first_byte << 3) >> 3;
128173
input_bytes[0] = first_byte_without_control_bits;
129174

@@ -136,7 +181,7 @@ impl Compress for BLS12381Curve {
136181
const VALUE: BLS12381FieldElement = BLS12381FieldElement::from_hex_unchecked("4");
137182
let b_param_qfe = FieldElement::<Degree2ExtensionField>::new([VALUE, VALUE]);
138183

139-
let y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0)
184+
let y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), third_bit)
140185
.ok_or(ByteConversionError::InvalidValue)?;
141186

142187
Self::G2Point::from_affine(x, y).map_err(|_| ByteConversionError::InvalidValue)
@@ -204,8 +249,7 @@ mod tests {
204249
use crate::elliptic_curve::short_weierstrass::traits::Compress;
205250

206251
let g = BLS12381Curve::generator();
207-
let compressed_g = BLS12381Curve::compress_g1_point(&g);
208-
let mut compressed_g_slice: [u8; 48] = compressed_g.try_into().unwrap();
252+
let mut compressed_g_slice = BLS12381Curve::compress_g1_point(&g);
209253

210254
let decompressed_g = BLS12381Curve::decompress_g1_point(&mut compressed_g_slice).unwrap();
211255

@@ -219,11 +263,106 @@ mod tests {
219263
// calculate g point operate with itself
220264
let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2"));
221265

222-
let compressed_g2 = BLS12381Curve::compress_g1_point(&g_2);
223-
let mut compressed_g2_slice: [u8; 48] = compressed_g2.try_into().unwrap();
266+
let mut compressed_g2_slice: [u8; 48] = BLS12381Curve::compress_g1_point(&g_2);
224267

225268
let decompressed_g2 = BLS12381Curve::decompress_g1_point(&mut compressed_g2_slice).unwrap();
226269

227270
assert_eq!(g_2, decompressed_g2);
228271
}
272+
273+
#[cfg(feature = "alloc")]
274+
#[test]
275+
fn test_compress_decompress_generator_g2() {
276+
use crate::elliptic_curve::short_weierstrass::{
277+
curves::bls12_381::twist::BLS12381TwistCurve, traits::Compress,
278+
};
279+
280+
let g = BLS12381TwistCurve::generator();
281+
let mut compressed_g_slice = BLS12381Curve::compress_g2_point(&g);
282+
283+
let decompressed_g = BLS12381Curve::decompress_g2_point(&mut compressed_g_slice).unwrap();
284+
285+
assert_eq!(g, decompressed_g);
286+
}
287+
288+
#[cfg(feature = "alloc")]
289+
#[test]
290+
fn test_compress_decompress_generator_g2_neg() {
291+
use crate::elliptic_curve::short_weierstrass::{
292+
curves::bls12_381::twist::BLS12381TwistCurve, traits::Compress,
293+
};
294+
295+
let g = BLS12381TwistCurve::generator();
296+
let g_neg = g.neg();
297+
298+
let mut compressed_g_neg_slice = BLS12381Curve::compress_g2_point(&g_neg);
299+
300+
let decompressed_g_neg =
301+
BLS12381Curve::decompress_g2_point(&mut compressed_g_neg_slice).unwrap();
302+
303+
assert_eq!(g_neg, decompressed_g_neg);
304+
}
305+
306+
#[cfg(feature = "alloc")]
307+
#[test]
308+
fn test_decompress_g2() {
309+
use crate::{
310+
elliptic_curve::short_weierstrass::curves::bls12_381::{
311+
field_extension::Degree2ExtensionField, twist::BLS12381TwistCurve,
312+
},
313+
field::element::FieldElement,
314+
};
315+
316+
let mut compressed_point = [0_u8; 96];
317+
compressed_point[0] |= 1 << 7;
318+
compressed_point[95] |= 1 << 1;
319+
320+
// Valig G2 point coordinates:
321+
let x_0 = BLS12381FieldElement::from_hex_unchecked("02");
322+
let x_1 = BLS12381FieldElement::from_hex_unchecked("0");
323+
let y_0 = BLS12381FieldElement::from_hex_unchecked("013a59858b6809fca4d9a3b6539246a70051a3c88899964a42bc9a69cf9acdd9dd387cfa9086b894185b9a46a402be73");
324+
let y_1 = BLS12381FieldElement::from_hex_unchecked("02d27e0ec3356299a346a09ad7dc4ef68a483c3aed53f9139d2f929a3eecebf72082e5e58c6da24ee32e03040c406d4f");
325+
326+
let x: FieldElement<Degree2ExtensionField> = FieldElement::new([x_0, x_1]);
327+
let y: FieldElement<Degree2ExtensionField> = FieldElement::new([y_0, y_1]);
328+
329+
let valid_g2_point = BLS12381TwistCurve::create_point_from_affine(x, y).unwrap();
330+
331+
let decompressed_point = BLS12381Curve::decompress_g2_point(&mut compressed_point).unwrap();
332+
333+
assert_eq!(valid_g2_point, decompressed_point);
334+
}
335+
336+
#[cfg(feature = "alloc")]
337+
#[test]
338+
fn test_compress_g2() {
339+
use crate::{
340+
elliptic_curve::short_weierstrass::{
341+
curves::bls12_381::{
342+
field_extension::Degree2ExtensionField, twist::BLS12381TwistCurve,
343+
},
344+
traits::Compress,
345+
},
346+
field::element::FieldElement,
347+
};
348+
349+
// Valig G2 point coordinates:
350+
let x_0 = BLS12381FieldElement::from_hex_unchecked("02");
351+
let x_1 = BLS12381FieldElement::from_hex_unchecked("0");
352+
let y_0 = BLS12381FieldElement::from_hex_unchecked("013a59858b6809fca4d9a3b6539246a70051a3c88899964a42bc9a69cf9acdd9dd387cfa9086b894185b9a46a402be73");
353+
let y_1 = BLS12381FieldElement::from_hex_unchecked("02d27e0ec3356299a346a09ad7dc4ef68a483c3aed53f9139d2f929a3eecebf72082e5e58c6da24ee32e03040c406d4f");
354+
355+
let x: FieldElement<Degree2ExtensionField> = FieldElement::new([x_0, x_1]);
356+
let y: FieldElement<Degree2ExtensionField> = FieldElement::new([y_0, y_1]);
357+
358+
let point = BLS12381TwistCurve::create_point_from_affine(x, y).unwrap();
359+
360+
let compress_point = BLS12381Curve::compress_g2_point(&point);
361+
362+
let mut valid_compressed_point = [0_u8; 96];
363+
valid_compressed_point[0] |= 1 << 7;
364+
valid_compressed_point[95] |= 1 << 1;
365+
366+
assert_eq!(compress_point, valid_compressed_point);
367+
}
229368
}

math/src/elliptic_curve/short_weierstrass/traits.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,15 @@ pub trait IsShortWeierstrass: IsEllipticCurve + Clone + Debug {
2323
pub trait Compress {
2424
type G1Point: IsGroup;
2525
type G2Point: IsGroup;
26+
type G1Compressed;
27+
type G2Compressed;
2628
type Error;
2729

2830
#[cfg(feature = "alloc")]
29-
fn compress_g1_point(point: &Self::G1Point) -> alloc::vec::Vec<u8>;
31+
fn compress_g1_point(point: &Self::G1Point) -> Self::G1Compressed;
32+
33+
#[cfg(feature = "alloc")]
34+
fn compress_g2_point(point: &Self::G2Point) -> Self::G2Compressed;
3035

3136
fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result<Self::G1Point, Self::Error>;
3237

0 commit comments

Comments
 (0)