Changes:
- removes unwanted fe_normalize_weakcalls to the second argument offe_equal
- removes fe_equal_var
_fe_equal_var, and unwanted _fe_normalize_weak calls (in tests)
    #1062
    
      
    
  63@@ -64,10 +64,13 @@ static int secp256k1_fe_is_zero(const secp256k1_fe *a);
64 /** Check the "oddness" of a field element. Requires the input to be normalized. */
65 static int secp256k1_fe_is_odd(const secp256k1_fe *a);
66 
67-/** Compare two field elements. Requires magnitude-1 inputs. */
68+/** Compare two field elements. Requires both the inputs to have magnitude = 1.
69+ *  i.e. a->magnitude = 1 and b->magnitude = 1 */
Just a nit but why not just
0/** Compare two field elements. Requires both the inputs to have magnitude 1. */
(Hm, in fact it’s <= 1 even but that should go away when  #1001 is solved).
I don’t think it’s necessary to phrase it twice (and x->magnitude is only there #ifdef VERIFY – but ok people would understands what is meant. ;))
Same below.
x->magnitude is used only in the VERIFY build.
              
            Concept ACK
I think then we should add (#ifdef VERIFY) magnitude checks to secp256k1_fe_equal_var and secp256k1_fe_equal.
If you’re up to it, I think the other field function could need an audit for similar checks.
In fe_sqrt:
https://github.com/bitcoin-core/secp256k1/blob/a1102b12196ea27f44d6201de4d25926a2ae9640/src/field_impl.h#L132-L135
Here, the secp256k1_fe_sqr function ensures that t1->magnitude = 1 but there is no guarantee for the magnitude of a to be one since it is passed into the function. Hence, some fe_sqrt tests are failing after adding magnitude checks inside fe_equal.
We could do secp256k1_fe_normalize_weak(a) but this changes the value of a and we don’t want that right? Alternatively, we could call fe_equal_var instead of fe_equal. Is it necessary for secp256k1_fe_sqrt to be of constant time?
@siv2r Regarding _fe_sqrt, there is an implicit magnitude restriction on a, since it is used as the input to _fe_mul and _fe_sqr at the top of the method. That magnitude restriction is <= 8. 8 is also a kind of global implicit limit for inputs where not otherwise stated (but we should definitely be working towards fully documenting and validating all such assumptions).
I think it might be better to just say that both inputs to the equals methods can be magnitude <= 8 too (indeed tied to the mul/sqr limit whatever it is or changes to). There’s no real obstacle to the implementation(s) (just change the third argument to _fe_negate to 8) and no performance hit AFAICT.
Is it necessary for
secp256k1_fe_sqrtto be of constant time?
It’s only used in a single var-time caller, so technically it could be changed to sqrt_var, but preferably not.
Apparently secp256k1_fe_equal and secp256k1_fe_equal_var are  identical up to the final call to normalizes_to_zero or normalizes_to_zero_var and so they both only require that a has magnitude 1. So we should just document that secp256k1_fe_equal only requires the magnitude of a to be 1.
Edited because I first thought secp256k1_fe_equal and secp256k1_fe_equal_var are exactly identical.
It’s only used in a single var-time caller
Oh, Okay. Then, creating a new function for secp256k1_fe_sqrt_var (while renaming the old one to _fe_sqrt_const) might be overkill.
we should just document that
secp256k1_fe_equalonly requires the magnitude ofato be 1.
Yes, I think this might be a better approach since it keeps the functionality of both fe_equal_var and fe_equal similar.
After this, I felt we needed to remove the _fe_normalize_weak calls before _fe_equal, but surprisingly _fe_equal is used rarely in the code. The only other place where it is used is:
https://github.com/bitcoin-core/secp256k1/blob/a1102b12196ea27f44d6201de4d25926a2ae9640/src/modules/extrakeys/tests_impl.h#L75-L77
Even here, the magnitude of y is two, so modifying the _fe_sqrt function (which I suggested) is a bad idea.
Hehe ok, and here it’s basically an oversight. This is just test code, so fe_equal_var could be used instead. I mean, not that it matters.
fe_equal_var is really a bit dubious. It only has a fast path when the inputs are not equal, and the only callers are for public key parsing and (ECDSA) verification, where that would mean an error condition and is probably uncommon. If the fast path is not hit, it actually costs a couple of cycles more. (Not to mention that the performance difference is tiny relative to those operations anyway).
        
      _fe_normalizes_to_zero_var expect a false result and so will almost always hit the fast path. Depending on the level of inlining, _fe_equal_var could be “spoiling” the branch prediction for that fast path check for the other callers.
        
      I also benchmarked the tests.c file (using time ./tests) since a lot of secp256k1_fe_verify calls were added in this PR. These are the results (on 64-bit, i7-8750H) for three iterations:
| branch | iter1 | iter2 | iter3 | 
|---|---|---|---|
| master | 2min 26.21sec | 2min 26.44sec | 2min 26.68sec | 
| this PR | 2min 26.99sec | 2min 26.91sec | 2min 26.83sec | 
fe_equal_var entirely and maybe document in fe_equal that this is intentional?
        
      @peterdettman Good points. Do you think we should remove
fe_equal_varentirely and maybe document infe_equalthat this is intentional?
Yeah, pretty much. Until there’s a performance-sensitive caller for whom a != b is the expected result, I don’t see any value in _fe_equal_var. If such a caller does turn up, then there should be a fast path for inequality that doesn’t even need the subtraction, so we’d rewrite it anyway.
@siv2r Do you plan to also remove the fe_equal_var in a further commit? Or should we do this in a separate PR?
(If you want to add it here, I think it’s really ok to add it on top of the existing commits, no need to rewrite the existing commits).
fe_equal_var in a new commit (this PR).
        
      fe_equal_var and documented the reason in fe_equalfe_equal_var calls with fe_equal63@@ -64,12 +64,12 @@ static int secp256k1_fe_is_zero(const secp256k1_fe *a);
64 /** Check the "oddness" of a field element. Requires the input to be normalized. */
65 static int secp256k1_fe_is_odd(const secp256k1_fe *a);
66 
67-/** Compare two field elements. Requires magnitude-1 inputs. */
68+/** Compare two field elements. Requires the first input to have magnitude equal to 1. 
69+ *  The variable time counterpart of this function `secp256k1_fe_equal_var` was removed 
70+ *  since it hits fast path only when the inputs are unequal. This unequal inputs 
240@@ -241,7 +241,7 @@ static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a)
241     secp256k1_fe r, r2;
242     VERIFY_CHECK(!a->infinity);
243     secp256k1_fe_sqr(&r, &a->z); secp256k1_fe_mul(&r, &r, x);
244-    r2 = a->x; secp256k1_fe_normalize_weak(&r2);
245+    r2 = a->x;
r2 variable isn’t needed anymore; you can just call return secp256k1_fe_equal_var(&r, &a->x); directly.
              
            No worries, I will rebase this PR.
Ok, merging #1066 took a while…. This PR here needs rebase now. :)
2966@@ -2967,7 +2967,6 @@ static int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
2967     secp256k1_fe an = *a;
2968     secp256k1_fe bn = *b;
2969     secp256k1_fe_normalize_weak(&an);
2970-    secp256k1_fe_normalize_var(&bn);
Not sure why normalize_var was used on bn. I assumed it intended to set bn’s magnitude to 1 for the fe_equal call. So, I removed it. Please let me know if that’s not the case, and I’ll revert this.
All test cases passed with this change.
Nice. :)
#1066 took care of all the magnitude and
fe_verifychecks so, this PR currently does:
Can you update the PR title and the initial comment, so that they reflect the current status?
Needs rebase again, sorry, but should be trivial. :)
Thanks for updating the PR title. Can you also update the initial post/PR description (e.g., replace the contents with #1062 (comment) )? It’s just cosmetic, but the text of will be added to the merge commit, so it makes sense to keep this updated.
It is not neccessary for the second argument in `secp256k1_fe_equal_var`
(or `secp256k1_fe_equal`) to have magnitude = 1.
Hence, removed the `secp256k1_fe_normalize_weak` call for those argument.
`fe_equal_var` hits a fast path only when the inputs are unequal, which is
uncommon among its callers (public key parsing, ECDSA verify).
Until there’s a performance-sensitive caller for whom a != b is the expected result, I don’t see any value in _fe_equal_var.
I don’t think we know what the expected result of the caller is. The difference seems to be negligible though.
ACK 54058d16feaa431520029335e2d56252859d3260