-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modified fuzz tests about field operations #1407
base: master
Are you sure you want to change the base?
Conversation
@YafeiXie1 Just FYI, you don't need to open a new pull request when you make changes. You can push again to the same branch with more commits (or if you modified existing commits, you'd need a force push, but it's still possible). |
src/fuzz.c
Outdated
} | ||
} | ||
|
||
#ifdef VERIFY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
src/fuzz.c
Outdated
if (size >= 32) { | ||
secp256k1_fe a; | ||
fuzz_field_construct(data, size, &a); | ||
if (a.normalized) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test could be a lot more efficient if fuzz_field_construct
had an input argument that can guarantee it only produces normalized field elements (because as-is, in many cases no test will be performed at all). Alternatively, there could be a separate function for just that.
src/fuzz.c
Outdated
int rand_magnitude = data[31] % 33; | ||
secp256k1_fe_normalize(r); | ||
if (rand_magnitude == 0) { | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, the returned field element will not have magnitude 0. In fact, it'll be unspecified, making the code that uses it below have undefined behavior. For magnitude 0, you should set the output to just be the 0 field element.
src/fuzz.c
Outdated
if (size>=32) { | ||
secp256k1_fe a, zero; | ||
secp256k1_fe_set_b32_mod(r, data); | ||
int rand_magnitude = data[31] % 33; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's a good idea to reuse fuzz input bytes for two distinct meanings (here: magnitude and the lowest byte of the field element representation). It may also be better to let the caller decide the magnitude (or at least max magnitude), and then let the function construct it.
src/fuzz.c
Outdated
} | ||
secp256k1_fe_clear(&zero); | ||
secp256k1_fe_negate(&zero, &zero, 0); | ||
secp256k1_fe_mul_int_unchecked(&zero, rand_magnitude - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approach here does not reach all possible field element representations; only ones that can be represented as "normalized_number times integer in range 1 to 31".
I think it would be better if you'd read a full 40 bytes, and then use masking (using bitwise AND to set certain bits to zero) and/or rejection (just give up if the resulting value has bad magnitude) to obtain field elements of the desired magnitude.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your suggestions! I will modify this fuzz_field_construct function and other errors.
src/fuzz.c
Outdated
if (size >= 32) { | ||
secp256k1_fe a, negate; | ||
fuzz_field_construct(data, size, &a); | ||
if (a.magnitude >= 32) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is unnecessary; fuzz_field_construct
only constructs values with magnitude at most 32.
src/fuzz.c
Outdated
if (size >= 64) { | ||
secp256k1_fe a, b, r1, r2; | ||
fuzz_field_construct(data, size, &a); | ||
fuzz_field_construct(data + 32, size, &b); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The size
pass here is incorrect, because the array pointed to by data + 32
only has length size - 32
. See also my comment about responsibility of guaranteeing length on fuzz_field_construct
.
Thanks for the previous review. |
.vscode/settings.json
Outdated
@@ -0,0 +1,5 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't add this file, please. It's for your local setup, not everyone's.
if (r->normalized) { | ||
if ((r->n[4] == 0x0FFFFFFFFFFFFULL) && ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL)) { | ||
uint64_t mask3 = 0xFFFFEFFFFFC2FULL - 1; | ||
r->n[0] &= mask3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is inaccurate, as it will unconditionally wipe some bits in r->n[0]
even when unnecessary, making certain values unreachable. E.g. it'll turn r->n[0] = 0x1000003d1
into r->n[0] = 0
.
src/fuzz.c
Outdated
* If 'normalized' = 0, this function will construct normalized or non-normalized field elements depending on the fuzzer data. | ||
* On output, r will be a valid field element | ||
**/ | ||
static void fuzz_field_construct(const uint8_t *data, int normalized, secp256k1_fe *r) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the effectiveness of some of the tests below could be increased further by passing a max magnitude argument to fuzz_field_construct
, so that things like
fuzz_field_construct(data, 0, &a);
fuzz_field_construct(data + 42, 0, &b);
if (a.magnitude + b.magnitude <= 32) {
could become
fuzz_field_construct(data, 0, 32, &a);
fuzz_field_construct(data + 42, 0, 32 - a.magnitude, &b);
It'd also remove the need for fuzz_ge_fe_construct
.
src/fuzz.c
Outdated
} | ||
|
||
/* Test commutativity of addition on two field elements */ | ||
static void fuzz_field_add_commutativty(const uint8_t *data, size_t size) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: typo
src/fuzz.c
Outdated
if (a.magnitude <= 10) { | ||
int m = a.magnitude; | ||
r1 = a; | ||
secp256k1_fe_mul_int(&r1, 3); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit limiting to only test multiplication by 3.
src/fuzz.c
Outdated
fuzz_ge_fe_construct(data, 1, &x); | ||
if (secp256k1_ge_x_on_curve_var(&x)) { | ||
int odd = data[42] % 2; | ||
secp256k1_ge_set_xo_var(&ge, &x, odd); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if the y
coordinate could also be given a nontrivial normalization/magnitude here.
As a stretch, it could be interesting to (optionally) also construct the group element by reading the Y coordinate from the fuzz input, and then construct the corresponding X coordinate (it needs a cube root, but that's relatively simple for our field). I can give you some pointers if you're interested in that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the comments! Yes, I’m interested in constructing the group element by reading the Y coordinates from fuzzer data (find X from the elliptic curve equation).
In addition, I changed the fuzz_field_construct function (add another input argument to determine the max magnitude) and fixed the mask errors. Currently, I’m working on the fuzzing test about the function related to point multiplication operation, is this a good part to carry on, or do you have a better recommendation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll have a look over your changes soon. In the mean time, yes, having some fuzzing of EC multiplication would be good.
To compute cube roots modulo 2^256-2^32-977 (or field size), see this explanation here: https://github.com/bitcoin/bips/blob/cc177ab7bc5abcdcdf9c956ee88afd1052053328/bip-0324/reference.py#L165L191
field and group elements
This PR: Group element construct function: I found an interesting situation: when I run the fuzzing test ‘fuzz_field_equal’ for the ‘secp256k1_fe_equal(a, b)’ (the magnitude of a and b cannot exceed 1 and 31 respectively), the test failed when the magnitude of ‘b’ equals to 31. |
Thanks for the previous review.
This PR:
Added a function to construct valid field elements with random magnitude.
Added the constraint in each field operation test based on the magnitude and normalization.
Could you give me some suggestions?