:sigh: I forgot the attachment with my test vectors. Here it is.

On Tue, Jun 28, 2016 at 10:43 AM, Brian Smith <br...@briansmith.org> wrote:
> When doing math on short Weierstrass curves like P-256, we have to
> special case points at infinity. In Jacobian coordinates (X, Y, Z),
> points at infinity have Z == 0. However, instead of checking for Z ==
> 0, p256-x86-64 instead checks for (X, Y) == (0, 0). In other words, it
> does, in some sense, the opposite of what I expect it to do.
>
> I have built a testing framework for exploring things like this in
> *ring*. I will attach the input file for my tests which show that
> ecp_nistz256_point_add seems to fail to recognize the point at
> infinity correctly. However, it is also possible that I just don't
> understand how ecp_nistz256 intends to work. My questions are:
>
> 1. With respect to additions of the form (a + infinity == a) and
> (infinity + b == b), is the code in ecp_nistz256_point_add and
> ecp_nistz256_point_add_affine correct?
>
> 2. if it is correct, could we add more explanation as to why it is correct?
>
> 3. Given the specifics of the implementation of the ecp_nistz256
> implementation, is it even possible for us to encounter the point at
> infinity as one of the parameters to ecp_nistz256_point_add, other
> than in the very final addition that adds g_scalar*G + p_scalar*P? See
> Section 4.1 of [1].
>
> Background: For based point (G) multiplication, the code has a large
> table of multiples of G, in affine (not Jacobian) coordinates. The
> point at infinity cannot be encoded in affine coordinates. The authors
> instead decided to encode the point at infinity as (0, 0), since the
> affine point (0, 0) isn't on the P-256 curve. It isn't clear why the
> authors chose to do that though, since the point at infinity doesn't
> (can't, logically) appear in the table of precomputed multiples of G
> anyway. Regardless, if you represent the point at infinity as (0, 0)
> then it makes sense to check (x, y) == (0, 0).
>
> But, it seems like the functions that do the computations, like
> ecp_nistz256_point_add and ecp_nistz256_point_add_affine, output the
> point at infinity as (_, _, 0), not necessarily (0, 0, _). Also,
> ecp_nistz256's EC_METHOD uses ec_GFp_simple_is_at_infinity and
> ec_GFp_simple_point_set_to_infinity, which represent the point at
> infinity with z == 0, not (x, y) == 0. Further ecp_nistz256_get_affine
> uses EC_POINT_is_at_infinity, which checks z == 0, not (x, y) == 0.
> This inconsistency is confusing, if not wrong. Given this, it seems
> like the point-at-infinity checks in the ecp_nistz256_point_add and
> ecp_nistz256_point_add_affine code should also be checking that z == 0
> instead of (x, y) == (0, 0).
>
> Note that this is confusing because `EC_POINT_new` followed by
> `EC_POINT_to_infinity` initializes (X, Y, Z) = (0, 0, 0). Thus, the
> check of (x, y) == (0, 0) "works" as well as the check z == 0. But, it
> doesn't work in real-life cases where the point is infinity is
> encountered during calculations, because we'll have (X, Y) != (0, 0)
> but Z == 0.
>
> The assembly language code that does this check is hard to understand
> unless one is familiar with SIMD. However, the C reference
> implementation that the assembly language code used as a model is easy
> to understand. This code can be found in the ecp_nistz256.c file.
>
> Note the parameters of ecp_nistz256_point_add are P256_POINT, not
> P256_POINT_AFFINE, so "representation of the point at infinity as (0,
> 0)" doesn't make sense to me. But, that's exactly what it checks.
>
> In ecp_nistz256_point_add_affine, it makes more sense to me, because
> parameter |b| is in fact a |P256_POINT_AFFINE|. However, |a| is not a
> |P256_POINT_AFFINE|, so the (x, y) == (0, 0) check doesn't make sense
> to me. The x86-64 and x86 assembly language code seems to emulate this
> exactly. I didn't test the ARM code, but I'd guess it is similar.
>
> [1] https://eprint.iacr.org/2014/130.pdf (Section 4.1)
>
> Here's the specific logic I'm talking about (which is also present in
> the asm code):
>
> ```
> static void ecp_nistz256_point_add(P256_POINT *r,
>                                    const P256_POINT *a, const P256_POINT *b) {
>     [...]
>
>     const BN_ULONG *in1_x = a->X;
>     const BN_ULONG *in1_y = a->Y;
>     const BN_ULONG *in1_z = a->Z;
>
>     const BN_ULONG *in2_x = b->X;
>     const BN_ULONG *in2_y = b->Y;
>     const BN_ULONG *in2_z = b->Z;
>
>     /* We encode infinity as (0,0), which is not on the curve,
>      * so it is OK. */
>     in1infty = (in1_x[0] | in1_x[1] | in1_x[2] | in1_x[3] |
>                 in1_y[0] | in1_y[1] | in1_y[2] | in1_y[3]);
>     if (P256_LIMBS == 8)
>         in1infty |= (in1_x[4] | in1_x[5] | in1_x[6] | in1_x[7] |
>                      in1_y[4] | in1_y[5] | in1_y[6] | in1_y[7]);
>
>     in2infty = (in2_x[0] | in2_x[1] | in2_x[2] | in2_x[3] |
>                 in2_y[0] | in2_y[1] | in2_y[2] | in2_y[3]);
>     if (P256_LIMBS == 8)
>         in2infty |= (in2_x[4] | in2_x[5] | in2_x[6] | in2_x[7] |
>                      in2_y[4] | in2_y[5] | in2_y[6] | in2_y[7]);
>
>     [...]
> }
>
> static void ecp_nistz256_point_add_affine(P256_POINT *r,
>                                           const P256_POINT *a,
>                                           const P256_POINT_AFFINE *b) {
>     [...]
>
>     const BN_ULONG *in1_x = a->X;
>     const BN_ULONG *in1_y = a->Y;
>     const BN_ULONG *in1_z = a->Z;
>
>     const BN_ULONG *in2_x = b->X;
>     const BN_ULONG *in2_y = b->Y;
>
>     /*
>      * In affine representation we encode infty as (0,0), which is not on the
>      * curve, so it is OK
>      */
>     in1infty = (in1_x[0] | in1_x[1] | in1_x[2] | in1_x[3] |
>                 in1_y[0] | in1_y[1] | in1_y[2] | in1_y[3]);
>     if (P256_LIMBS == 8)
>         in1infty |= (in1_x[4] | in1_x[5] | in1_x[6] | in1_x[7] |
>                      in1_y[4] | in1_y[5] | in1_y[6] | in1_y[7]);
>
>     in2infty = (in2_x[0] | in2_x[1] | in2_x[2] | in2_x[3] |
>                 in2_y[0] | in2_y[1] | in2_y[2] | in2_y[3]);
>     if (P256_LIMBS == 8)
>         in2infty |= (in2_x[4] | in2_x[5] | in2_x[6] | in2_x[7] |
>                      in2_y[4] | in2_y[5] | in2_y[6] | in2_y[7]);
>
>     in1infty = is_zero(in1infty);
>     in2infty = is_zero(in2infty);
>
>     [...]
> }
> ```
>
> Cheers,
> Brian
> --
> https://briansmith.org/



