Skip to content

Commit d2e37c5

Browse files
committed
Add bn254 group
1 parent 56c1a8c commit d2e37c5

6 files changed

Lines changed: 683 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fastcrypto/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ elliptic-curve = { version = "0.13.2", features = ["hash2curve"] }
4646
rsa = { version = "0.8.2", features = ["sha2"] }
4747
static_assertions = "1.1.0"
4848
ark-secp256r1 = "0.4.0"
49+
ark-bn254 = "0.4.0"
4950
ark-ec = "0.4.1"
5051
ark-ff = "0.4.1"
5152
ark-serialize = "0.4.1"

fastcrypto/src/groups/bn254.rs

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
// Copyright (c) 2022, Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use std::fmt::Debug;
5+
use std::ops::{Div, Mul};
6+
7+
use ark_bn254::{Bn254, Fr, G1Affine, G1Projective, G2Affine, G2Projective};
8+
use ark_ec::pairing::{Pairing as ArkworksPairing, PairingOutput};
9+
use ark_ec::{AffineRepr, Group};
10+
use ark_ff::{Field, One, UniformRand, Zero};
11+
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
12+
use derive_more::{Add, Neg, Sub};
13+
use once_cell::sync::OnceCell;
14+
use serde::{de, Deserialize};
15+
16+
use fastcrypto_derive::GroupOpsExtend;
17+
18+
use crate::error::{FastCryptoError, FastCryptoResult};
19+
use crate::groups::{FromTrustedByteArray, GroupElement, Pairing, Scalar as ScalarType};
20+
use crate::serde_helpers::ToFromByteArray;
21+
use crate::traits::AllowedRng;
22+
use crate::{groups, serialize_deserialize_with_to_from_byte_array};
23+
24+
/// Elements of the group G_1 in BN254.
25+
#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)]
26+
#[repr(transparent)]
27+
pub struct G1Element(G1Projective);
28+
29+
/// Elements of the group G_2 in BN254.
30+
#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)]
31+
#[repr(transparent)]
32+
pub struct G2Element(G2Projective);
33+
34+
/// Elements of the subgroup G_T of F_q^{12} in BN254. Note that it is written in additive notation here.
35+
#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)]
36+
pub struct GTElement(PairingOutput<Bn254>);
37+
38+
/// This represents a scalar modulo r = 21888242871839275222246405745257275088548364400416034343698204186575808495617
39+
/// which is the order of the groups G1, G2 and GT. Note that r is a 254 bit prime.
40+
#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)]
41+
pub struct Scalar(Fr);
42+
43+
pub const SCALAR_LENGTH: usize = 32;
44+
pub const G1_ELEMENT_BYTE_LENGTH: usize = 32;
45+
pub const G2_ELEMENT_BYTE_LENGTH: usize = 64;
46+
pub const GT_ELEMENT_BYTE_LENGTH: usize = 384;
47+
48+
impl Div<Self> for Scalar {
49+
type Output = FastCryptoResult<Self>;
50+
51+
fn div(self, rhs: Self) -> FastCryptoResult<Self> {
52+
if rhs.0.is_zero() {
53+
return Err(FastCryptoError::InvalidInput);
54+
}
55+
Ok(Self(self.0.div(rhs.0)))
56+
}
57+
}
58+
59+
impl Mul<Scalar> for Scalar {
60+
type Output = Self;
61+
62+
fn mul(self, rhs: Scalar) -> Self::Output {
63+
Self(self.0.mul(rhs.0))
64+
}
65+
}
66+
67+
impl GroupElement for Scalar {
68+
type ScalarType = Scalar;
69+
70+
fn zero() -> Self {
71+
Self(Fr::zero())
72+
}
73+
74+
fn generator() -> Self {
75+
Self(Fr::one())
76+
}
77+
}
78+
79+
impl GroupElement for G1Element {
80+
type ScalarType = Scalar;
81+
82+
fn zero() -> Self {
83+
G1Element(G1Projective::zero())
84+
}
85+
86+
fn generator() -> Self {
87+
G1Element(G1Projective::generator())
88+
}
89+
}
90+
91+
impl Div<Scalar> for G1Element {
92+
type Output = FastCryptoResult<Self>;
93+
94+
fn div(self, rhs: Scalar) -> Self::Output {
95+
let inverse = rhs.inverse()?;
96+
Ok(self.mul(inverse))
97+
}
98+
}
99+
100+
impl Mul<Scalar> for G1Element {
101+
type Output = Self;
102+
103+
fn mul(self, rhs: Scalar) -> Self::Output {
104+
Self(self.0.mul(rhs.0))
105+
}
106+
}
107+
108+
impl ToFromByteArray<32> for G1Element {
109+
fn from_byte_array(bytes: &[u8; 32]) -> Result<Self, FastCryptoError> {
110+
let point = G1Affine::deserialize_compressed(bytes.as_slice())
111+
.map_err(|_| FastCryptoError::InvalidInput)?;
112+
113+
// Arkworks only checks the infinty flag, but we require all-zeros to have unique serialization
114+
if point.is_zero() && bytes[0..31].iter().any(|x| !x.is_zero()) {
115+
return Err(FastCryptoError::InvalidInput);
116+
}
117+
118+
Ok(Self(G1Projective::from(point)))
119+
}
120+
121+
fn to_byte_array(&self) -> [u8; 32] {
122+
let mut bytes = [0u8; 32];
123+
self.0
124+
.serialize_compressed(bytes.as_mut_slice())
125+
.expect("Never fails");
126+
bytes
127+
}
128+
}
129+
130+
serialize_deserialize_with_to_from_byte_array!(G1Element);
131+
132+
impl FromTrustedByteArray<32> for G1Element {
133+
fn from_trusted_byte_array(bytes: &[u8; 32]) -> FastCryptoResult<Self> {
134+
G1Projective::deserialize_compressed_unchecked(bytes.as_slice())
135+
.map_err(|_| FastCryptoError::InvalidInput)
136+
.map(G1Element)
137+
}
138+
}
139+
140+
impl GroupElement for G2Element {
141+
type ScalarType = Scalar;
142+
143+
fn zero() -> Self {
144+
G2Element(G2Projective::zero())
145+
}
146+
147+
fn generator() -> Self {
148+
G2Element(G2Projective::generator())
149+
}
150+
}
151+
152+
impl Div<Scalar> for G2Element {
153+
type Output = FastCryptoResult<Self>;
154+
155+
fn div(self, rhs: Scalar) -> Self::Output {
156+
let inverse = rhs.inverse()?;
157+
Ok(self.mul(inverse))
158+
}
159+
}
160+
161+
impl Mul<Scalar> for G2Element {
162+
type Output = Self;
163+
164+
fn mul(self, rhs: Scalar) -> Self::Output {
165+
Self(self.0.mul(rhs.0))
166+
}
167+
}
168+
169+
impl ToFromByteArray<64> for G2Element {
170+
fn from_byte_array(bytes: &[u8; 64]) -> Result<Self, FastCryptoError> {
171+
let point = G2Affine::deserialize_compressed(bytes.as_slice())
172+
.map_err(|_| FastCryptoError::InvalidInput)?;
173+
174+
// Arkworks only checks the infinty flag, but we require all-zeros to have unique serialization
175+
if point.is_zero() && bytes[0..63].iter().any(|x| !x.is_zero()) {
176+
return Err(FastCryptoError::InvalidInput);
177+
}
178+
179+
Ok(Self(G2Projective::from(point)))
180+
}
181+
182+
fn to_byte_array(&self) -> [u8; 64] {
183+
let mut bytes = [0u8; 64];
184+
self.0
185+
.serialize_compressed(bytes.as_mut_slice())
186+
.expect("Never fails");
187+
bytes
188+
}
189+
}
190+
191+
serialize_deserialize_with_to_from_byte_array!(G2Element);
192+
193+
impl FromTrustedByteArray<64> for G2Element {
194+
fn from_trusted_byte_array(bytes: &[u8; 64]) -> FastCryptoResult<Self> {
195+
G2Projective::deserialize_compressed_unchecked(bytes.as_slice())
196+
.map_err(|_| FastCryptoError::InvalidInput)
197+
.map(G2Element)
198+
}
199+
}
200+
201+
impl From<u128> for Scalar {
202+
fn from(value: u128) -> Self {
203+
Self(Fr::from(value))
204+
}
205+
}
206+
207+
impl groups::Scalar for Scalar {
208+
fn rand<R: AllowedRng>(rng: &mut R) -> Self {
209+
Self(Fr::rand(rng))
210+
}
211+
212+
fn inverse(&self) -> FastCryptoResult<Self> {
213+
Ok(Self(self.0.inverse().ok_or(FastCryptoError::InvalidInput)?))
214+
}
215+
}
216+
217+
impl ToFromByteArray<32> for Scalar {
218+
fn from_byte_array(bytes: &[u8; Self::BYTE_LENGTH]) -> Result<Self, FastCryptoError> {
219+
// Arkworks uses little-endian byte order for serialization, but we use big-endian.
220+
let mut reversed = *bytes;
221+
reversed.reverse();
222+
Fr::deserialize_compressed(reversed.as_slice())
223+
.map_err(|_| FastCryptoError::InvalidInput)
224+
.map(Scalar)
225+
}
226+
227+
fn to_byte_array(&self) -> [u8; Self::BYTE_LENGTH] {
228+
let mut bytes = [0u8; Self::BYTE_LENGTH];
229+
self.0
230+
.serialize_compressed(bytes.as_mut_slice())
231+
.expect("Never fails");
232+
// Arkworks uses little-endian byte order for serialization, but we use big-endian.
233+
bytes.reverse();
234+
bytes
235+
}
236+
}
237+
238+
serialize_deserialize_with_to_from_byte_array!(Scalar);
239+
240+
impl Pairing for G1Element {
241+
type Other = G2Element;
242+
type Output = GTElement;
243+
244+
fn pairing(&self, other: &Self::Other) -> <Self as Pairing>::Output {
245+
GTElement(Bn254::pairing(self.0, other.0))
246+
}
247+
}
248+
249+
#[allow(clippy::suspicious_arithmetic_impl)]
250+
impl Div<Scalar> for GTElement {
251+
type Output = FastCryptoResult<GTElement>;
252+
253+
fn div(self, rhs: Scalar) -> Self::Output {
254+
let inverse = rhs.inverse()?;
255+
Ok(self * inverse)
256+
}
257+
}
258+
259+
impl Mul<Scalar> for GTElement {
260+
type Output = GTElement;
261+
262+
fn mul(self, rhs: Scalar) -> Self::Output {
263+
Self(self.0.mul(rhs.0))
264+
}
265+
}
266+
267+
impl GroupElement for GTElement {
268+
type ScalarType = Scalar;
269+
270+
fn zero() -> Self {
271+
GTElement(PairingOutput::zero())
272+
}
273+
274+
fn generator() -> Self {
275+
static G: OnceCell<PairingOutput<Bn254>> = OnceCell::new();
276+
Self(*G.get_or_init(Self::compute_generator))
277+
}
278+
}
279+
280+
impl GTElement {
281+
fn compute_generator() -> PairingOutput<Bn254> {
282+
G1Element::generator().pairing(&G2Element::generator()).0
283+
}
284+
}
285+
286+
impl FromTrustedByteArray<384> for GTElement {
287+
fn from_trusted_byte_array(bytes: &[u8; 384]) -> FastCryptoResult<Self> {
288+
PairingOutput::<Bn254>::deserialize_compressed_unchecked(bytes.as_ref())
289+
.map_err(|_| FastCryptoError::InvalidInput)
290+
.map(GTElement)
291+
}
292+
}
293+
294+
impl ToFromByteArray<384> for GTElement {
295+
fn from_byte_array(bytes: &[u8; 384]) -> Result<Self, FastCryptoError> {
296+
PairingOutput::<Bn254>::deserialize_compressed(bytes.as_ref())
297+
.map_err(|_| FastCryptoError::InvalidInput)
298+
.map(GTElement)
299+
}
300+
301+
fn to_byte_array(&self) -> [u8; 384] {
302+
let mut bytes = [0u8; 384];
303+
self.0
304+
.serialize_compressed(bytes.as_mut_slice())
305+
.expect("Never fails");
306+
bytes
307+
}
308+
}
309+
310+
serialize_deserialize_with_to_from_byte_array!(GTElement);

fastcrypto/src/groups/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::fmt::Debug;
1010
use std::ops::{AddAssign, SubAssign};
1111

1212
pub mod bls12381;
13+
pub mod bn254;
1314
pub mod ristretto255;
1415
pub mod secp256r1;
1516

fastcrypto/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ pub mod utils_tests;
8484
#[path = "tests/secp256r1_group_tests.rs"]
8585
pub mod secp256r1_group_tests;
8686

87+
#[cfg(test)]
88+
#[path = "tests/bn254_group_tests.rs"]
89+
pub mod bn254_group_tests;
90+
8791
pub mod traits;
8892

8993
#[cfg(feature = "aes")]

0 commit comments

Comments
 (0)