From 2e330f002aa7e395ba5f51fe607b261bbc47cc83 Mon Sep 17 00:00:00 2001 From: Alexey Ermishkin Date: Mon, 11 May 2015 12:45:23 +0500 Subject: [PATCH 1/7] Elligator implementation --- Chaos.NaCl.Tests/Ed25519Tests.cs | 297 +++++++++++++++++ Chaos.NaCl/Chaos.NaCl.csproj | 1 + .../Internal/Ed25519Ref10/fe_bytesLE.cs | 21 ++ Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs | 302 +++++++++++++++++- Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs | 24 +- 5 files changed, 639 insertions(+), 6 deletions(-) create mode 100644 Chaos.NaCl/Internal/Ed25519Ref10/fe_bytesLE.cs diff --git a/Chaos.NaCl.Tests/Ed25519Tests.cs b/Chaos.NaCl.Tests/Ed25519Tests.cs index 5e77d99..767c7f0 100644 --- a/Chaos.NaCl.Tests/Ed25519Tests.cs +++ b/Chaos.NaCl.Tests/Ed25519Tests.cs @@ -2,13 +2,252 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Security.Cryptography; using Microsoft.VisualStudio.TestTools.UnitTesting; +using static Chaos.NaCl.Internal.Ed25519Ref10.Ed25519Operations; namespace Chaos.NaCl.Tests { + + internal struct TestVectors + { + public bool valid; + public byte[] pub; + public byte[] repr; + public byte[] priv; + }; + + [TestClass] public class Ed25519Tests { + private TestVectors[] vectorz = new[] + { + new TestVectors() + { + valid = true, + pub = + new byte[] + { + 0x4b, 0x97, 0x97, 0xd0, 0x98, 0x5a, 0xf7, 0x42, 0x9b, 0xc3, 0x55, 0xd9, 0x4, 0x81, 0xcf, 0xc7, 0xcc, + 0x14, 0x54, 0x5c, 0xa5, 0xe6, 0x7c, 0x84, 0xcb, 0x1b, 0x4a, 0x4c, 0x4d, 0xa1, 0xda, 0x33, + }, + repr = + new byte[] + { + 0xbc, 0x13, 0x1a, 0x67, 0xb, 0x92, 0x2, 0x65, 0x8f, 0x2f, 0x79, 0xa, 0x7e, 0x4, 0x71, 0xd0, 0xe, + 0x67, 0x90, 0xdb, 0x4d, 0x59, 0x8, 0xd2, 0x54, 0x4e, 0x5f, 0xbb, 0x8d, 0xa, 0x89, 0x78, + }, + priv = + new byte[] + { + 0x10, 0x3d, 0xba, 0xb8, 0x6f, 0x99, 0xee, 0xdb, 0xec, 0xa, 0xd6, 0x8f, 0xa9, 0x20, 0x3d, 0x5f, 0xd4, + 0xf5, 0xe0, 0xdc, 0x48, 0xbc, 0xaf, 0x6c, 0x98, 0x50, 0xd0, 0x1a, 0x12, 0x9f, 0x28, 0x5c, + } + }, + new TestVectors() + { + valid = true, + pub = + new byte[] + { + 0xf1, 0x8, 0x6d, 0x75, 0xb3, 0x59, 0x5c, 0xe7, 0x3c, 0x41, 0xa8, 0x11, 0xbf, 0x1a, 0x10, 0xb1, 0xba, + 0x1a, 0x75, 0xe4, 0xff, 0xd4, 0x98, 0x6c, 0x37, 0x98, 0xe3, 0x54, 0xf3, 0x24, 0x6b, 0x50, + }, + repr = + new byte[] + { + 0x6, 0x13, 0x6a, 0x4f, 0x20, 0x93, 0x37, 0x12, 0x4f, 0x4d, 0x92, 0x31, 0xc8, 0x34, 0xe9, 0xa0, 0x94, + 0x8f, 0x89, 0x6d, 0xc9, 0x1c, 0x85, 0x5b, 0x32, 0x80, 0xd3, 0xd1, 0x4f, 0x42, 0xe8, 0x4e, + }, + priv = + new byte[] + { + 0x68, 0x4f, 0x96, 0xdf, 0xde, 0xa0, 0x57, 0xf5, 0xb2, 0x3f, 0xf6, 0x29, 0x52, 0xb3, 0x34, 0x95, + 0xb0, 0x7b, 0xa3, 0xd5, 0x4, 0xac, 0x79, 0x1d, 0xf, 0x3c, 0x87, 0x52, 0x3a, 0xa7, 0x3f, 0x6d, + } + }, + new TestVectors() + { + valid = true, + pub = + new byte[] + { + 0x49, 0x76, 0xe, 0x1, 0x60, 0x24, 0x44, 0x48, 0x48, 0xc7, 0x9d, 0xc1, 0x81, 0x4, 0x6d, 0xc, 0x3a, + 0x48, 0x8e, 0xf8, 0x67, 0xbd, 0xf9, 0xd1, 0x6f, 0x8c, 0x8f, 0xe4, 0x9b, 0x7b, 0x7f, 0x66, + }, + repr = + new byte[] + { + 0xb7, 0xdd, 0x0, 0x28, 0x3, 0xe0, 0x9f, 0x93, 0x52, 0x5d, 0xf6, 0x49, 0xa3, 0x9, 0xbf, 0x29, 0x16, + 0x71, 0xfd, 0x82, 0x52, 0x23, 0xf2, 0x96, 0x2, 0xee, 0x97, 0x20, 0xc1, 0xd7, 0xa6, 0x3b, + }, + priv = + new byte[] + { + 0x8, 0x69, 0x14, 0xc7, 0xc7, 0xe8, 0x33, 0x79, 0x27, 0xf4, 0xa7, 0x1c, 0xda, 0x21, 0x25, 0x41, 0x9a, + 0xe7, 0xe1, 0x83, 0x90, 0x52, 0x1f, 0xaf, 0x14, 0x3d, 0x5a, 0x2, 0xbc, 0x2e, 0x9c, 0x55, + } + }, + new TestVectors() + { + valid = false, + pub = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + repr = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + priv = + new byte[] + { + 0x78, 0x51, 0x2b, 0xcf, 0xe1, 0x62, 0x1d, 0x53, 0xed, 0xfc, 0xd5, 0xc0, 0x96, 0xc8, 0xb9, 0x96, + 0x8e, 0x3, 0x7, 0x5e, 0xc3, 0x3a, 0x2e, 0xea, 0xaa, 0x44, 0x96, 0x64, 0x71, 0xfc, 0x98, 0x65, + } + }, + new TestVectors() + { + valid = false, + pub = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + repr = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + priv = + new byte[] + { + 0x38, 0xa9, 0xb4, 0x8a, 0xe8, 0x8b, 0xad, 0xbe, 0x15, 0xd9, 0xbe, 0x6b, 0x1d, 0xf8, 0x82, 0xa1, + 0x89, 0x60, 0xd1, 0x14, 0xb6, 0x74, 0xba, 0xe8, 0x3f, 0xb6, 0xac, 0xd4, 0x5d, 0x94, 0x1, 0x68, + } + }, + new TestVectors() + { + valid = true, + pub = + new byte[] + { + 0x1e, 0x92, 0x62, 0xd, 0xc3, 0x9f, 0xf1, 0x28, 0x86, 0x40, 0x80, 0xb1, 0xfd, 0x37, 0xb9, 0x91, 0xac, + 0xcc, 0xbf, 0x3d, 0x2e, 0x48, 0x67, 0xd2, 0xed, 0xf1, 0x75, 0xa6, 0x58, 0x10, 0x6d, 0x55, + }, + repr = + new byte[] + { + 0x5a, 0x25, 0x9d, 0x71, 0x1f, 0xec, 0xb2, 0x6d, 0xe7, 0x8, 0x4f, 0x8d, 0x80, 0x15, 0x4e, 0xec, 0x8c, + 0xa3, 0xde, 0xd5, 0xde, 0x3f, 0xb6, 0x2f, 0x38, 0xc8, 0x6b, 0xf5, 0xf6, 0x84, 0x6e, 0x26, + }, + priv = + new byte[] + { + 0x28, 0x4f, 0x7, 0xcf, 0x45, 0xc0, 0x56, 0x74, 0xc6, 0xa7, 0xce, 0xa4, 0x8e, 0xf1, 0x83, 0xb7, 0xb5, + 0x22, 0x3c, 0xff, 0xe9, 0x2e, 0xa7, 0xcb, 0x78, 0xa2, 0x3, 0x1a, 0x47, 0x54, 0xc, 0x6d, + } + }, + new TestVectors() + { + valid = false, + pub = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + repr = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + priv = + new byte[] + { + 0x10, 0x22, 0xe3, 0x43, 0x94, 0x95, 0xd7, 0xd9, 0x0, 0xf5, 0xf5, 0xac, 0xd, 0x39, 0x6, 0x48, 0x86, + 0x54, 0x91, 0xe2, 0x88, 0xe5, 0xc2, 0xe6, 0x53, 0x5f, 0x10, 0xd8, 0x3e, 0xd0, 0xe, 0x7f, + } + }, + new TestVectors() + { + valid = false, + pub = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + repr = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + priv = + new byte[] + { + 0xa8, 0x30, 0x33, 0xe7, 0x55, 0x18, 0xae, 0x32, 0xb2, 0xd4, 0xdd, 0xb7, 0x76, 0x9a, 0x73, 0xd7, + 0x72, 0x8d, 0x5f, 0xd8, 0xd8, 0xe9, 0x57, 0x8a, 0xa9, 0x8e, 0xb6, 0x12, 0x7b, 0x3e, 0x8d, 0x6e, + } + }, + new TestVectors() + { + valid = true, + pub = + new byte[] + { + 0x64, 0x27, 0xe0, 0x5, 0x13, 0xab, 0x7a, 0x81, 0x46, 0xd5, 0x8e, 0xbc, 0x28, 0x25, 0xf4, 0x66, 0xe3, + 0x1c, 0x12, 0xbf, 0x97, 0x25, 0x99, 0x20, 0x37, 0x27, 0xd6, 0x1e, 0x9b, 0x6a, 0x6e, 0x7d, + }, + repr = + new byte[] + { + 0x5f, 0x9e, 0x2, 0x23, 0x7c, 0xa4, 0xfc, 0xc2, 0xc1, 0x8c, 0xc8, 0x91, 0x53, 0xdb, 0xa7, 0x5c, 0xca, + 0x58, 0xea, 0x12, 0x60, 0x41, 0x3f, 0x36, 0xe, 0xe7, 0x7d, 0x78, 0x1d, 0x72, 0xa9, 0x33, + }, + priv = + new byte[] + { + 0x80, 0x5a, 0x8f, 0xba, 0x73, 0x45, 0x30, 0xc8, 0xf0, 0xb7, 0x64, 0x6a, 0xae, 0x73, 0x6f, 0x54, + 0x65, 0x22, 0x5, 0xe8, 0x7, 0xd6, 0xab, 0x83, 0xc2, 0xe6, 0x79, 0x5e, 0x9a, 0x73, 0x22, 0x78, + } + }, + new TestVectors() + { + valid = false, + pub = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + repr = + new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + priv = + new byte[] + { + 0xc8, 0x25, 0x50, 0x29, 0xcd, 0x16, 0x69, 0xa9, 0x5c, 0x37, 0xab, 0xe7, 0xf5, 0x51, 0x9e, 0xaa, + 0xbe, 0x7e, 0x8d, 0x51, 0x27, 0x4c, 0xd4, 0x46, 0x14, 0xb, 0xf9, 0x50, 0x80, 0x4, 0x14, 0x6e, + } + } + }; + + + + [AssemblyInitializeAttribute] public static void LoadTestVectors(TestContext context) { @@ -20,6 +259,64 @@ public static void LoadTestVectors(TestContext context) Ed25519.Verify(sig, new byte[10], pk); } + + + [TestMethod] + public void ElligatorTest() + { + var rnd = new RNGCryptoServiceProvider(); + for (var i = 0; i< 100000; i++) + { + var publicKey1= new byte[32]; + var publicKey2= new byte[32]; + var publicKey3 = new byte[32]; + var privateKey = new byte[32]; + var rep = new byte[32]; + + rnd.GetBytes(privateKey); + + if (Elligator(publicKey1, rep, privateKey)) + { + RepresentativeToPublicKey(rep, publicKey2); + crypto_ecdh_keypair(publicKey3, privateKey); + TestHelpers.AssertEqualBytes(publicKey2, publicKey3); + TestHelpers.AssertEqualBytes(publicKey2, publicKey1); + } + + } + + + + var representative1 = new byte[32]; + var decodedPubKey1 = new byte[32]; + var publicKeyT1 = new byte[32]; + var res1 = Elligator(publicKeyT1, representative1, vectorz[0].priv); + + + var representative2 = new byte[32]; + var decodedPubKey2 = new byte[32]; + var publicKeyT2 = new byte[32]; + var res2 = Elligator(publicKeyT2, representative2, vectorz[1].priv); + + var k1 = MontgomeryCurve25519.KeyExchange(publicKeyT1, vectorz[1].priv); + var k2 = MontgomeryCurve25519.KeyExchange(publicKeyT2, vectorz[0].priv); + + + foreach (var vector in vectorz) + { + var representative = new byte[32]; + var decodedPubKey = new byte[32]; + var publicKeyT = new byte[32]; + var res = Elligator(publicKeyT, representative, vector.priv); + Assert.AreEqual(res, vector.valid); + if (res) + { + RepresentativeToPublicKey(representative, decodedPubKey); + TestHelpers.AssertEqualBytes(decodedPubKey, vector.pub); + } + } + } + [TestMethod] public void KeyPairFromSeed() { diff --git a/Chaos.NaCl/Chaos.NaCl.csproj b/Chaos.NaCl/Chaos.NaCl.csproj index 5c67ef1..b0afaea 100644 --- a/Chaos.NaCl/Chaos.NaCl.csproj +++ b/Chaos.NaCl/Chaos.NaCl.csproj @@ -42,6 +42,7 @@ + diff --git a/Chaos.NaCl/Internal/Ed25519Ref10/fe_bytesLE.cs b/Chaos.NaCl/Internal/Ed25519Ref10/fe_bytesLE.cs new file mode 100644 index 0000000..52ee402 --- /dev/null +++ b/Chaos.NaCl/Internal/Ed25519Ref10/fe_bytesLE.cs @@ -0,0 +1,21 @@ +using System; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + public static int fe_bytesLE(ref byte[] a, ref byte[] b) + { + var equalSoFar = -1; + var greater = 0; + for (var i = 31; i > 0; i--) + { + var x = a[i]; + var y = b[i]; + greater = (~equalSoFar & greater) | (equalSoFar & ((x - y) >> 31)); + equalSoFar = equalSoFar & (((x ^ y) - 1) >> 31); + } + return ~equalSoFar & 1 & greater; + } + } +} \ No newline at end of file diff --git a/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs b/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs index bbccc1a..aa9d372 100644 --- a/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs +++ b/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs @@ -1,5 +1,10 @@ using System; +using static Chaos.NaCl.Internal.Ed25519Ref10.FieldOperations; +using static Chaos.NaCl.Internal.Ed25519Ref10.GroupOperations; +using static Chaos.NaCl.Internal.Ed25519Ref10.LookupTables; +using static Chaos.NaCl.MontgomeryCurve25519; + namespace Chaos.NaCl.Internal.Ed25519Ref10 { internal static partial class Ed25519Operations @@ -13,11 +18,304 @@ public static void crypto_sign_keypair(byte[] pk, int pkoffset, byte[] sk, int s byte[] h = Sha512.Hash(sk, skoffset, 32);//ToDo: Remove alloc ScalarOperations.sc_clamp(h, 0); - GroupOperations.ge_scalarmult_base(out A, h, 0); - GroupOperations.ge_p3_tobytes(pk, pkoffset, ref A); + ge_scalarmult_base(out A, h, 0); + ge_p3_tobytes(pk, pkoffset, ref A); for (i = 0; i < 32; ++i) sk[skoffset + 32 + i] = pk[pkoffset + i]; CryptoBytes.Wipe(h); } + + + public static void crypto_ecdh_keypair(byte[] publicKey, byte[] privateKey) + { + ScalarOperations.sc_clamp(privateKey, 0); + + GroupElementP3 A; + ge_scalarmult_base(out A, privateKey, 0); + FieldElement publicKeyFE; + EdwardsToMontgomeryX(out publicKeyFE, ref A.Y, ref A.Z); + fe_tobytes(publicKey, 0, ref publicKeyFE); + } + + + public static bool Elligator(byte[] publicKey, byte[] representative, byte[] privateKey) + { + GroupElementP3 AA; + int i; + + //byte[] h = Sha512.Hash(privateKey, 0, 32);//ToDo: Remove alloc + ScalarOperations.sc_clamp(privateKey, 0); + + ge_scalarmult_base(out AA, privateKey, 0); + + FieldElement inv1; + fe_sub(out inv1, ref AA.Z, ref AA.Y); /* edwards25519.FeSub(&inv1, &A.Z, &A.Y) */ + fe_mul(out inv1, ref inv1, ref AA.X); /* edwards25519.FeMul(&inv1, &inv1, &A.X) */ + fe_invert(out inv1, ref inv1); /* edwards25519.FeInvert(&inv1, &inv1) */ + + FieldElement t0, u; + fe_mul(out u, ref inv1, ref AA.X); /* edwards25519.FeMul(&u, &inv1, &A.X) */ + fe_add(out t0, ref AA.Y, ref AA.Z); /* edwards25519.FeAdd(&t0, &A.Y, &A.Z) */ + fe_mul(out u, ref u, ref t0); /* edwards25519.FeMul(&u, &u, &t0) */ + + FieldElement v; + fe_mul(out v, ref t0, ref inv1); /* edwards25519.FeMul(&v, &t0, &inv1) */ + fe_mul(out v, ref v, ref AA.Z); /* edwards25519.FeMul(&v, &v, &A.Z) */ + fe_mul(out v, ref v, ref sqrtMinusA); /* edwards25519.FeMul(&v, &v, &sqrtMinusA) */ + + FieldElement b; + fe_add(out b, ref u, ref A); /* edwards25519.FeAdd(&b, &u, &edwards25519.A) */ + + FieldElement c, b3, b8; + fe_sq(out b3, ref b); /* edwards25519.FeSquare(&b3, &b) // 2 */ + fe_mul(out b3, ref b3, ref b); /* edwards25519.FeMul(&b3, &b3, &b) // 3 */ + fe_sq(out c, ref b3); /* edwards25519.FeSquare(&c, &b3) // 6 */ + fe_mul(out c, ref c, ref b); /* edwards25519.FeMul(&c, &c, &b) // 7 */ + fe_mul(out b8, ref c, ref b); /* edwards25519.FeMul(&b8, &c, &b) // 8 */ + fe_mul(out c, ref c, ref u); /* edwards25519.FeMul(&c, &c, &u) */ + q58(out c, ref c); /* q58(&c, &c) */ + + FieldElement chi; + fe_sq(out chi, ref c); /* edwards25519.FeSquare(&chi, &c) */ + fe_sq(out chi, ref chi); /* edwards25519.FeSquare(&chi, &chi) */ + + fe_sq(out t0, ref u); /* edwards25519.FeSquare(&t0, &u) */ + fe_mul(out chi, ref chi, ref t0); /* edwards25519.FeMul(&chi, &chi, &t0) */ + + fe_sq(out t0, ref b); /* edwards25519.FeSquare(&t0, &b) // 2 */ + fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) // 3 */ + fe_sq(out t0, ref t0); /* edwards25519.FeSquare(&t0, &t0) // 6 */ + fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) // 7 */ + fe_sq(out t0, ref t0); /* edwards25519.FeSquare(&t0, &t0) // 14 */ + fe_mul(out chi, ref chi, ref t0); /* edwards25519.FeMul(&chi, &chi, &t0) */ + fe_neg(out chi, ref chi); /* edwards25519.FeNeg(&chi, &chi) */ + + var chiBytes = new byte[32]; + fe_tobytes(chiBytes,0,ref chi); /*edwards25519.FeToBytes(&chiBytes, &chi) */ + // chi[1] is either 0 or 0xff + if (chiBytes[1] == 0xff) + { + return false; + } + + // Calculate r1 = sqrt(-u/(2*(u+A))) + FieldElement r1; + fe_mul(out r1, ref c, ref u); /* edwards25519.FeMul(&r1, &c, &u) */ + fe_mul(out r1, ref r1, ref b3); /* edwards25519.FeMul(&r1, &r1, &b3) */ + fe_mul(out r1, ref r1, ref sqrtMinusHalf); /* edwards25519.FeMul(&r1, &r1, &sqrtMinusHalf) */ + + FieldElement maybeSqrtM1; + fe_sq(out t0, ref r1); /* edwards25519.FeSquare(&t0, &r1) */ + fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) */ + fe_add(out t0, ref t0, ref t0); /* edwards25519.FeAdd(&t0, &t0, &t0) */ + fe_add(out t0, ref t0, ref u); /* edwards25519.FeAdd(&t0, &t0, &u) */ + + fe_1(out maybeSqrtM1); /* edwards25519.FeOne(&maybeSqrtM1) */ + fe_cmov(ref maybeSqrtM1, ref sqrtm1, fe_isnonzero(ref t0)); /* edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0)) */ + fe_mul(out r1, ref r1, ref maybeSqrtM1);/* edwards25519.FeMul(&r1, &r1, &maybeSqrtM1) */ + + // Calculate r = sqrt(-(u+A)/(2u)) + FieldElement r; + fe_sq(out t0, ref c); /* edwards25519.FeSquare(&t0, &c) // 2 */ + fe_mul(out t0, ref t0, ref c); /* edwards25519.FeMul(&t0, &t0, &c) // 3 */ + fe_sq(out t0, ref t0); /* edwards25519.FeSquare(&t0, &t0) // 6 */ + fe_mul(out r, ref t0, ref c); /* edwards25519.FeMul(&r, &t0, &c) // 7 */ + + fe_sq(out t0, ref u); /* edwards25519.FeSquare(&t0, &u) // 2 */ + fe_mul(out t0, ref t0, ref u); /* edwards25519.FeMul(&t0, &t0, &u) // 3 */ + fe_mul(out r, ref r, ref t0); /* edwards25519.FeMul(&r, &r, &t0) */ + + fe_sq(out t0, ref b8); /* edwards25519.FeSquare(&t0, &b8) // 16 */ + fe_mul(out t0, ref t0, ref b8); /* edwards25519.FeMul(&t0, &t0, &b8) // 24 */ + fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) // 25 */ + fe_mul(out r, ref r, ref t0); /* edwards25519.FeMul(&r, &r, &t0) */ + fe_mul(out r, ref r, ref sqrtMinusHalf); /* edwards25519.FeMul(&r, &r, &sqrtMinusHalf) */ + + fe_sq(out t0, ref r); /* edwards25519.FeSquare(&t0, &r) */ + fe_mul(out t0, ref t0, ref u); /* edwards25519.FeMul(&t0, &t0, &u) */ + fe_add(out t0, ref t0, ref t0); /* edwards25519.FeAdd(&t0, &t0, &t0) */ + fe_add(out t0, ref t0, ref b); /* edwards25519.FeAdd(&t0, &t0, &b) */ + fe_1(out maybeSqrtM1); /* edwards25519.FeOne(&maybeSqrtM1) */ + fe_cmov(ref maybeSqrtM1, ref sqrtm1, fe_isnonzero(ref t0)); /* edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0)) */ + fe_mul(out r, ref r, ref maybeSqrtM1); /* edwards25519.FeMul(&r, &r, &maybeSqrtM1) */ + + var vBytes = new byte[32]; + fe_tobytes(vBytes,0,ref v); /* edwards25519.FeToBytes(&vBytes, &v) */ + var vInSquareRootImage = fe_bytesLE(ref vBytes, ref halfQMinus1Bytes); /* vInSquareRootImage := feBytesLE(&vBytes, &halfQMinus1Bytes) */ + fe_cmov(ref r, ref r1, vInSquareRootImage); /* edwards25519.FeCMove(&r, &r1, vInSquareRootImage) */ + + fe_tobytes(publicKey,0, ref u); /* edwards25519.FeToBytes(publicKey, &u) */ + fe_tobytes(representative, 0, ref r); /* edwards25519.FeToBytes(representative, &r) */ + return true; + } + + + public static void q58(out FieldElement o, ref FieldElement z) + { + FieldElement t1, t2, t3; + int i; + fe_sq(out t1, ref z); /* edwards25519.FeSquare(&t1, z) // 2^1 */ + fe_mul(out t1, ref z, ref t1); /* edwards25519.FeMul(&t1, &t1, z) // 2^1 + 2^0 */ + fe_sq(out t1, ref t1); /* edwards25519.FeSquare(&t1, &t1) // 2^2 + 2^1 */ + fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 2^3 + 2^2 */ + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) // 2^4 + 2^3 */ + fe_mul(out t2, ref t1, ref t2); /* edwards25519.FeMul(&t2, &t2, &t1) // 4,3,2,1 */ + fe_mul(out t1, ref t2, ref z); /* edwards25519.FeMul(&t1, &t2, z) // 4..0 */ + fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 5..1 */ + for (i = 1; i < 5; i++) + { + // 9,8,7,6,5 + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t1, ref t2, ref t1); /* edwards25519.FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0 */ + fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 10..1 */ + for (i = 1; i < 10; i++) + { + // 19..10 + fe_sq(out t2, ref t2); /*edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t2, ref t1, ref t2); /* edwards25519.FeMul(&t2, &t2, &t1) // 19..0 */ + fe_sq(out t3, ref t2); /* edwards25519.FeSquare(&t3, &t2) // 20..1 */ + for (i = 1; i < 20; i++) + { + // 39..20 + fe_sq(out t3, ref t3); /* edwards25519.FeSquare(&t3, &t3) */ + } + fe_mul(out t2, ref t3, ref t2); /* edwards25519.FeMul(&t2, &t3, &t2) // 39..0 */ + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) // 40..1 */ + for (i = 1; i < 10; i++) + { + // 49..10 + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t1, ref t2, ref t1); /* edwards25519.FeMul(&t1, &t2, &t1) // 49..0 */ + fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 50..1 */ + for (i = 1; i < 50; i++) + { + // 99..50 + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t2, ref t1, ref t2); /* edwards25519.FeMul(&t2, &t2, &t1) // 99..0 */ + fe_sq(out t3, ref t2); /* edwards25519.FeSquare(&t3, &t2) // 100..1 */ + for (i = 1; i < 100; i++) + { + // 199..100 + fe_sq(out t3, ref t3); /* edwards25519.FeSquare(&t3, &t3) */ + } + fe_mul(out t2, ref t3, ref t2); /* edwards25519.FeMul(&t2, &t3, &t2) // 199..0 */ + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) // 200..1 */ + for (i = 1; i < 50; i++) + { + // 249..50 + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t1, ref t2, ref t1); /* edwards25519.FeMul(&t1, &t2, &t1) // 249..0 */ + fe_sq(out t1, ref t1); /* edwards25519.FeSquare(&t1, &t1) // 250..1 */ + fe_sq(out t1, ref t1); /* edwards25519.FeSquare(&t1, &t1) // 251..2 */ + fe_mul(out o, ref t1, ref z); /* edwards25519.FeMul(out, &t1, z) // 251..2,0 */ + } + + public static void chi(out FieldElement o, ref FieldElement z) + { + FieldElement t0, t1, t2, t3; + int i; + fe_sq(out t0, ref z); // 2^1 + fe_mul(out t1, ref t0, ref z); // 2^1 + 2^0 + fe_sq(out t0, ref t1); // 2^2 + 2^1 + fe_sq(out t2, ref t0); // 2^3 + 2^2 + fe_sq(out t2, ref t2); // 4,3 + fe_mul(out t2, ref t2, ref t0); // 4,3,2,1 + fe_mul(out t1, ref t2, ref z); // 4..0 + fe_sq(out t2, ref t1); // 5..1 + for (i = 1; i < 5; i++) + { + // 9,8,7,6,5 + fe_sq(out t2, ref t2); + } + fe_mul(out t1, ref t2, ref t1); // 9,8,7,6,5,4,3,2,1,0 + fe_sq(out t2, ref t1); // 10..1 + for (i = 1; i < 10; i++) + { + // 19..10 + fe_sq(out t2, ref t2); + } + fe_mul(out t2, ref t2, ref t1); // 19..0 + fe_sq(out t3, ref t2); // 20..1 + for (i = 1; i < 20; i++) + { + // 39..20 + fe_sq(out t3, ref t3); + } + fe_mul(out t2, ref t3, ref t2); // 39..0 + fe_sq(out t2, ref t2); // 40..1 + for (i = 1; i < 10; i++) + { + // 49..10 + fe_sq(out t2, ref t2); + } + fe_mul(out t1, ref t2, ref t1); // 49..0 + fe_sq(out t2, ref t1); // 50..1 + for (i = 1; i < 50; i++) + { + // 99..50 + fe_sq(out t2, ref t2); + } + fe_mul(out t2, ref t2, ref t1); // 99..0 + fe_sq(out t3, ref t2); // 100..1 + for (i = 1; i < 100; i++) + { + // 199..100 + fe_sq(out t3, ref t3); + } + fe_mul(out t2, ref t3, ref t2); // 199..0 + fe_sq(out t2, ref t2); // 200..1 + for (i = 1; i < 50; i++) + { + // 249..50 + fe_sq(out t2, ref t2); + } + fe_mul(out t1, ref t2, ref t1); // 249..0 + fe_sq(out t1, ref t1); // 250..1 + for (i = 1; i < 4; i++) + { + // 253..4 + fe_sq(out t1, ref t1); + } + fe_mul(out o, ref t1, ref t0); // 253..4,2,1 + } + + public static void RepresentativeToPublicKey(byte[] representative, byte[] publicKey) + { + FieldElement rr2, v, e; + + fe_frombytes(out rr2, representative, 0); + + fe_sq2(out rr2, ref rr2); + rr2.x0++; + fe_invert(out rr2, ref rr2); + fe_mul(out v, ref A, ref rr2); + fe_neg(out v, ref v); + + FieldElement v2, v3; + fe_sq(out v2, ref v); + fe_mul(out v3, ref v, ref v2); + fe_add(out e, ref v3, ref v); + fe_mul(out v2, ref v2, ref A); + fe_add(out e, ref v2, ref e); + + chi(out e, ref e); + + var eBytes = new byte[32]; + fe_tobytes(eBytes,0,ref e); + var eIsMinus1 = eBytes[1] & 1; + + FieldElement negV; + fe_neg(out negV, ref v); + fe_cmov(ref v, ref negV, eIsMinus1); + fe_0(out v2); + fe_cmov(ref v2, ref A, eIsMinus1); + fe_sub(out v, ref v, ref v2); + fe_tobytes(publicKey, 0, ref v); + } } } diff --git a/Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs b/Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs index fb8b501..0aa9134 100644 --- a/Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs +++ b/Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs @@ -2,8 +2,24 @@ namespace Chaos.NaCl.Internal.Ed25519Ref10 { - internal static partial class LookupTables - { - internal static FieldElement sqrtm1 = new FieldElement(-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482); - } + internal static partial class LookupTables + { + internal static FieldElement sqrtm1 = new FieldElement(-32595792, -7943725, 9377950, 3500415, 12389472, -272473, + -25146209, -2005654, 326686, 11406482); + + internal static FieldElement A = new FieldElement(486662, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + internal static FieldElement sqrtMinusA = new FieldElement(12222970, 8312128, 11511410, -9067497, 15300785, + 241793, -25456130, -14121551, 12187136, -3972024); + + internal static FieldElement sqrtMinusHalf = new FieldElement(-17256545, 3971863, 28865457, -1750208, 27359696, + -16640980, 12573105, 1002827, -163343, 11073975); + + internal static byte[] halfQMinus1Bytes = + { + 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x3f + }; + } } \ No newline at end of file From a0ae6e06a3a22842d8c401ad05ba18cbb7401123 Mon Sep 17 00:00:00 2001 From: Alexey Ermishkin Date: Mon, 11 May 2015 12:46:59 +0500 Subject: [PATCH 2/7] Updated readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index dcecc0d..5293e99 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -Chaos.NaCl +Chaos.NaCl + Elligator ========== Chaos.NaCl is a cryptography library written in C#. It is based on djb's NaCl. From fa1ea22c18f040d78219528b30a945ebb0993e74 Mon Sep 17 00:00:00 2001 From: Alexey Ermishkin Date: Tue, 12 May 2015 23:33:17 +0500 Subject: [PATCH 3/7] refactoring --- Chaos.NaCl.Tests/Ed25519Tests.cs | 297 +----------------- Chaos.NaCl/Chaos.NaCl.csproj | 3 + Chaos.NaCl/Internal/Ed25519Ref10/chi.cs | 74 +++++ Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs | 159 ++++++++++ Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs | 281 ----------------- Chaos.NaCl/Internal/Ed25519Ref10/q58.cs | 71 +++++ 6 files changed, 309 insertions(+), 576 deletions(-) create mode 100644 Chaos.NaCl/Internal/Ed25519Ref10/chi.cs create mode 100644 Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs create mode 100644 Chaos.NaCl/Internal/Ed25519Ref10/q58.cs diff --git a/Chaos.NaCl.Tests/Ed25519Tests.cs b/Chaos.NaCl.Tests/Ed25519Tests.cs index 767c7f0..f694ce0 100644 --- a/Chaos.NaCl.Tests/Ed25519Tests.cs +++ b/Chaos.NaCl.Tests/Ed25519Tests.cs @@ -3,252 +3,17 @@ using System.Linq; using System.Numerics; using System.Security.Cryptography; +using Chaos.NaCl.Internal.Ed25519Ref10; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Chaos.NaCl.Internal.Ed25519Ref10.Ed25519Operations; namespace Chaos.NaCl.Tests { - internal struct TestVectors - { - public bool valid; - public byte[] pub; - public byte[] repr; - public byte[] priv; - }; - - [TestClass] public class Ed25519Tests { - private TestVectors[] vectorz = new[] - { - new TestVectors() - { - valid = true, - pub = - new byte[] - { - 0x4b, 0x97, 0x97, 0xd0, 0x98, 0x5a, 0xf7, 0x42, 0x9b, 0xc3, 0x55, 0xd9, 0x4, 0x81, 0xcf, 0xc7, 0xcc, - 0x14, 0x54, 0x5c, 0xa5, 0xe6, 0x7c, 0x84, 0xcb, 0x1b, 0x4a, 0x4c, 0x4d, 0xa1, 0xda, 0x33, - }, - repr = - new byte[] - { - 0xbc, 0x13, 0x1a, 0x67, 0xb, 0x92, 0x2, 0x65, 0x8f, 0x2f, 0x79, 0xa, 0x7e, 0x4, 0x71, 0xd0, 0xe, - 0x67, 0x90, 0xdb, 0x4d, 0x59, 0x8, 0xd2, 0x54, 0x4e, 0x5f, 0xbb, 0x8d, 0xa, 0x89, 0x78, - }, - priv = - new byte[] - { - 0x10, 0x3d, 0xba, 0xb8, 0x6f, 0x99, 0xee, 0xdb, 0xec, 0xa, 0xd6, 0x8f, 0xa9, 0x20, 0x3d, 0x5f, 0xd4, - 0xf5, 0xe0, 0xdc, 0x48, 0xbc, 0xaf, 0x6c, 0x98, 0x50, 0xd0, 0x1a, 0x12, 0x9f, 0x28, 0x5c, - } - }, - new TestVectors() - { - valid = true, - pub = - new byte[] - { - 0xf1, 0x8, 0x6d, 0x75, 0xb3, 0x59, 0x5c, 0xe7, 0x3c, 0x41, 0xa8, 0x11, 0xbf, 0x1a, 0x10, 0xb1, 0xba, - 0x1a, 0x75, 0xe4, 0xff, 0xd4, 0x98, 0x6c, 0x37, 0x98, 0xe3, 0x54, 0xf3, 0x24, 0x6b, 0x50, - }, - repr = - new byte[] - { - 0x6, 0x13, 0x6a, 0x4f, 0x20, 0x93, 0x37, 0x12, 0x4f, 0x4d, 0x92, 0x31, 0xc8, 0x34, 0xe9, 0xa0, 0x94, - 0x8f, 0x89, 0x6d, 0xc9, 0x1c, 0x85, 0x5b, 0x32, 0x80, 0xd3, 0xd1, 0x4f, 0x42, 0xe8, 0x4e, - }, - priv = - new byte[] - { - 0x68, 0x4f, 0x96, 0xdf, 0xde, 0xa0, 0x57, 0xf5, 0xb2, 0x3f, 0xf6, 0x29, 0x52, 0xb3, 0x34, 0x95, - 0xb0, 0x7b, 0xa3, 0xd5, 0x4, 0xac, 0x79, 0x1d, 0xf, 0x3c, 0x87, 0x52, 0x3a, 0xa7, 0x3f, 0x6d, - } - }, - new TestVectors() - { - valid = true, - pub = - new byte[] - { - 0x49, 0x76, 0xe, 0x1, 0x60, 0x24, 0x44, 0x48, 0x48, 0xc7, 0x9d, 0xc1, 0x81, 0x4, 0x6d, 0xc, 0x3a, - 0x48, 0x8e, 0xf8, 0x67, 0xbd, 0xf9, 0xd1, 0x6f, 0x8c, 0x8f, 0xe4, 0x9b, 0x7b, 0x7f, 0x66, - }, - repr = - new byte[] - { - 0xb7, 0xdd, 0x0, 0x28, 0x3, 0xe0, 0x9f, 0x93, 0x52, 0x5d, 0xf6, 0x49, 0xa3, 0x9, 0xbf, 0x29, 0x16, - 0x71, 0xfd, 0x82, 0x52, 0x23, 0xf2, 0x96, 0x2, 0xee, 0x97, 0x20, 0xc1, 0xd7, 0xa6, 0x3b, - }, - priv = - new byte[] - { - 0x8, 0x69, 0x14, 0xc7, 0xc7, 0xe8, 0x33, 0x79, 0x27, 0xf4, 0xa7, 0x1c, 0xda, 0x21, 0x25, 0x41, 0x9a, - 0xe7, 0xe1, 0x83, 0x90, 0x52, 0x1f, 0xaf, 0x14, 0x3d, 0x5a, 0x2, 0xbc, 0x2e, 0x9c, 0x55, - } - }, - new TestVectors() - { - valid = false, - pub = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - repr = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - priv = - new byte[] - { - 0x78, 0x51, 0x2b, 0xcf, 0xe1, 0x62, 0x1d, 0x53, 0xed, 0xfc, 0xd5, 0xc0, 0x96, 0xc8, 0xb9, 0x96, - 0x8e, 0x3, 0x7, 0x5e, 0xc3, 0x3a, 0x2e, 0xea, 0xaa, 0x44, 0x96, 0x64, 0x71, 0xfc, 0x98, 0x65, - } - }, - new TestVectors() - { - valid = false, - pub = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - repr = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - priv = - new byte[] - { - 0x38, 0xa9, 0xb4, 0x8a, 0xe8, 0x8b, 0xad, 0xbe, 0x15, 0xd9, 0xbe, 0x6b, 0x1d, 0xf8, 0x82, 0xa1, - 0x89, 0x60, 0xd1, 0x14, 0xb6, 0x74, 0xba, 0xe8, 0x3f, 0xb6, 0xac, 0xd4, 0x5d, 0x94, 0x1, 0x68, - } - }, - new TestVectors() - { - valid = true, - pub = - new byte[] - { - 0x1e, 0x92, 0x62, 0xd, 0xc3, 0x9f, 0xf1, 0x28, 0x86, 0x40, 0x80, 0xb1, 0xfd, 0x37, 0xb9, 0x91, 0xac, - 0xcc, 0xbf, 0x3d, 0x2e, 0x48, 0x67, 0xd2, 0xed, 0xf1, 0x75, 0xa6, 0x58, 0x10, 0x6d, 0x55, - }, - repr = - new byte[] - { - 0x5a, 0x25, 0x9d, 0x71, 0x1f, 0xec, 0xb2, 0x6d, 0xe7, 0x8, 0x4f, 0x8d, 0x80, 0x15, 0x4e, 0xec, 0x8c, - 0xa3, 0xde, 0xd5, 0xde, 0x3f, 0xb6, 0x2f, 0x38, 0xc8, 0x6b, 0xf5, 0xf6, 0x84, 0x6e, 0x26, - }, - priv = - new byte[] - { - 0x28, 0x4f, 0x7, 0xcf, 0x45, 0xc0, 0x56, 0x74, 0xc6, 0xa7, 0xce, 0xa4, 0x8e, 0xf1, 0x83, 0xb7, 0xb5, - 0x22, 0x3c, 0xff, 0xe9, 0x2e, 0xa7, 0xcb, 0x78, 0xa2, 0x3, 0x1a, 0x47, 0x54, 0xc, 0x6d, - } - }, - new TestVectors() - { - valid = false, - pub = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - repr = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - priv = - new byte[] - { - 0x10, 0x22, 0xe3, 0x43, 0x94, 0x95, 0xd7, 0xd9, 0x0, 0xf5, 0xf5, 0xac, 0xd, 0x39, 0x6, 0x48, 0x86, - 0x54, 0x91, 0xe2, 0x88, 0xe5, 0xc2, 0xe6, 0x53, 0x5f, 0x10, 0xd8, 0x3e, 0xd0, 0xe, 0x7f, - } - }, - new TestVectors() - { - valid = false, - pub = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - repr = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - priv = - new byte[] - { - 0xa8, 0x30, 0x33, 0xe7, 0x55, 0x18, 0xae, 0x32, 0xb2, 0xd4, 0xdd, 0xb7, 0x76, 0x9a, 0x73, 0xd7, - 0x72, 0x8d, 0x5f, 0xd8, 0xd8, 0xe9, 0x57, 0x8a, 0xa9, 0x8e, 0xb6, 0x12, 0x7b, 0x3e, 0x8d, 0x6e, - } - }, - new TestVectors() - { - valid = true, - pub = - new byte[] - { - 0x64, 0x27, 0xe0, 0x5, 0x13, 0xab, 0x7a, 0x81, 0x46, 0xd5, 0x8e, 0xbc, 0x28, 0x25, 0xf4, 0x66, 0xe3, - 0x1c, 0x12, 0xbf, 0x97, 0x25, 0x99, 0x20, 0x37, 0x27, 0xd6, 0x1e, 0x9b, 0x6a, 0x6e, 0x7d, - }, - repr = - new byte[] - { - 0x5f, 0x9e, 0x2, 0x23, 0x7c, 0xa4, 0xfc, 0xc2, 0xc1, 0x8c, 0xc8, 0x91, 0x53, 0xdb, 0xa7, 0x5c, 0xca, - 0x58, 0xea, 0x12, 0x60, 0x41, 0x3f, 0x36, 0xe, 0xe7, 0x7d, 0x78, 0x1d, 0x72, 0xa9, 0x33, - }, - priv = - new byte[] - { - 0x80, 0x5a, 0x8f, 0xba, 0x73, 0x45, 0x30, 0xc8, 0xf0, 0xb7, 0x64, 0x6a, 0xae, 0x73, 0x6f, 0x54, - 0x65, 0x22, 0x5, 0xe8, 0x7, 0xd6, 0xab, 0x83, 0xc2, 0xe6, 0x79, 0x5e, 0x9a, 0x73, 0x22, 0x78, - } - }, - new TestVectors() - { - valid = false, - pub = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - repr = - new byte[] - { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - priv = - new byte[] - { - 0xc8, 0x25, 0x50, 0x29, 0xcd, 0x16, 0x69, 0xa9, 0x5c, 0x37, 0xab, 0xe7, 0xf5, 0x51, 0x9e, 0xaa, - 0xbe, 0x7e, 0x8d, 0x51, 0x27, 0x4c, 0xd4, 0x46, 0x14, 0xb, 0xf9, 0x50, 0x80, 0x4, 0x14, 0x6e, - } - } - }; - - - - - [AssemblyInitializeAttribute] + [AssemblyInitialize] public static void LoadTestVectors(TestContext context) { Ed25519TestVectors.LoadTestCases(); @@ -259,64 +24,6 @@ public static void LoadTestVectors(TestContext context) Ed25519.Verify(sig, new byte[10], pk); } - - - [TestMethod] - public void ElligatorTest() - { - var rnd = new RNGCryptoServiceProvider(); - for (var i = 0; i< 100000; i++) - { - var publicKey1= new byte[32]; - var publicKey2= new byte[32]; - var publicKey3 = new byte[32]; - var privateKey = new byte[32]; - var rep = new byte[32]; - - rnd.GetBytes(privateKey); - - if (Elligator(publicKey1, rep, privateKey)) - { - RepresentativeToPublicKey(rep, publicKey2); - crypto_ecdh_keypair(publicKey3, privateKey); - TestHelpers.AssertEqualBytes(publicKey2, publicKey3); - TestHelpers.AssertEqualBytes(publicKey2, publicKey1); - } - - } - - - - var representative1 = new byte[32]; - var decodedPubKey1 = new byte[32]; - var publicKeyT1 = new byte[32]; - var res1 = Elligator(publicKeyT1, representative1, vectorz[0].priv); - - - var representative2 = new byte[32]; - var decodedPubKey2 = new byte[32]; - var publicKeyT2 = new byte[32]; - var res2 = Elligator(publicKeyT2, representative2, vectorz[1].priv); - - var k1 = MontgomeryCurve25519.KeyExchange(publicKeyT1, vectorz[1].priv); - var k2 = MontgomeryCurve25519.KeyExchange(publicKeyT2, vectorz[0].priv); - - - foreach (var vector in vectorz) - { - var representative = new byte[32]; - var decodedPubKey = new byte[32]; - var publicKeyT = new byte[32]; - var res = Elligator(publicKeyT, representative, vector.priv); - Assert.AreEqual(res, vector.valid); - if (res) - { - RepresentativeToPublicKey(representative, decodedPubKey); - TestHelpers.AssertEqualBytes(decodedPubKey, vector.pub); - } - } - } - [TestMethod] public void KeyPairFromSeed() { diff --git a/Chaos.NaCl/Chaos.NaCl.csproj b/Chaos.NaCl/Chaos.NaCl.csproj index b0afaea..edcdc65 100644 --- a/Chaos.NaCl/Chaos.NaCl.csproj +++ b/Chaos.NaCl/Chaos.NaCl.csproj @@ -42,9 +42,12 @@ + + + diff --git a/Chaos.NaCl/Internal/Ed25519Ref10/chi.cs b/Chaos.NaCl/Internal/Ed25519Ref10/chi.cs new file mode 100644 index 0000000..9a73a56 --- /dev/null +++ b/Chaos.NaCl/Internal/Ed25519Ref10/chi.cs @@ -0,0 +1,74 @@ +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + public static void chi(out FieldElement o, ref FieldElement z) + { + FieldElement t0, t1, t2, t3; + int i; + fe_sq(out t0, ref z); // 2^1 + fe_mul(out t1, ref t0, ref z); // 2^1 + 2^0 + fe_sq(out t0, ref t1); // 2^2 + 2^1 + fe_sq(out t2, ref t0); // 2^3 + 2^2 + fe_sq(out t2, ref t2); // 4,3 + fe_mul(out t2, ref t2, ref t0); // 4,3,2,1 + fe_mul(out t1, ref t2, ref z); // 4..0 + fe_sq(out t2, ref t1); // 5..1 + for (i = 1; i < 5; i++) + { + // 9,8,7,6,5 + fe_sq(out t2, ref t2); + } + fe_mul(out t1, ref t2, ref t1); // 9,8,7,6,5,4,3,2,1,0 + fe_sq(out t2, ref t1); // 10..1 + for (i = 1; i < 10; i++) + { + // 19..10 + fe_sq(out t2, ref t2); + } + fe_mul(out t2, ref t2, ref t1); // 19..0 + fe_sq(out t3, ref t2); // 20..1 + for (i = 1; i < 20; i++) + { + // 39..20 + fe_sq(out t3, ref t3); + } + fe_mul(out t2, ref t3, ref t2); // 39..0 + fe_sq(out t2, ref t2); // 40..1 + for (i = 1; i < 10; i++) + { + // 49..10 + fe_sq(out t2, ref t2); + } + fe_mul(out t1, ref t2, ref t1); // 49..0 + fe_sq(out t2, ref t1); // 50..1 + for (i = 1; i < 50; i++) + { + // 99..50 + fe_sq(out t2, ref t2); + } + fe_mul(out t2, ref t2, ref t1); // 99..0 + fe_sq(out t3, ref t2); // 100..1 + for (i = 1; i < 100; i++) + { + // 199..100 + fe_sq(out t3, ref t3); + } + fe_mul(out t2, ref t3, ref t2); // 199..0 + fe_sq(out t2, ref t2); // 200..1 + for (i = 1; i < 50; i++) + { + // 249..50 + fe_sq(out t2, ref t2); + } + fe_mul(out t1, ref t2, ref t1); // 249..0 + fe_sq(out t1, ref t1); // 250..1 + for (i = 1; i < 4; i++) + { + // 253..4 + fe_sq(out t1, ref t1); + } + fe_mul(out o, ref t1, ref t0); // 253..4,2,1 + } + } +} \ No newline at end of file diff --git a/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs b/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs new file mode 100644 index 0000000..0a707e7 --- /dev/null +++ b/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs @@ -0,0 +1,159 @@ +using static Chaos.NaCl.Internal.Ed25519Ref10.GroupOperations; +using static Chaos.NaCl.Internal.Ed25519Ref10.LookupTables; + +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + public static bool Elligator(out byte[] publicKey, out byte[] representative, ref byte[] privateKey) + { + GroupElementP3 AA; + + byte[] h = Sha512.Hash(privateKey, 0, 32);//ToDo: Remove alloc + ScalarOperations.sc_clamp(h, 0); + + ge_scalarmult_base(out AA, h, 0); + + FieldElement inv1; + fe_sub(out inv1, ref AA.Z, ref AA.Y); /* edwards25519.FeSub(&inv1, &A.Z, &A.Y) */ + fe_mul(out inv1, ref inv1, ref AA.X); /* edwards25519.FeMul(&inv1, &inv1, &A.X) */ + fe_invert(out inv1, ref inv1); /* edwards25519.FeInvert(&inv1, &inv1) */ + + FieldElement t0, u; + fe_mul(out u, ref inv1, ref AA.X); /* edwards25519.FeMul(&u, &inv1, &A.X) */ + fe_add(out t0, ref AA.Y, ref AA.Z); /* edwards25519.FeAdd(&t0, &A.Y, &A.Z) */ + fe_mul(out u, ref u, ref t0); /* edwards25519.FeMul(&u, &u, &t0) */ + + FieldElement v; + fe_mul(out v, ref t0, ref inv1); /* edwards25519.FeMul(&v, &t0, &inv1) */ + fe_mul(out v, ref v, ref AA.Z); /* edwards25519.FeMul(&v, &v, &A.Z) */ + fe_mul(out v, ref v, ref sqrtMinusA); /* edwards25519.FeMul(&v, &v, &sqrtMinusA) */ + + FieldElement b; + fe_add(out b, ref u, ref A); /* edwards25519.FeAdd(&b, &u, &edwards25519.A) */ + + FieldElement c, b3, b8; + fe_sq(out b3, ref b); /* edwards25519.FeSquare(&b3, &b) // 2 */ + fe_mul(out b3, ref b3, ref b); /* edwards25519.FeMul(&b3, &b3, &b) // 3 */ + fe_sq(out c, ref b3); /* edwards25519.FeSquare(&c, &b3) // 6 */ + fe_mul(out c, ref c, ref b); /* edwards25519.FeMul(&c, &c, &b) // 7 */ + fe_mul(out b8, ref c, ref b); /* edwards25519.FeMul(&b8, &c, &b) // 8 */ + fe_mul(out c, ref c, ref u); /* edwards25519.FeMul(&c, &c, &u) */ + q58(out c, ref c); /* q58(&c, &c) */ + + FieldElement chi; + fe_sq(out chi, ref c); /* edwards25519.FeSquare(&chi, &c) */ + fe_sq(out chi, ref chi); /* edwards25519.FeSquare(&chi, &chi) */ + + fe_sq(out t0, ref u); /* edwards25519.FeSquare(&t0, &u) */ + fe_mul(out chi, ref chi, ref t0); /* edwards25519.FeMul(&chi, &chi, &t0) */ + + fe_sq(out t0, ref b); /* edwards25519.FeSquare(&t0, &b) // 2 */ + fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) // 3 */ + fe_sq(out t0, ref t0); /* edwards25519.FeSquare(&t0, &t0) // 6 */ + fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) // 7 */ + fe_sq(out t0, ref t0); /* edwards25519.FeSquare(&t0, &t0) // 14 */ + fe_mul(out chi, ref chi, ref t0); /* edwards25519.FeMul(&chi, &chi, &t0) */ + fe_neg(out chi, ref chi); /* edwards25519.FeNeg(&chi, &chi) */ + + var chiBytes = new byte[32]; + fe_tobytes(chiBytes, 0, ref chi); /*edwards25519.FeToBytes(&chiBytes, &chi) */ + // chi[1] is either 0 or 0xff + if (chiBytes[1] == 0xff) + { + representative = null; + publicKey = null; + return false; + } + + // Calculate r1 = sqrt(-u/(2*(u+A))) + FieldElement r1; + fe_mul(out r1, ref c, ref u); /* edwards25519.FeMul(&r1, &c, &u) */ + fe_mul(out r1, ref r1, ref b3); /* edwards25519.FeMul(&r1, &r1, &b3) */ + fe_mul(out r1, ref r1, ref sqrtMinusHalf); /* edwards25519.FeMul(&r1, &r1, &sqrtMinusHalf) */ + + FieldElement maybeSqrtM1; + fe_sq(out t0, ref r1); /* edwards25519.FeSquare(&t0, &r1) */ + fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) */ + fe_add(out t0, ref t0, ref t0); /* edwards25519.FeAdd(&t0, &t0, &t0) */ + fe_add(out t0, ref t0, ref u); /* edwards25519.FeAdd(&t0, &t0, &u) */ + + fe_1(out maybeSqrtM1); /* edwards25519.FeOne(&maybeSqrtM1) */ + fe_cmov(ref maybeSqrtM1, ref sqrtm1, fe_isnonzero(ref t0)); /* edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0)) */ + fe_mul(out r1, ref r1, ref maybeSqrtM1);/* edwards25519.FeMul(&r1, &r1, &maybeSqrtM1) */ + + // Calculate r = sqrt(-(u+A)/(2u)) + FieldElement r; + fe_sq(out t0, ref c); /* edwards25519.FeSquare(&t0, &c) // 2 */ + fe_mul(out t0, ref t0, ref c); /* edwards25519.FeMul(&t0, &t0, &c) // 3 */ + fe_sq(out t0, ref t0); /* edwards25519.FeSquare(&t0, &t0) // 6 */ + fe_mul(out r, ref t0, ref c); /* edwards25519.FeMul(&r, &t0, &c) // 7 */ + + fe_sq(out t0, ref u); /* edwards25519.FeSquare(&t0, &u) // 2 */ + fe_mul(out t0, ref t0, ref u); /* edwards25519.FeMul(&t0, &t0, &u) // 3 */ + fe_mul(out r, ref r, ref t0); /* edwards25519.FeMul(&r, &r, &t0) */ + + fe_sq(out t0, ref b8); /* edwards25519.FeSquare(&t0, &b8) // 16 */ + fe_mul(out t0, ref t0, ref b8); /* edwards25519.FeMul(&t0, &t0, &b8) // 24 */ + fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) // 25 */ + fe_mul(out r, ref r, ref t0); /* edwards25519.FeMul(&r, &r, &t0) */ + fe_mul(out r, ref r, ref sqrtMinusHalf); /* edwards25519.FeMul(&r, &r, &sqrtMinusHalf) */ + + fe_sq(out t0, ref r); /* edwards25519.FeSquare(&t0, &r) */ + fe_mul(out t0, ref t0, ref u); /* edwards25519.FeMul(&t0, &t0, &u) */ + fe_add(out t0, ref t0, ref t0); /* edwards25519.FeAdd(&t0, &t0, &t0) */ + fe_add(out t0, ref t0, ref b); /* edwards25519.FeAdd(&t0, &t0, &b) */ + fe_1(out maybeSqrtM1); /* edwards25519.FeOne(&maybeSqrtM1) */ + fe_cmov(ref maybeSqrtM1, ref sqrtm1, fe_isnonzero(ref t0)); /* edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0)) */ + fe_mul(out r, ref r, ref maybeSqrtM1); /* edwards25519.FeMul(&r, &r, &maybeSqrtM1) */ + + var vBytes = new byte[32]; + fe_tobytes(vBytes, 0, ref v); /* edwards25519.FeToBytes(&vBytes, &v) */ + var vInSquareRootImage = fe_bytesLE(ref vBytes, ref halfQMinus1Bytes); /* vInSquareRootImage := feBytesLE(&vBytes, &halfQMinus1Bytes) */ + fe_cmov(ref r, ref r1, vInSquareRootImage); /* edwards25519.FeCMove(&r, &r1, vInSquareRootImage) */ + + + publicKey = new byte[32]; + representative = new byte[32]; + fe_tobytes(publicKey, 0, ref u); /* edwards25519.FeToBytes(publicKey, &u) */ + fe_tobytes(representative, 0, ref r); /* edwards25519.FeToBytes(representative, &r) */ + return true; + } + + + public static void RepresentativeToPublicKey(out byte[] publicKey, ref byte[] representative) + { + FieldElement rr2, v, e; + + fe_frombytes(out rr2, representative, 0); + + fe_sq2(out rr2, ref rr2); + rr2.x0++; + fe_invert(out rr2, ref rr2); + fe_mul(out v, ref LookupTables.A, ref rr2); + fe_neg(out v, ref v); + + FieldElement v2, v3; + fe_sq(out v2, ref v); + fe_mul(out v3, ref v, ref v2); + fe_add(out e, ref v3, ref v); + fe_mul(out v2, ref v2, ref LookupTables.A); + fe_add(out e, ref v2, ref e); + + chi(out e, ref e); + + var eBytes = new byte[32]; + fe_tobytes(eBytes, 0, ref e); + var eIsMinus1 = eBytes[1] & 1; + + FieldElement negV; + fe_neg(out negV, ref v); + fe_cmov(ref v, ref negV, eIsMinus1); + fe_0(out v2); + fe_cmov(ref v2, ref LookupTables.A, eIsMinus1); + fe_sub(out v, ref v, ref v2); + publicKey = new byte[32]; + fe_tobytes(publicKey, 0, ref v); + } + } +} \ No newline at end of file diff --git a/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs b/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs index aa9d372..323ec40 100644 --- a/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs +++ b/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs @@ -36,286 +36,5 @@ public static void crypto_ecdh_keypair(byte[] publicKey, byte[] privateKey) EdwardsToMontgomeryX(out publicKeyFE, ref A.Y, ref A.Z); fe_tobytes(publicKey, 0, ref publicKeyFE); } - - - public static bool Elligator(byte[] publicKey, byte[] representative, byte[] privateKey) - { - GroupElementP3 AA; - int i; - - //byte[] h = Sha512.Hash(privateKey, 0, 32);//ToDo: Remove alloc - ScalarOperations.sc_clamp(privateKey, 0); - - ge_scalarmult_base(out AA, privateKey, 0); - - FieldElement inv1; - fe_sub(out inv1, ref AA.Z, ref AA.Y); /* edwards25519.FeSub(&inv1, &A.Z, &A.Y) */ - fe_mul(out inv1, ref inv1, ref AA.X); /* edwards25519.FeMul(&inv1, &inv1, &A.X) */ - fe_invert(out inv1, ref inv1); /* edwards25519.FeInvert(&inv1, &inv1) */ - - FieldElement t0, u; - fe_mul(out u, ref inv1, ref AA.X); /* edwards25519.FeMul(&u, &inv1, &A.X) */ - fe_add(out t0, ref AA.Y, ref AA.Z); /* edwards25519.FeAdd(&t0, &A.Y, &A.Z) */ - fe_mul(out u, ref u, ref t0); /* edwards25519.FeMul(&u, &u, &t0) */ - - FieldElement v; - fe_mul(out v, ref t0, ref inv1); /* edwards25519.FeMul(&v, &t0, &inv1) */ - fe_mul(out v, ref v, ref AA.Z); /* edwards25519.FeMul(&v, &v, &A.Z) */ - fe_mul(out v, ref v, ref sqrtMinusA); /* edwards25519.FeMul(&v, &v, &sqrtMinusA) */ - - FieldElement b; - fe_add(out b, ref u, ref A); /* edwards25519.FeAdd(&b, &u, &edwards25519.A) */ - - FieldElement c, b3, b8; - fe_sq(out b3, ref b); /* edwards25519.FeSquare(&b3, &b) // 2 */ - fe_mul(out b3, ref b3, ref b); /* edwards25519.FeMul(&b3, &b3, &b) // 3 */ - fe_sq(out c, ref b3); /* edwards25519.FeSquare(&c, &b3) // 6 */ - fe_mul(out c, ref c, ref b); /* edwards25519.FeMul(&c, &c, &b) // 7 */ - fe_mul(out b8, ref c, ref b); /* edwards25519.FeMul(&b8, &c, &b) // 8 */ - fe_mul(out c, ref c, ref u); /* edwards25519.FeMul(&c, &c, &u) */ - q58(out c, ref c); /* q58(&c, &c) */ - - FieldElement chi; - fe_sq(out chi, ref c); /* edwards25519.FeSquare(&chi, &c) */ - fe_sq(out chi, ref chi); /* edwards25519.FeSquare(&chi, &chi) */ - - fe_sq(out t0, ref u); /* edwards25519.FeSquare(&t0, &u) */ - fe_mul(out chi, ref chi, ref t0); /* edwards25519.FeMul(&chi, &chi, &t0) */ - - fe_sq(out t0, ref b); /* edwards25519.FeSquare(&t0, &b) // 2 */ - fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) // 3 */ - fe_sq(out t0, ref t0); /* edwards25519.FeSquare(&t0, &t0) // 6 */ - fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) // 7 */ - fe_sq(out t0, ref t0); /* edwards25519.FeSquare(&t0, &t0) // 14 */ - fe_mul(out chi, ref chi, ref t0); /* edwards25519.FeMul(&chi, &chi, &t0) */ - fe_neg(out chi, ref chi); /* edwards25519.FeNeg(&chi, &chi) */ - - var chiBytes = new byte[32]; - fe_tobytes(chiBytes,0,ref chi); /*edwards25519.FeToBytes(&chiBytes, &chi) */ - // chi[1] is either 0 or 0xff - if (chiBytes[1] == 0xff) - { - return false; - } - - // Calculate r1 = sqrt(-u/(2*(u+A))) - FieldElement r1; - fe_mul(out r1, ref c, ref u); /* edwards25519.FeMul(&r1, &c, &u) */ - fe_mul(out r1, ref r1, ref b3); /* edwards25519.FeMul(&r1, &r1, &b3) */ - fe_mul(out r1, ref r1, ref sqrtMinusHalf); /* edwards25519.FeMul(&r1, &r1, &sqrtMinusHalf) */ - - FieldElement maybeSqrtM1; - fe_sq(out t0, ref r1); /* edwards25519.FeSquare(&t0, &r1) */ - fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) */ - fe_add(out t0, ref t0, ref t0); /* edwards25519.FeAdd(&t0, &t0, &t0) */ - fe_add(out t0, ref t0, ref u); /* edwards25519.FeAdd(&t0, &t0, &u) */ - - fe_1(out maybeSqrtM1); /* edwards25519.FeOne(&maybeSqrtM1) */ - fe_cmov(ref maybeSqrtM1, ref sqrtm1, fe_isnonzero(ref t0)); /* edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0)) */ - fe_mul(out r1, ref r1, ref maybeSqrtM1);/* edwards25519.FeMul(&r1, &r1, &maybeSqrtM1) */ - - // Calculate r = sqrt(-(u+A)/(2u)) - FieldElement r; - fe_sq(out t0, ref c); /* edwards25519.FeSquare(&t0, &c) // 2 */ - fe_mul(out t0, ref t0, ref c); /* edwards25519.FeMul(&t0, &t0, &c) // 3 */ - fe_sq(out t0, ref t0); /* edwards25519.FeSquare(&t0, &t0) // 6 */ - fe_mul(out r, ref t0, ref c); /* edwards25519.FeMul(&r, &t0, &c) // 7 */ - - fe_sq(out t0, ref u); /* edwards25519.FeSquare(&t0, &u) // 2 */ - fe_mul(out t0, ref t0, ref u); /* edwards25519.FeMul(&t0, &t0, &u) // 3 */ - fe_mul(out r, ref r, ref t0); /* edwards25519.FeMul(&r, &r, &t0) */ - - fe_sq(out t0, ref b8); /* edwards25519.FeSquare(&t0, &b8) // 16 */ - fe_mul(out t0, ref t0, ref b8); /* edwards25519.FeMul(&t0, &t0, &b8) // 24 */ - fe_mul(out t0, ref t0, ref b); /* edwards25519.FeMul(&t0, &t0, &b) // 25 */ - fe_mul(out r, ref r, ref t0); /* edwards25519.FeMul(&r, &r, &t0) */ - fe_mul(out r, ref r, ref sqrtMinusHalf); /* edwards25519.FeMul(&r, &r, &sqrtMinusHalf) */ - - fe_sq(out t0, ref r); /* edwards25519.FeSquare(&t0, &r) */ - fe_mul(out t0, ref t0, ref u); /* edwards25519.FeMul(&t0, &t0, &u) */ - fe_add(out t0, ref t0, ref t0); /* edwards25519.FeAdd(&t0, &t0, &t0) */ - fe_add(out t0, ref t0, ref b); /* edwards25519.FeAdd(&t0, &t0, &b) */ - fe_1(out maybeSqrtM1); /* edwards25519.FeOne(&maybeSqrtM1) */ - fe_cmov(ref maybeSqrtM1, ref sqrtm1, fe_isnonzero(ref t0)); /* edwards25519.FeCMove(&maybeSqrtM1, &edwards25519.SqrtM1, edwards25519.FeIsNonZero(&t0)) */ - fe_mul(out r, ref r, ref maybeSqrtM1); /* edwards25519.FeMul(&r, &r, &maybeSqrtM1) */ - - var vBytes = new byte[32]; - fe_tobytes(vBytes,0,ref v); /* edwards25519.FeToBytes(&vBytes, &v) */ - var vInSquareRootImage = fe_bytesLE(ref vBytes, ref halfQMinus1Bytes); /* vInSquareRootImage := feBytesLE(&vBytes, &halfQMinus1Bytes) */ - fe_cmov(ref r, ref r1, vInSquareRootImage); /* edwards25519.FeCMove(&r, &r1, vInSquareRootImage) */ - - fe_tobytes(publicKey,0, ref u); /* edwards25519.FeToBytes(publicKey, &u) */ - fe_tobytes(representative, 0, ref r); /* edwards25519.FeToBytes(representative, &r) */ - return true; - } - - - public static void q58(out FieldElement o, ref FieldElement z) - { - FieldElement t1, t2, t3; - int i; - fe_sq(out t1, ref z); /* edwards25519.FeSquare(&t1, z) // 2^1 */ - fe_mul(out t1, ref z, ref t1); /* edwards25519.FeMul(&t1, &t1, z) // 2^1 + 2^0 */ - fe_sq(out t1, ref t1); /* edwards25519.FeSquare(&t1, &t1) // 2^2 + 2^1 */ - fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 2^3 + 2^2 */ - fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) // 2^4 + 2^3 */ - fe_mul(out t2, ref t1, ref t2); /* edwards25519.FeMul(&t2, &t2, &t1) // 4,3,2,1 */ - fe_mul(out t1, ref t2, ref z); /* edwards25519.FeMul(&t1, &t2, z) // 4..0 */ - fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 5..1 */ - for (i = 1; i < 5; i++) - { - // 9,8,7,6,5 - fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ - } - fe_mul(out t1, ref t2, ref t1); /* edwards25519.FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0 */ - fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 10..1 */ - for (i = 1; i < 10; i++) - { - // 19..10 - fe_sq(out t2, ref t2); /*edwards25519.FeSquare(&t2, &t2) */ - } - fe_mul(out t2, ref t1, ref t2); /* edwards25519.FeMul(&t2, &t2, &t1) // 19..0 */ - fe_sq(out t3, ref t2); /* edwards25519.FeSquare(&t3, &t2) // 20..1 */ - for (i = 1; i < 20; i++) - { - // 39..20 - fe_sq(out t3, ref t3); /* edwards25519.FeSquare(&t3, &t3) */ - } - fe_mul(out t2, ref t3, ref t2); /* edwards25519.FeMul(&t2, &t3, &t2) // 39..0 */ - fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) // 40..1 */ - for (i = 1; i < 10; i++) - { - // 49..10 - fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ - } - fe_mul(out t1, ref t2, ref t1); /* edwards25519.FeMul(&t1, &t2, &t1) // 49..0 */ - fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 50..1 */ - for (i = 1; i < 50; i++) - { - // 99..50 - fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ - } - fe_mul(out t2, ref t1, ref t2); /* edwards25519.FeMul(&t2, &t2, &t1) // 99..0 */ - fe_sq(out t3, ref t2); /* edwards25519.FeSquare(&t3, &t2) // 100..1 */ - for (i = 1; i < 100; i++) - { - // 199..100 - fe_sq(out t3, ref t3); /* edwards25519.FeSquare(&t3, &t3) */ - } - fe_mul(out t2, ref t3, ref t2); /* edwards25519.FeMul(&t2, &t3, &t2) // 199..0 */ - fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) // 200..1 */ - for (i = 1; i < 50; i++) - { - // 249..50 - fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ - } - fe_mul(out t1, ref t2, ref t1); /* edwards25519.FeMul(&t1, &t2, &t1) // 249..0 */ - fe_sq(out t1, ref t1); /* edwards25519.FeSquare(&t1, &t1) // 250..1 */ - fe_sq(out t1, ref t1); /* edwards25519.FeSquare(&t1, &t1) // 251..2 */ - fe_mul(out o, ref t1, ref z); /* edwards25519.FeMul(out, &t1, z) // 251..2,0 */ - } - - public static void chi(out FieldElement o, ref FieldElement z) - { - FieldElement t0, t1, t2, t3; - int i; - fe_sq(out t0, ref z); // 2^1 - fe_mul(out t1, ref t0, ref z); // 2^1 + 2^0 - fe_sq(out t0, ref t1); // 2^2 + 2^1 - fe_sq(out t2, ref t0); // 2^3 + 2^2 - fe_sq(out t2, ref t2); // 4,3 - fe_mul(out t2, ref t2, ref t0); // 4,3,2,1 - fe_mul(out t1, ref t2, ref z); // 4..0 - fe_sq(out t2, ref t1); // 5..1 - for (i = 1; i < 5; i++) - { - // 9,8,7,6,5 - fe_sq(out t2, ref t2); - } - fe_mul(out t1, ref t2, ref t1); // 9,8,7,6,5,4,3,2,1,0 - fe_sq(out t2, ref t1); // 10..1 - for (i = 1; i < 10; i++) - { - // 19..10 - fe_sq(out t2, ref t2); - } - fe_mul(out t2, ref t2, ref t1); // 19..0 - fe_sq(out t3, ref t2); // 20..1 - for (i = 1; i < 20; i++) - { - // 39..20 - fe_sq(out t3, ref t3); - } - fe_mul(out t2, ref t3, ref t2); // 39..0 - fe_sq(out t2, ref t2); // 40..1 - for (i = 1; i < 10; i++) - { - // 49..10 - fe_sq(out t2, ref t2); - } - fe_mul(out t1, ref t2, ref t1); // 49..0 - fe_sq(out t2, ref t1); // 50..1 - for (i = 1; i < 50; i++) - { - // 99..50 - fe_sq(out t2, ref t2); - } - fe_mul(out t2, ref t2, ref t1); // 99..0 - fe_sq(out t3, ref t2); // 100..1 - for (i = 1; i < 100; i++) - { - // 199..100 - fe_sq(out t3, ref t3); - } - fe_mul(out t2, ref t3, ref t2); // 199..0 - fe_sq(out t2, ref t2); // 200..1 - for (i = 1; i < 50; i++) - { - // 249..50 - fe_sq(out t2, ref t2); - } - fe_mul(out t1, ref t2, ref t1); // 249..0 - fe_sq(out t1, ref t1); // 250..1 - for (i = 1; i < 4; i++) - { - // 253..4 - fe_sq(out t1, ref t1); - } - fe_mul(out o, ref t1, ref t0); // 253..4,2,1 - } - - public static void RepresentativeToPublicKey(byte[] representative, byte[] publicKey) - { - FieldElement rr2, v, e; - - fe_frombytes(out rr2, representative, 0); - - fe_sq2(out rr2, ref rr2); - rr2.x0++; - fe_invert(out rr2, ref rr2); - fe_mul(out v, ref A, ref rr2); - fe_neg(out v, ref v); - - FieldElement v2, v3; - fe_sq(out v2, ref v); - fe_mul(out v3, ref v, ref v2); - fe_add(out e, ref v3, ref v); - fe_mul(out v2, ref v2, ref A); - fe_add(out e, ref v2, ref e); - - chi(out e, ref e); - - var eBytes = new byte[32]; - fe_tobytes(eBytes,0,ref e); - var eIsMinus1 = eBytes[1] & 1; - - FieldElement negV; - fe_neg(out negV, ref v); - fe_cmov(ref v, ref negV, eIsMinus1); - fe_0(out v2); - fe_cmov(ref v2, ref A, eIsMinus1); - fe_sub(out v, ref v, ref v2); - fe_tobytes(publicKey, 0, ref v); - } } } diff --git a/Chaos.NaCl/Internal/Ed25519Ref10/q58.cs b/Chaos.NaCl/Internal/Ed25519Ref10/q58.cs new file mode 100644 index 0000000..3e10c95 --- /dev/null +++ b/Chaos.NaCl/Internal/Ed25519Ref10/q58.cs @@ -0,0 +1,71 @@ +namespace Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + + { + public static void q58(out FieldElement o, ref FieldElement z) + { + FieldElement t1, t2, t3; + int i; + fe_sq(out t1, ref z); /* edwards25519.FeSquare(&t1, z) // 2^1 */ + fe_mul(out t1, ref z, ref t1); /* edwards25519.FeMul(&t1, &t1, z) // 2^1 + 2^0 */ + fe_sq(out t1, ref t1); /* edwards25519.FeSquare(&t1, &t1) // 2^2 + 2^1 */ + fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 2^3 + 2^2 */ + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) // 2^4 + 2^3 */ + fe_mul(out t2, ref t1, ref t2); /* edwards25519.FeMul(&t2, &t2, &t1) // 4,3,2,1 */ + fe_mul(out t1, ref t2, ref z); /* edwards25519.FeMul(&t1, &t2, z) // 4..0 */ + fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 5..1 */ + for (i = 1; i < 5; i++) + { + // 9,8,7,6,5 + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t1, ref t2, ref t1); /* edwards25519.FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0 */ + fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 10..1 */ + for (i = 1; i < 10; i++) + { + // 19..10 + fe_sq(out t2, ref t2); /*edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t2, ref t1, ref t2); /* edwards25519.FeMul(&t2, &t2, &t1) // 19..0 */ + fe_sq(out t3, ref t2); /* edwards25519.FeSquare(&t3, &t2) // 20..1 */ + for (i = 1; i < 20; i++) + { + // 39..20 + fe_sq(out t3, ref t3); /* edwards25519.FeSquare(&t3, &t3) */ + } + fe_mul(out t2, ref t3, ref t2); /* edwards25519.FeMul(&t2, &t3, &t2) // 39..0 */ + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) // 40..1 */ + for (i = 1; i < 10; i++) + { + // 49..10 + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t1, ref t2, ref t1); /* edwards25519.FeMul(&t1, &t2, &t1) // 49..0 */ + fe_sq(out t2, ref t1); /* edwards25519.FeSquare(&t2, &t1) // 50..1 */ + for (i = 1; i < 50; i++) + { + // 99..50 + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t2, ref t1, ref t2); /* edwards25519.FeMul(&t2, &t2, &t1) // 99..0 */ + fe_sq(out t3, ref t2); /* edwards25519.FeSquare(&t3, &t2) // 100..1 */ + for (i = 1; i < 100; i++) + { + // 199..100 + fe_sq(out t3, ref t3); /* edwards25519.FeSquare(&t3, &t3) */ + } + fe_mul(out t2, ref t3, ref t2); /* edwards25519.FeMul(&t2, &t3, &t2) // 199..0 */ + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) // 200..1 */ + for (i = 1; i < 50; i++) + { + // 249..50 + fe_sq(out t2, ref t2); /* edwards25519.FeSquare(&t2, &t2) */ + } + fe_mul(out t1, ref t2, ref t1); /* edwards25519.FeMul(&t1, &t2, &t1) // 249..0 */ + fe_sq(out t1, ref t1); /* edwards25519.FeSquare(&t1, &t1) // 250..1 */ + fe_sq(out t1, ref t1); /* edwards25519.FeSquare(&t1, &t1) // 251..2 */ + fe_mul(out o, ref t1, ref z); /* edwards25519.FeMul(out, &t1, z) // 251..2,0 */ + } + } +} \ No newline at end of file From 221bf1e29055c6fb314f8aa38051e2de639b2ae4 Mon Sep 17 00:00:00 2001 From: Scratch Date: Sat, 23 May 2015 21:46:07 +0500 Subject: [PATCH 4/7] Working tests --- Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs | 22 +++++++++++++ Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs | 31 ++++++++----------- Chaos.NaCl/MontgomeryCurve25519.cs | 30 ++++++++++++++++++ 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs b/Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs index e0e8378..d644eb8 100644 --- a/Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs +++ b/Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs @@ -30,6 +30,28 @@ public void GetPublicKeySegments() TestHelpers.AssertEqualBytes(MontgomeryCurve25519TestVectors.BobPublicKey, calculatedBobPublicKey.UnPad()); } + [TestMethod] + public void GetElligatorPublicKeySegments() + { + var privateKey = MontgomeryCurve25519TestVectors.BobPrivateKey.Pad(); + var elligatorKey = new byte[32].Pad(); + if (MontgomeryCurve25519.GetElligatorPublicKey(elligatorKey, privateKey)) + { + var originalKey = new byte[32].Pad(); + MontgomeryCurve25519.GetPublicKeyFromRepresentative(originalKey, elligatorKey); + TestHelpers.AssertEqualBytes(MontgomeryCurve25519TestVectors.BobPublicKey, originalKey.UnPad()); + } + + + privateKey = MontgomeryCurve25519TestVectors.AlicePrivateKey.Pad(); + elligatorKey = new byte[32].Pad(); + if (MontgomeryCurve25519.GetElligatorPublicKey(elligatorKey, privateKey)) + { + throw new Exception("The key is not suitable for Elligator"); + } + + } + [TestMethod] public void GetSharedKeySegments() { diff --git a/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs b/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs index 0a707e7..87bb0a2 100644 --- a/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs +++ b/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs @@ -5,14 +5,14 @@ namespace Chaos.NaCl.Internal.Ed25519Ref10 { internal static partial class FieldOperations { - public static bool Elligator(out byte[] publicKey, out byte[] representative, ref byte[] privateKey) + public static bool Elligator(byte[] representative, int representativeOffset, byte[] privateKey, int privatekeyOffset) { GroupElementP3 AA; - byte[] h = Sha512.Hash(privateKey, 0, 32);//ToDo: Remove alloc - ScalarOperations.sc_clamp(h, 0); + //byte[] h = Sha512.Hash(privateKey, privatekeyOffset, 32);//ToDo: Remove alloc + ScalarOperations.sc_clamp(privateKey, privatekeyOffset); - ge_scalarmult_base(out AA, h, 0); + ge_scalarmult_base(out AA, privateKey, privatekeyOffset); FieldElement inv1; fe_sub(out inv1, ref AA.Z, ref AA.Y); /* edwards25519.FeSub(&inv1, &A.Z, &A.Y) */ @@ -61,8 +61,6 @@ public static bool Elligator(out byte[] publicKey, out byte[] representative, re // chi[1] is either 0 or 0xff if (chiBytes[1] == 0xff) { - representative = null; - publicKey = null; return false; } @@ -112,32 +110,30 @@ public static bool Elligator(out byte[] publicKey, out byte[] representative, re var vInSquareRootImage = fe_bytesLE(ref vBytes, ref halfQMinus1Bytes); /* vInSquareRootImage := feBytesLE(&vBytes, &halfQMinus1Bytes) */ fe_cmov(ref r, ref r1, vInSquareRootImage); /* edwards25519.FeCMove(&r, &r1, vInSquareRootImage) */ - - publicKey = new byte[32]; - representative = new byte[32]; - fe_tobytes(publicKey, 0, ref u); /* edwards25519.FeToBytes(publicKey, &u) */ - fe_tobytes(representative, 0, ref r); /* edwards25519.FeToBytes(representative, &r) */ + var pub = new byte[32]; + fe_tobytes(pub,0, ref u); + fe_tobytes(representative, representativeOffset, ref r); /* edwards25519.FeToBytes(representative, &r) */ return true; } - public static void RepresentativeToPublicKey(out byte[] publicKey, ref byte[] representative) + public static void RepresentativeToPublicKey(byte[] publicKey, int publickeyOffset, byte[] representative, int representativeOffset) { FieldElement rr2, v, e; - fe_frombytes(out rr2, representative, 0); + fe_frombytes(out rr2, representative, representativeOffset); fe_sq2(out rr2, ref rr2); rr2.x0++; fe_invert(out rr2, ref rr2); - fe_mul(out v, ref LookupTables.A, ref rr2); + fe_mul(out v, ref A, ref rr2); fe_neg(out v, ref v); FieldElement v2, v3; fe_sq(out v2, ref v); fe_mul(out v3, ref v, ref v2); fe_add(out e, ref v3, ref v); - fe_mul(out v2, ref v2, ref LookupTables.A); + fe_mul(out v2, ref v2, ref A); fe_add(out e, ref v2, ref e); chi(out e, ref e); @@ -150,10 +146,9 @@ public static void RepresentativeToPublicKey(out byte[] publicKey, ref byte[] re fe_neg(out negV, ref v); fe_cmov(ref v, ref negV, eIsMinus1); fe_0(out v2); - fe_cmov(ref v2, ref LookupTables.A, eIsMinus1); + fe_cmov(ref v2, ref A, eIsMinus1); fe_sub(out v, ref v, ref v2); - publicKey = new byte[32]; - fe_tobytes(publicKey, 0, ref v); + fe_tobytes(publicKey, publickeyOffset, ref v); } } } \ No newline at end of file diff --git a/Chaos.NaCl/MontgomeryCurve25519.cs b/Chaos.NaCl/MontgomeryCurve25519.cs index 562521f..c5c14fc 100644 --- a/Chaos.NaCl/MontgomeryCurve25519.cs +++ b/Chaos.NaCl/MontgomeryCurve25519.cs @@ -59,6 +59,36 @@ public static void GetPublicKey(ArraySegment publicKey, ArraySegment FieldOperations.fe_tobytes(publicKey.Array, publicKey.Offset, ref publicKeyFE); } + public static bool GetElligatorPublicKey(ArraySegment publicKey, ArraySegment privateKey) + { + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (privateKey.Array == null) + throw new ArgumentNullException("privateKey.Array"); + if (publicKey.Count != PublicKeySizeInBytes) + throw new ArgumentException("privateKey.Count must be 32"); + if (privateKey.Count != PrivateKeySizeInBytes) + throw new ArgumentException("privateKey.Count must be 32"); + + return FieldOperations.Elligator(publicKey.Array, publicKey.Offset, privateKey.Array, privateKey.Offset); + } + + + public static void GetPublicKeyFromRepresentative(ArraySegment publicKey, ArraySegment representative) + { + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (representative.Array == null) + throw new ArgumentNullException("representative.Array"); + if (publicKey.Count != PublicKeySizeInBytes) + throw new ArgumentException("publicKey.Count must be 32"); + if (representative.Count != PrivateKeySizeInBytes) + throw new ArgumentException("representative.Count must be 32"); + + FieldOperations.RepresentativeToPublicKey(publicKey.Array, publicKey.Offset, representative.Array, + representative.Offset); + } + // hashes like the Curve25519 paper says internal static void KeyExchangeOutputHashCurve25519Paper(byte[] sharedKey, int offset) { From 6eac9ab3966144f494d2cbf5ce72a5ac28922e3f Mon Sep 17 00:00:00 2001 From: Scratch Date: Sun, 24 May 2015 00:19:58 +0500 Subject: [PATCH 5/7] Added random tests --- Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs b/Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs index d644eb8..af43204 100644 --- a/Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs +++ b/Chaos.NaCl.Tests/MontgomeryCurve25519Tests.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Security.Cryptography; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Chaos.NaCl.Tests @@ -52,6 +53,30 @@ public void GetElligatorPublicKeySegments() } + + [TestMethod] + public void ElligatorRandomKeys() + { + var rng = new RNGCryptoServiceProvider(); + var priv = new byte[32].Pad(); + var elligatorKey = new byte[32].Pad(); + var bytes = new byte[32]; + var restoredKey = new byte[32].Pad(); + var originalKey = new byte[32].Pad(); + for (var i = 0; i < 1000; i++) + { + rng.GetBytes(bytes); + Array.Copy(bytes, 0, priv.Array, priv.Offset, 32); + if (MontgomeryCurve25519.GetElligatorPublicKey(elligatorKey, priv)) + { + MontgomeryCurve25519.GetPublicKeyFromRepresentative(restoredKey, elligatorKey); + MontgomeryCurve25519.GetPublicKey(originalKey, priv); + TestHelpers.AssertEqualBytes(originalKey.UnPad(), restoredKey.UnPad()); + } + } + + } + [TestMethod] public void GetSharedKeySegments() { From d65bbc987ecf0e58f1e57d46616ef97582644872 Mon Sep 17 00:00:00 2001 From: Scratch Date: Sat, 12 Dec 2015 10:34:48 +0500 Subject: [PATCH 6/7] Square root fix --- Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs b/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs index 87bb0a2..afdf4dc 100644 --- a/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs +++ b/Chaos.NaCl/Internal/Ed25519Ref10/elligator.cs @@ -110,6 +110,20 @@ public static bool Elligator(byte[] representative, int representativeOffset, by var vInSquareRootImage = fe_bytesLE(ref vBytes, ref halfQMinus1Bytes); /* vInSquareRootImage := feBytesLE(&vBytes, &halfQMinus1Bytes) */ fe_cmov(ref r, ref r1, vInSquareRootImage); /* edwards25519.FeCMove(&r, &r1, vInSquareRootImage) */ + /* + /* 5.5: Here |b| means b if b in {0, 1, ..., (q - 1)/2}, otherwise -b. + +uint8_t rBytes[32]; + +r.toBytes(rBytes); + +unsigned int negateB = (1 & ~feBytesLE(rBytes, halfQMinus1Bytes)); + +r1.neg(r); + +r.cmov(r1, negateB); + */ + var rbytes = new byte[32]; + fe_tobytes(rbytes,0, ref r); + var negateB = (1 & ~fe_bytesLE(ref rbytes, ref halfQMinus1Bytes)); + fe_neg(out r1, ref r); + fe_cmov(ref r, ref r1, negateB); + var pub = new byte[32]; fe_tobytes(pub,0, ref u); fe_tobytes(representative, representativeOffset, ref r); /* edwards25519.FeToBytes(representative, &r) */ From fbf80fcc91606e3e72b0994190269e629d5d9ef5 Mon Sep 17 00:00:00 2001 From: Scratch Date: Sat, 12 Dec 2015 11:11:54 +0500 Subject: [PATCH 7/7] Added elligator note --- readme.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 5293e99..bbff141 100644 --- a/readme.md +++ b/readme.md @@ -155,4 +155,19 @@ and uses the special characters `+`, `/` and `=`. Decodes a Base64 encoded string back to bytes. -*variable time* \ No newline at end of file +*variable time* + +It is the caller's responsibility to randomize the 2 high bits of the +representative, and to mask out said randomness before converting back from +the representative to the public key. + +This will look something like: + + // Assuming `repr` holds the representative from ScalarBaseMult... + uint8_t bits; + random_bytes(&bits, 1); + repr[31] |= bits & 0xc0; + + // ... on the other side, mask out the 2 bits, then reverse the map. + repr[31] &= ~ 0xc0; + RepresentativeToPublicKey(pub, repr); \ No newline at end of file