-- 
https://briansmith.org/
# Some test vectors for a + b == r for the P-256 curve that demonstrate the
# incorrect point-on-the-curve check.
#
# All test vectors' elements are in the Montgomery domain. Also, the result |r|
# is normalized to Z == to_mont(1).

# G + inf == G. The code handles this correctly, but only accidentally because
# all three of (X, Y, Z) are zero.
Curve = P-256
a = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c, 
8571ff1825885d85d2e88688dd21f3258b4ab8e4ba19e45cddf25357ce95560a, 
00000000fffffffeffffffffffffffffffffffff000000000000000000000001
b = 0000000000000000000000000000000000000000000000000000000000000000, 
0000000000000000000000000000000000000000000000000000000000000000, 
0000000000000000000000000000000000000000000000000000000000000000
r = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c, 
8571ff1825885d85d2e88688dd21f3258b4ab8e4ba19e45cddf25357ce95560a, 
00000000fffffffeffffffffffffffffffffffff000000000000000000000001

# G + the point (0, 0, Junk), which is not on the curve as affine point (0, 0)
# is not on the curve. The code thinks this is at infinity because
# (X, Y) == (0, 0), but it's not at infinity, because Z != 0. The value of |r|
# here is what the code actually calculates (the value of G). There is no right
# answer for this input because b isn't even on the curve.
Curve = P-256
a = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c, 
8571ff1825885d85d2e88688dd21f3258b4ab8e4ba19e45cddf25357ce95560a, 
00000000fffffffeffffffffffffffffffffffff000000000000000000000001
b = 0000000000000000000000000000000000000000000000000000000000000000, 
0000000000000000000000000000000000000000000000000000000000000000, 
6d333da42e30f7011245b6281015ded14e0f100968e758a1b6c3c083afc14ea0
r = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c, 
8571ff1825885d85d2e88688dd21f3258b4ab8e4ba19e45cddf25357ce95560a, 
00000000fffffffeffffffffffffffffffffffff000000000000000000000001

# G + (n*G) == G. Note that n*G results in the point at infinity (Z == 0), but
# with (X, Y) != (0, 0). The code calculates the wrong result. Here |r| is what
# the code should calculate (the value of G).
Curve = P-256
a = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c, 
8571ff1825885d85d2e88688dd21f3258b4ab8e4ba19e45cddf25357ce95560a, 
00000000fffffffeffffffffffffffffffffffff000000000000000000000001
b = 2b11cb945c8cf152ffa4c9c2b1c965b019b35d0b7626919ef0ae6cb9d232f8af, 
6d333da42e30f7011245b6281015ded14e0f100968e758a1b6c3c083afc14ea0, 
0000000000000000000000000000000000000000000000000000000000000000
r = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c, 
8571ff1825885d85d2e88688dd21f3258b4ab8e4ba19e45cddf25357ce95560a, 
00000000fffffffeffffffffffffffffffffffff000000000000000000000001
-- 
openssl-dev mailing list
To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-dev

Reply via email to