Skip to content

Commit 9f7b369

Browse files
fix overflow (#894)
* fix overflow * fmt * simplify condition * Fix and improve fuzzers * Remove u64 since now they are full fields * Fix mini goldilocks fuzz * Add fuzzer * Fix * Fix * Fix * Fix * Fix --------- Co-authored-by: MauroFab <[email protected]>
1 parent a68d555 commit 9f7b369

File tree

4 files changed

+113
-6
lines changed

4 files changed

+113
-6
lines changed

fuzz/no_gpu_fuzz/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ path = "fuzz_targets/curve/grumpkin.rs"
3535
test = false
3636
doc = false
3737

38+
[[bin]]
39+
name = "secp256k1"
40+
path = "fuzz_targets/field/secp256k1.rs"
41+
test = false
42+
doc = false
43+
3844
[[bin]]
3945
name = "stark252"
4046
path = "fuzz_targets/field/stark252.rs"
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#![no_main]
2+
3+
use libfuzzer_sys::fuzz_target;
4+
use lambdaworks_math::field::{
5+
element::FieldElement
6+
};
7+
use ibig::{modular::ModuloRing, UBig};
8+
use lambdaworks_math::traits::ByteConversion;
9+
use lambdaworks_math::field::fields::montgomery_backed_prime_fields::U256PrimeField;
10+
use lambdaworks_math::unsigned_integer::element::U256;
11+
use lambdaworks_math::field::fields::montgomery_backed_prime_fields::IsModulus;
12+
13+
#[derive(Clone, Debug, Hash, Copy)]
14+
pub struct MontgomeryConfigSecpPrimeField;
15+
16+
impl IsModulus<U256> for MontgomeryConfigSecpPrimeField {
17+
const MODULUS: U256 =
18+
U256::from_hex_unchecked("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
19+
}
20+
21+
pub type SecpPrimeField = U256PrimeField<MontgomeryConfigSecpPrimeField>;
22+
23+
fuzz_target!(|bytes: ([u8;32], [u8;32])| {
24+
25+
let secp256k1_prime =
26+
UBig::from_str_radix("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16).unwrap();
27+
28+
let secp256k1_ring_prime = ModuloRing::new(&secp256k1_prime);
29+
30+
let (bytes_a, bytes_b) = bytes;
31+
let a = FieldElement::<SecpPrimeField>::from_bytes_be(&bytes_a).unwrap();
32+
let b = FieldElement::<SecpPrimeField>::from_bytes_be(&bytes_b).unwrap();
33+
34+
let a_hex = a.to_string()[2..].to_string();
35+
let b_hex = b.to_string()[2..].to_string();
36+
37+
let a_ring = secp256k1_ring_prime.from(&UBig::from_str_radix(&a_hex, 16).unwrap());
38+
let b_ring = secp256k1_ring_prime.from(&UBig::from_str_radix(&b_hex, 16).unwrap());
39+
40+
let add = &a + &b;
41+
let addition = &a_ring + &b_ring;
42+
43+
assert_eq!(&(add.to_string())[2..], addition.residue().in_radix(16).to_string());
44+
45+
let sub = &a - &b;
46+
let substraction = &a_ring - &b_ring;
47+
assert_eq!(&(sub.to_string())[2..], substraction.residue().in_radix(16).to_string());
48+
49+
let mul = &a * &b;
50+
let multiplication = &a_ring * &b_ring;
51+
assert_eq!(&(mul.to_string())[2..], multiplication.residue().in_radix(16).to_string());
52+
53+
let pow = &a.pow(b.representative());
54+
let expected_pow = a_ring.pow(&b_ring.residue());
55+
assert_eq!(&(pow.to_string())[2..], expected_pow.residue().in_radix(16).to_string());
56+
57+
if b != FieldElement::zero() {
58+
59+
let div = &a / &b;
60+
assert_eq!(&div * &b, a.clone());
61+
let expected_div = &a_ring / &b_ring;
62+
assert_eq!(&(div.to_string())[2..], expected_div.residue().in_radix(16).to_string());
63+
}
64+
65+
for n in [&a, &b] {
66+
match n.sqrt() {
67+
Some((fst_sqrt, snd_sqrt)) => {
68+
assert_eq!(fst_sqrt.square(), snd_sqrt.square(), "Squared roots don't match each other");
69+
assert_eq!(n, &fst_sqrt.square(), "Squared roots don't match original number");
70+
}
71+
None => {}
72+
};
73+
}
74+
75+
// Axioms soundness
76+
77+
let one = FieldElement::<SecpPrimeField>::one();
78+
let zero = FieldElement::<SecpPrimeField>::zero();
79+
80+
assert_eq!(&a + &zero, a, "Neutral add element a failed");
81+
assert_eq!(&b + &zero, b, "Neutral mul element b failed");
82+
assert_eq!(&a * &one, a, "Neutral add element a failed");
83+
assert_eq!(&b * &one, b, "Neutral mul element b failed");
84+
85+
assert_eq!(&a + &b, &b + &a, "Commutative add property failed");
86+
assert_eq!(&a * &b, &b * &a, "Commutative mul property failed");
87+
88+
let c = &a * &b;
89+
assert_eq!((&a + &b) + &c, &a + (&b + &c), "Associative add property failed");
90+
assert_eq!((&a * &b) * &c, &a * (&b * &c), "Associative mul property failed");
91+
92+
assert_eq!(&a * (&b + &c), &a * &b + &a * &c, "Distributive property failed");
93+
94+
assert_eq!(&a - &a, zero, "Inverse add a failed");
95+
assert_eq!(&b - &b, zero, "Inverse add b failed");
96+
97+
if a != zero {
98+
assert_eq!(&a * a.inv().unwrap(), one, "Inverse mul a failed");
99+
}
100+
if b != zero {
101+
assert_eq!(&b * b.inv().unwrap(), one, "Inverse mul b failed");
102+
}
103+
});

math/src/field/fields/montgomery_backed_prime_fields.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,7 @@ where
150150

151151
#[inline(always)]
152152
fn square(a: &UnsignedInteger<NUM_LIMBS>) -> UnsignedInteger<NUM_LIMBS> {
153-
if Self::MODULUS_HAS_ONE_SPARE_BIT {
154-
MontgomeryAlgorithms::sos_square(a, &M::MODULUS, &Self::MU)
155-
} else {
156-
MontgomeryAlgorithms::cios(a, a, &M::MODULUS, &Self::MU)
157-
}
153+
MontgomeryAlgorithms::sos_square(a, &M::MODULUS, &Self::MU)
158154
}
159155

160156
#[inline(always)]

math/src/unsigned_integer/montgomery.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ impl MontgomeryAlgorithms {
158158
// `q`.
159159
let mut c: u128 = 0;
160160
let mut i = NUM_LIMBS;
161+
let mut overflow = false;
161162
while i > 0 {
162163
i -= 1;
163164
c = 0;
@@ -186,6 +187,7 @@ impl MontgomeryAlgorithms {
186187
hi.limbs[i - t] = cs as u64;
187188
t += 1;
188189
}
190+
overflow |= c > 0;
189191
}
190192

191193
// Step 3: At this point `overflow * 2^{2 * NUM_LIMBS * 64} + (hi, lo)` is a multiple
@@ -197,7 +199,7 @@ impl MontgomeryAlgorithms {
197199
// The easy case is when `overflow` is zero. We just use the `sub` function.
198200
// If `overflow` is 1, then `hi` is smaller than `q`. The function `sub(hi, q)` wraps
199201
// around `2^{NUM_LIMBS * 64}`. This is the result we need.
200-
let overflow = c > 0;
202+
overflow |= c > 0;
201203
if overflow || UnsignedInteger::const_le(q, &hi) {
202204
(hi, _) = UnsignedInteger::sub(&hi, q);
203205
}

0 commit comments

Comments
 (0)