Skip to content

Commit 86de329

Browse files
feat: bls signature (#216)
* hello bls * hello bls * tests added * tests added * bls signature refactored * bls signature refactored * bls completed * bls completed * a little refactoring to adjust hash_tocurve * a little refactoring to adjust hash_tocurve * bls basic features test passed * bls completed * bls completed * typo fixed * review edit added * review edit added * generic over galoisfield * galoisfield generic * fix : totally generic over fields and ellipticCurve * additional checks added * elliminating conflicts * elliminating conflicts * refactor: signatures * additional refactorings * readmes added * latex added * corrections added * Update src/signatures/bls/README.md Co-authored-by: Colin Roberts <colin@autoparallel.xyz> * Update src/signatures/bls/README.md Co-authored-by: Colin Roberts <colin@autoparallel.xyz> * Update src/signatures/bls/README.md Co-authored-by: Colin Roberts <colin@autoparallel.xyz> * refactorings and additions * refactorings and additions * refactorings and additions * refactorings and additions * refactorings and additions * refactorings and additions * signatures cleanup * signatures cleanup --------- Co-authored-by: = <=> Co-authored-by: Colin Roberts <colin@autoparallel.xyz>
1 parent 3ec83fc commit 86de329

26 files changed

Lines changed: 1009 additions & 249 deletions

File tree

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ Ronkathon is a collection of cryptographic primitives implemented in Rust. It is
3232

3333
### Signatures
3434

35-
- [Digital Signature Algorithms](src/dsa/README.md)
36-
- [Elliptic Curve Digital Signature Algorithm(ECDSA)](src/dsa/ecdsa.rs)
37-
- [Edwards-Curve Digital Signature Algorithm(EdDSA)](src/dsa/eddsa/mod.rs)
35+
- [Signature Algorithms](src/signatures/README.md)
36+
- [Elliptic Curve Digital Signature Algorithm(ECDSA)](src/signatures/ecdsa.rs)
37+
- [Edwards-Curve Digital Signature Algorithm(EdDSA)](src/signatures/eddsa/mod.rs)
38+
- [Boneh-Lynn-Shacham SIgnature](src/signatures/bls/mod.rs)
39+
- [Lamport Signature](src/signatures/lamport/mod.rs)
3840

3941
### Encryption
4042

SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
- [Codes](src/codes/README.md)
77
- [Compiler](src/compiler/README.md)
88
- [Curve](src/curve/README.md)
9-
- [Digital Signature Algorithms](src/dsa/README.md)
9+
- [Signature Algorithms](src/signatures/README.md)
1010
- [Encryption]()
1111
- [Asymmetric]()
1212
- [RSA](src/encryption/asymmetric/rsa/README.md)

examples/eddsa.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// Example of Ed25519 digital signature algorithm.
2-
use ronkathon::dsa::eddsa::Ed25519;
2+
use ronkathon::signatures::eddsa::Ed25519;
33

44
fn main() {
55
let ed25519 = Ed25519::new(None);

src/algebra/field/extension/arithmetic.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,60 @@ impl<const N: usize, const P: usize> Mul<GaloisField<N, P>> for PrimeField<P> {
155155
res
156156
}
157157
}
158+
159+
impl FieldExt for GaloisField<2, 101> {
160+
/// Computes euler criterion of the field element, i.e. Returns true if the element is a quadratic
161+
/// residue (a square number) in the field.
162+
fn euler_criterion(&self) -> bool { self.norm().euler_criterion() }
163+
164+
/// Computes square root of the quadratic field element `(x_0 + x_1*u)` (if it exists) and return
165+
/// a tuple of `(r, -r)` where `r` is lower.
166+
///
167+
/// - `(a_0 + a_1*u)^2 = a_0^2+2*a_0*a_1*u+βa_1^2 = (x_0 + x_1*u)`. Equating x_0 and x_1 with LHS:
168+
/// - `x_0 = a_0^2 + βa_1^2`
169+
/// - `x_1 = 2a_0*a_1`
170+
fn sqrt(&self) -> Option<(Self, Self)> {
171+
let (a0, a1) = (self.coeffs[0], self.coeffs[1]);
172+
173+
// irreducible poly: F[X]/(X^2+2)
174+
let residue = -PlutoBaseFieldExtension::IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS[0];
175+
176+
// x_0 = a_0^2 + βa_1^2
177+
if a1 == PlutoBaseField::ZERO {
178+
// if a_1 = 0, then straight away compute sqrt of a_0 as base field element
179+
if a0.euler_criterion() {
180+
return a0.sqrt().map(|(res0, res1)| (Self::from(res0), Self::from(res1)));
181+
} else {
182+
// if a_0 is not a square, then compute a_1 = sqrt(x_0 / β)
183+
return a0.div(residue).sqrt().map(|(res0, res1)| {
184+
(Self::new([PlutoBaseField::ZERO, res0]), Self::new([PlutoBaseField::ZERO, res1]))
185+
});
186+
}
187+
}
188+
189+
// x_0 = ((a_0 ± (a_0² − βa_1²)^½)/2)^½
190+
// x_1 = a_1/(2x_0)
191+
192+
// α = (a_0² − βa_1²)
193+
let alpha = self.norm();
194+
let two_inv = PlutoBaseField::new(2).inverse().expect("2 should have an inverse");
195+
196+
alpha.sqrt().map(|(alpha, _)| {
197+
let mut delta = (alpha + a0) * two_inv;
198+
if !delta.euler_criterion() {
199+
delta -= alpha;
200+
}
201+
202+
let x0 = delta.sqrt().expect("delta must have an square root").0;
203+
let x0_inv = x0.inverse().expect("x0 must have an inverse");
204+
let x1 = a1 * two_inv * x0_inv;
205+
let x = Self::new([x0, x1]);
206+
if -x < x {
207+
(-x, x)
208+
} else {
209+
(x, -x)
210+
}
211+
})
212+
}
213+
}
158214
///////////////////////////////////////////////////////////////////////////////////////////////

src/algebra/field/extension/gf_101_2.rs

Lines changed: 4 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,65 +18,11 @@ impl ExtensionField<2, 101> for PlutoBaseFieldExtension {
1818
}
1919

2020
impl PlutoBaseFieldExtension {
21-
fn norm(&self) -> PlutoBaseField {
21+
pub fn norm(&self) -> PlutoBaseField {
2222
let mut result = self.coeffs[0].pow(2);
2323
result -= -PlutoBaseField::new(2) * self.coeffs[1].pow(2);
2424
result
2525
}
26-
27-
/// Computes euler criterion of the field element, i.e. Returns true if the element is a quadratic
28-
/// residue (a square number) in the field.
29-
pub fn euler_criterion(&self) -> bool { self.norm().euler_criterion() }
30-
31-
/// Computes square root of the quadratic field element `(x_0 + x_1*u)` (if it exists) and return
32-
/// a tuple of `(r, -r)` where `r` is lower.
33-
///
34-
/// - `(a_0 + a_1*u)^2 = a_0^2+2*a_0*a_1*u+βa_1^2 = (x_0 + x_1*u)`. Equating x_0 and x_1 with LHS:
35-
/// - `x_0 = a_0^2 + βa_1^2`
36-
/// - `x_1 = 2a_0*a_1`
37-
pub fn sqrt(&self) -> Option<(Self, Self)> {
38-
let (a0, a1) = (self.coeffs[0], self.coeffs[1]);
39-
40-
// irreducible poly: F[X]/(X^2+2)
41-
let residue = -PlutoBaseFieldExtension::IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS[0];
42-
43-
// x_0 = a_0^2 + βa_1^2
44-
if a1 == PlutoBaseField::ZERO {
45-
// if a_1 = 0, then straight away compute sqrt of a_0 as base field element
46-
if a0.euler_criterion() {
47-
return a0.sqrt().map(|(res0, res1)| (Self::from(res0), Self::from(res1)));
48-
} else {
49-
// if a_0 is not a square, then compute a_1 = sqrt(x_0 / β)
50-
return a0.div(residue).sqrt().map(|(res0, res1)| {
51-
(Self::new([PlutoBaseField::ZERO, res0]), Self::new([PlutoBaseField::ZERO, res1]))
52-
});
53-
}
54-
}
55-
56-
// x_0 = ((a_0 ± (a_0² − βa_1²)^½)/2)^½
57-
// x_1 = a_1/(2x_0)
58-
59-
// α = (a_0² − βa_1²)
60-
let alpha = self.norm();
61-
let two_inv = PlutoBaseField::new(2).inverse().expect("2 should have an inverse");
62-
63-
alpha.sqrt().map(|(alpha, _)| {
64-
let mut delta = (alpha + a0) * two_inv;
65-
if !delta.euler_criterion() {
66-
delta -= alpha;
67-
}
68-
69-
let x0 = delta.sqrt().expect("delta must have an square root").0;
70-
let x0_inv = x0.inverse().expect("x0 must have an inverse");
71-
let x1 = a1 * two_inv * x0_inv;
72-
let x = Self::new([x0, x1]);
73-
if -x < x {
74-
(-x, x)
75-
} else {
76-
(x, -x)
77-
}
78-
})
79-
}
8026
}
8127

8228
impl Field for PlutoBaseFieldExtension {
@@ -179,6 +125,9 @@ impl Rem for PlutoBaseFieldExtension {
179125
fn rem(self, rhs: Self) -> Self::Output { self - (self / rhs) * rhs }
180126
}
181127

128+
impl From<[PlutoBaseField; 2]> for PlutoBaseFieldExtension {
129+
fn from(value: [PlutoBaseField; 2]) -> Self { Self { coeffs: value } }
130+
}
182131
#[cfg(test)]
183132
mod tests {
184133
use rstest::rstest;

src/algebra/field/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,11 @@ pub trait FiniteField: Finite + Field {
7474
Self::PRIMITIVE_ELEMENT.pow(pow)
7575
}
7676
}
77+
78+
/// traits that define some functions usable in signatures
79+
pub trait FieldExt: Sized {
80+
/// Returns the square root of the field element.
81+
fn sqrt(&self) -> Option<(Self, Self)>;
82+
83+
fn euler_criterion(&self) -> bool;
84+
}

src/algebra/field/prime/mod.rs

Lines changed: 85 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -49,90 +49,6 @@ impl<const P: usize> PrimeField<P> {
4949
is_prime(P);
5050
Self { value: value % P }
5151
}
52-
53-
/// Computes euler criterion of the field element, i.e. Returns true if the element is a quadratic
54-
/// residue (a square number) in the field.
55-
///
56-
/// ## NOTES
57-
/// By fermat's little theorem, (assume `is_congruent_to` is =)
58-
/// x^(p-1) - 1 = 0 mod P
59-
///
60-
/// All primes > 2 are odd, a.k.a P is odd, hence (P-1) is even.
61-
/// So, we can split as follows:
62-
/// (x^(p-1)/2 - 1)(x^(p-1)/2 + 1) = 0 mod P
63-
/// or L * R = 0 mod P
64-
///
65-
/// All quadratic residues are of the form (g^(2k)) where `g` is the
66-
/// multiplicative generator and k is some natural number. All non-residues
67-
/// on the other hand are of the form (g^(2k+1)).
68-
///
69-
/// In case of QR, substitute x = g^2k
70-
/// g^(2k)((p-1)/2) = 1 mod P
71-
/// g^(p-1) = 1 mod P
72-
/// which is true by fermat's little theorem
73-
///
74-
/// In the other case, the same doesn't hold.
75-
/// Hence, the case `L` should hold for all quadratic residues and is the
76-
/// test for quadratic residuosity.
77-
///
78-
/// More info here: https://www.youtube.com/watch?v=2IBPOI43jek
79-
pub fn euler_criterion(&self) -> bool { self.pow((P - 1) / 2).value == 1 }
80-
81-
/// Computes the square root of a field element using the [Tonelli-Shanks algorithm](https://en.wikipedia.org/wiki/Tonelli–Shanks_algorithm).
82-
pub fn sqrt(&self) -> Option<(Self, Self)> {
83-
if *self == Self::ZERO {
84-
return Some((Self::ZERO, Self::ZERO));
85-
}
86-
87-
assert!(self.euler_criterion(), "Element is not a quadratic residue");
88-
89-
// First define the Q and S values for the prime number P.
90-
let q: usize;
91-
let mut s = 1;
92-
loop {
93-
let lhs = P - 1;
94-
let rhs = 2_usize.pow(s);
95-
let check_value = lhs % rhs;
96-
if check_value == 0 {
97-
s += 1;
98-
} else {
99-
s -= 1;
100-
q = (P - 1) / 2_usize.pow(s);
101-
break;
102-
}
103-
}
104-
105-
// Find a z that is not a quadratic residue
106-
let mut z = Self::new(2);
107-
while z.euler_criterion() {
108-
z += Self::ONE;
109-
}
110-
let mut m = s;
111-
let mut c = z.pow(q);
112-
let mut t = self.pow(q);
113-
let mut r = self.pow((q + 1) / 2);
114-
loop {
115-
if t == Self::ONE {
116-
if -r < r {
117-
return Some((-r, r));
118-
} else {
119-
return Some((r, -r));
120-
}
121-
}
122-
// Repeatedly square to find a t^2^i = 1
123-
let mut i = 1;
124-
let mut t_pow = t.pow(2);
125-
while t_pow != Self::ONE {
126-
t_pow = t_pow.pow(2);
127-
i += 1;
128-
}
129-
let b = c.pow(2_usize.pow(m - i - 1));
130-
m = i;
131-
c = b.pow(2);
132-
t *= c;
133-
r *= b;
134-
}
135-
}
13652
}
13753

13854
impl<const P: usize> const Finite for PrimeField<P> {
@@ -223,6 +139,91 @@ impl<const P: usize> Distribution<PrimeField<P>> for Standard {
223139
}
224140
}
225141

142+
impl<const P: usize> FieldExt for PrimeField<P> {
143+
/// Computes euler criterion of the field element, i.e. Returns true if the element is a quadratic
144+
/// residue (a square number) in the field.
145+
///
146+
/// ## NOTES
147+
/// By fermat's little theorem, (assume `is_congruent_to` is =)
148+
/// x^(p-1) - 1 = 0 mod P
149+
///
150+
/// All primes > 2 are odd, a.k.a P is odd, hence (P-1) is even.
151+
/// So, we can split as follows:
152+
/// (x^(p-1)/2 - 1)(x^(p-1)/2 + 1) = 0 mod P
153+
/// or L * R = 0 mod P
154+
///
155+
/// All quadratic residues are of the form (g^(2k)) where `g` is the
156+
/// multiplicative generator and k is some natural number. All non-residues
157+
/// on the other hand are of the form (g^(2k+1)).
158+
///
159+
/// In case of QR, substitute x = g^2k
160+
/// g^(2k)((p-1)/2) = 1 mod P
161+
/// g^(p-1) = 1 mod P
162+
/// which is true by fermat's little theorem
163+
///
164+
/// In the other case, the same doesn't hold.
165+
/// Hence, the case `L` should hold for all quadratic residues and is the
166+
/// test for quadratic residuosity.
167+
///
168+
/// More info here: https://www.youtube.com/watch?v=2IBPOI43jek
169+
fn euler_criterion(&self) -> bool { self.pow((P - 1) / 2).value == 1 }
170+
171+
/// Computes the square root of a field element using the [Tonelli-Shanks algorithm](https://en.wikipedia.org/wiki/Tonelli–Shanks_algorithm).
172+
fn sqrt(&self) -> Option<(Self, Self)> {
173+
if *self == Self::ZERO {
174+
return Some((Self::ZERO, Self::ZERO));
175+
}
176+
177+
assert!(self.euler_criterion(), "Element is not a quadratic residue");
178+
179+
// First define the Q and S values for the prime number P.
180+
let q: usize;
181+
let mut s = 1;
182+
loop {
183+
let lhs = P - 1;
184+
let rhs = 2_usize.pow(s);
185+
let check_value = lhs % rhs;
186+
if check_value == 0 {
187+
s += 1;
188+
} else {
189+
s -= 1;
190+
q = (P - 1) / 2_usize.pow(s);
191+
break;
192+
}
193+
}
194+
195+
// Find a z that is not a quadratic residue
196+
let mut z = Self::new(2);
197+
while z.euler_criterion() {
198+
z += Self::ONE;
199+
}
200+
let mut m = s;
201+
let mut c = z.pow(q);
202+
let mut t = self.pow(q);
203+
let mut r = self.pow((q + 1) / 2);
204+
loop {
205+
if t == Self::ONE {
206+
if -r < r {
207+
return Some((-r, r));
208+
} else {
209+
return Some((r, -r));
210+
}
211+
}
212+
// Repeatedly square to find a t^2^i = 1
213+
let mut i = 1;
214+
let mut t_pow = t.pow(2);
215+
while t_pow != Self::ONE {
216+
t_pow = t_pow.pow(2);
217+
i += 1;
218+
}
219+
let b = c.pow(2_usize.pow(m - i - 1));
220+
m = i;
221+
c = b.pow(2);
222+
t *= c;
223+
r *= b;
224+
}
225+
}
226+
}
226227
impl<const P: usize> From<u32> for PrimeField<P> {
227228
fn from(val: u32) -> Self { Self::new(val as usize) }
228229
}

src/curve/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use algebra::{
1010

1111
use super::*;
1212
use crate::{
13-
algebra::group::{FiniteCyclicGroup, Group},
13+
algebra::{
14+
field::FieldExt,
15+
group::{FiniteCyclicGroup, Group},
16+
},
1417
Field, PlutoScalarField,
1518
};
1619

@@ -26,10 +29,10 @@ pub trait EllipticCurve: Copy + Debug + Eq {
2629

2730
/// curve base field element type
2831
/// TODO: need to be converted for big integers later
29-
type BaseField: FiniteField + Into<usize>;
32+
type BaseField: FiniteField + Into<usize> + FieldExt;
3033

3134
/// Curve scalar field type
32-
type ScalarField: FiniteField + Into<usize>;
35+
type ScalarField: FiniteField + Into<usize> + FieldExt;
3336

3437
/// Order of this elliptic curve, i.e. number of elements in the scalar field.
3538
const ORDER: usize;

src/curve/pluto_curve.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use super::*;
1212
use crate::algebra::field::extension::PlutoExtensions;
1313

14-
/// The [`PlutoBaseCurve`] is an the base field set to the [`PlutoBaseField`]. This is the curve
14+
/// The [`PlutoBaseCurve`] has the base field set to the [`PlutoBaseField`]. This is the curve
1515
/// used in the Pluto `ronkathon` system. The curve is defined by the equation `y^2 = x^3 + 3`.
1616
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
1717
pub struct PlutoBaseCurve;

0 commit comments

Comments
 (0)