herumi / mcl

a portable and fast pairing-based cryptography library
BSD 3-Clause "New" or "Revised" License
450 stars 152 forks source link

Cannot get the multiplication right #78

Closed tkstanczak closed 4 years ago

tkstanczak commented 4 years ago

So I get the pairing and addition fine but multiplication is always failing to be consistent with home build solution:

    /// <summary>
    /// P is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1.
    /// </summary>
    public static readonly BigInteger P = BigInteger.Parse("21888242871839275222246405745257275088696311157297823662689037894645226208583");

    /// <summary>
    /// Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1.
    /// R order of the BN128G2 cyclic subgroup
    /// </summary>
    public static readonly BigInteger R = BigInteger.Parse("21888242871839275222246405745257275088548364400416034343698204186575808495617");

I feel like I tried various combinations but the numbers are just different. The reason I am trying is that I need to be able to verify if x,y pair is on the curve (without setStr and getStr that hurt performance) The only serialization format supported is compressed so I cannot really feed x and y in little endian binary form and need to run the verification myself.

        public static bool IsOnCurve(UInt256 x, UInt256 y)
        {
            BigInteger r = BigInteger.Parse("2523648240000001ba344d8000000007ff9f800000000010a10000000000000d", NumberStyles.HexNumber);
            BigInteger p = BigInteger.Parse("2523648240000001ba344d80000000086121000000000013a700000000000013", NumberStyles.HexNumber);
            // return true;
            // no idea why below never works

            if (x.IsZero && y.IsZero)
            {
                return true;
            }

            Span<byte> bytesX = stackalloc byte[32];
            x.ToLittleEndian(bytesX);
            Fr xFr = new Fr();
            xFr.Deserialize(bytesX, bytesX.Length);

            Fr xFp = new Fr();
            xFp.DeserializeFp(bytesX, bytesX.Length);

            Span<byte> bytesY = stackalloc byte[32];
            y.ToLittleEndian(bytesY);
            Fr yFr = new Fr();
            yFr.Deserialize(bytesY, bytesY.Length);

            Fr yFp = new Fr();
            yFp.DeserializeFp(bytesY, bytesY.Length);

            // y^2 = x^3 + 2
            //
            Fr left = new Fr();
            left.Sqr(yFr);

            Fr leftAlt = new Fr();
            leftAlt.Mul(yFr, yFr);

            Fr resAlt = MulAlternative(xFr, y);

            Fr leftAltFp = new Fr();
            leftAltFp.MulFp(yFp, yFp);

            Fr leftAltFrFp = new Fr();
            leftAltFrFp.MulFp(yFr, yFr);

            Fr leftAltFpFR = new Fr();
            leftAltFpFR.Mul(yFp, yFp);

            Fr leftOp = yFp * yFp;
            Fr leftOp2 = yFr * yFr;
            Fr leftOp3 = yFr * yFp;
            Fr leftOp4 = yFp * yFr;

            Fr two = new Fr();
            two.SetInt(3);
            //
            Fr right = new Fr();
            right.Sqr(xFr);
            right.Mul(right, xFr);
            right.Add(right, two);

            Fr rightAlt = new Fr();
            rightAlt.Mul(xFr, xFr);
            rightAlt.Mul(rightAlt, xFr);

            Fr rightAltFp = new Fr();
            rightAltFp.MulFp(xFp, xFp);
            rightAltFp.MulFp(rightAltFp, xFp);

            return left.Equals(right);
            // return true;
        }
tkstanczak commented 4 years ago

image

tkstanczak commented 4 years ago

For example the multiplication done in a very crude way at the bottomw works fine and passes all tests. But using mclbn256 Mul call on G1 * Fr gives a totally unexpected result:

image

tkstanczak commented 4 years ago

OK, found all the problems with this: I needed to use the right r and p / use SetLittleEndianMod instead of deserialize.