I just migrated CodesInChaos' C# port of Ed25519(ref10) to Java, and everything works fine. (I.e. I get the same results for key generation, signature and verification.)
Now, I would like to do a Diffie-Hellman key exchange directly on Ed25519. Therefore I am examining the functions for point addition, doubling and multiplication.
After several tests it appears that doubling a point
dbl(P)
provides a different result than adding the same point to itself
add(P,P).
Even when I use different points for the addition, the results are still different. For example: dbl(dbl(P)) is also different from add(add(dbl(P),P),P).
Is this normal? (I am not familiar with ECC, and I had expected that these functions would return the same values.)
UPDATE
There was a bug in my code which is now corrected thanks to CodesInChaos.
Here is my test code for C#:
using System;
using Chaos.NaCl.Internal.Ed25519Ref10;
namespace Chaos.NaCl
{
public static class RunTest
{
static void Main ()
{
testAddition ();
}
public static void testAddition ()
{
Console.WriteLine ("============================= testAddition ");
byte[] l_Seed = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
byte[] l_PK = new byte[32];
byte[] l_SK = new byte[64];
Ed25519Operations.crypto_sign_keypair (l_PK, 0, l_SK, 0, l_Seed, 0);
byte[] l_A = add (l_PK, l_PK);
byte[] l_B = dbl (l_PK);
Console.WriteLine (CryptoBytes.ToHexStringUpper (l_A));
Console.WriteLine (CryptoBytes.ToHexStringUpper (l_B));
// Output Java and C#:
// 382CE0B6971265E859F317BBCD18F6ADF4517102055DEFA3ED7C4EBFD2C0D655
// 382CE0B6971265E859F317BBCD18F6ADF4517102055DEFA3ED7C4EBFD2C0D655
// which is OK
// LETS DO IT AGAIN:
byte[] l_C = add (l_B, l_B);
byte[] l_D = dbl (l_B);
Console.WriteLine (CryptoBytes.ToHexStringUpper (l_C));
Console.WriteLine (CryptoBytes.ToHexStringUpper (l_D));
// Output Java port:
// 40838B988FB4A3809EBEAA600604EAB4A39A75BD86509A73C40B5A2820BEB94E
// 90A0F26E495C4A73BCE8BE36B361FF84F8CA8E19E15B9F623AC538E5F9646B63
// which is wrong ?
// Output C# (Mono):
// 0000000000000000000000000000000000000000000000000000000000000040
// 0000000000000000000000000000000000000000000000000000000000000040
// which is also wrong ?? :)
}
static byte[] dbl (byte[] p_Element)
{
byte[] l_Result = new byte[32];
GroupElementP3 l_P3;
GroupOperations.ge_frombytes_negate_vartime(out l_P3, p_Element, 0);
GroupElementP1P1 l_P1P1;
GroupOperations.ge_p3_dbl(out l_P1P1,ref l_P3);
GroupElementP3 l_P3again;
GroupOperations.ge_p1p1_to_p3(out l_P3again,ref l_P1P1);
GroupOperations.ge_p3_tobytes(l_Result,0,ref l_P3again);
// EVIL MISTAKE: ScalarOperations.sc_clamp(l_Result, 0);
return l_Result;
}
static byte[] add (byte[] p_ElementA, byte[]p_ElementB)
{
byte[] l_Result = new byte[32];
GroupElementP3 l_A;
GroupOperations.ge_frombytes_negate_vartime(out l_A, p_ElementA, 0);
GroupElementP3 l_B;
GroupOperations.ge_frombytes_negate_vartime(out l_B, p_ElementB, 0);
GroupElementCached l_Cached;
GroupOperations.ge_p3_to_cached(out l_Cached,ref l_A);
GroupElementP1P1 l_P1P1;
GroupOperations.ge_add(out l_P1P1, ref l_B, ref l_Cached);
GroupElementP3 l_P3again;
GroupOperations.ge_p1p1_to_p3(out l_P3again, ref l_P1P1);
GroupOperations.ge_p3_tobytes(l_Result, 0, ref l_P3again);
// EVIL MISTAKE: ScalarOperations.sc_clamp (l_Result, 0);
return l_Result;
}
}
}
sc_clamp
makes no sense on encoded points. It operations on scalars. $\endgroup$