Skip to content

Commit

Permalink
Fix Ed25519.KeyExchange
Browse files Browse the repository at this point in the history
  • Loading branch information
CodesInChaos committed Apr 13, 2014
1 parent 55e8473 commit 5d67d2a
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 129 deletions.
35 changes: 35 additions & 0 deletions Chaos.NaCl.Tests/Ed25519Tests.cs
Expand Up @@ -141,5 +141,40 @@ public void VerifyFailSegments()
Assert.IsFalse(Ed25519.Verify(modifiedSignature.Pad(), message.Pad(), pk.Pad()));
}
}

[TestMethod]
public void KeyExchange()
{
var seed = new byte[32];

byte[] publicEdwards, privateEdwards;
Ed25519.KeyPairFromSeed(out publicEdwards, out privateEdwards, seed);
var sharedEdwards = Ed25519.KeyExchange(publicEdwards, privateEdwards);

var privateMontgomery = Sha512.Hash(seed).Take(32).ToArray();
var publicMontgomery = MontgomeryCurve25519.GetPublicKey(privateMontgomery);
var sharedMontgomery = MontgomeryCurve25519.KeyExchange(publicMontgomery, privateMontgomery);

TestHelpers.AssertEqualBytes(sharedMontgomery, sharedEdwards);
}

[TestMethod]
public void KeyExchangeSegments()
{
var seed = new byte[32].Pad();

var publicEdwards = new byte[32].Pad();
var privateEdwards = new byte[64].Pad();
Ed25519.KeyPairFromSeed(publicEdwards, privateEdwards, seed);
var sharedEdwards = new byte[32].Pad();
Ed25519.KeyExchange(sharedEdwards, publicEdwards, privateEdwards);

var privateMontgomery = Sha512.Hash(seed.UnPad()).Take(32).ToArray();
var publicMontgomery = MontgomeryCurve25519.GetPublicKey(privateMontgomery);
var sharedMontgomery = MontgomeryCurve25519.KeyExchange(publicMontgomery, privateMontgomery);

TestHelpers.AssertEqualBytes(sharedMontgomery, sharedEdwards.UnPad());
}

}
}
257 changes: 130 additions & 127 deletions Chaos.NaCl/Ed25519.cs
Expand Up @@ -3,142 +3,145 @@

namespace Chaos.NaCl
{
public static class Ed25519
{
public static readonly int PublicKeySizeInBytes = 32;
public static readonly int SignatureSizeInBytes = 64;
public static readonly int ExpandedPrivateKeySizeInBytes = 32 * 2;
public static readonly int PrivateKeySeedSizeInBytes = 32;
public static readonly int SharedKeySizeInBytes = 32;
public static class Ed25519
{
public static readonly int PublicKeySizeInBytes = 32;
public static readonly int SignatureSizeInBytes = 64;
public static readonly int ExpandedPrivateKeySizeInBytes = 32 * 2;
public static readonly int PrivateKeySeedSizeInBytes = 32;
public static readonly int SharedKeySizeInBytes = 32;

public static bool Verify(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> publicKey)
{
if (signature.Count != SignatureSizeInBytes)
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Count");
if (publicKey.Count != PublicKeySizeInBytes)
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Count");
return Ed25519Operations.crypto_sign_verify(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, publicKey.Array, publicKey.Offset);
}
public static bool Verify(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> publicKey)
{
if (signature.Count != SignatureSizeInBytes)
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Count");
if (publicKey.Count != PublicKeySizeInBytes)
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Count");
return Ed25519Operations.crypto_sign_verify(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, publicKey.Array, publicKey.Offset);
}

public static bool Verify(byte[] signature, byte[] message, byte[] publicKey)
{
if (signature == null)
throw new ArgumentNullException("signature");
if (message == null)
throw new ArgumentNullException("message");
if (publicKey == null)
throw new ArgumentNullException("publicKey");
if (signature.Length != SignatureSizeInBytes)
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Length");
if (publicKey.Length != PublicKeySizeInBytes)
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Length");
return Ed25519Operations.crypto_sign_verify(signature, 0, message, 0, message.Length, publicKey, 0);
}
public static bool Verify(byte[] signature, byte[] message, byte[] publicKey)
{
if (signature == null)
throw new ArgumentNullException("signature");
if (message == null)
throw new ArgumentNullException("message");
if (publicKey == null)
throw new ArgumentNullException("publicKey");
if (signature.Length != SignatureSizeInBytes)
throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Length");
if (publicKey.Length != PublicKeySizeInBytes)
throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Length");
return Ed25519Operations.crypto_sign_verify(signature, 0, message, 0, message.Length, publicKey, 0);
}

public static void Sign(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> expandedPrivateKey)
{
if (signature.Array == null)
throw new ArgumentNullException("signature.Array");
if (signature.Count != SignatureSizeInBytes)
throw new ArgumentException("signature.Count");
if (expandedPrivateKey.Array == null)
throw new ArgumentNullException("expandedPrivateKey.Array");
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
throw new ArgumentException("expandedPrivateKey.Count");
if (message.Array == null)
throw new ArgumentNullException("message.Array");
Ed25519Operations.crypto_sign2(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, expandedPrivateKey.Array, expandedPrivateKey.Offset);
}
public static void Sign(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> expandedPrivateKey)
{
if (signature.Array == null)
throw new ArgumentNullException("signature.Array");
if (signature.Count != SignatureSizeInBytes)
throw new ArgumentException("signature.Count");
if (expandedPrivateKey.Array == null)
throw new ArgumentNullException("expandedPrivateKey.Array");
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
throw new ArgumentException("expandedPrivateKey.Count");
if (message.Array == null)
throw new ArgumentNullException("message.Array");
Ed25519Operations.crypto_sign2(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, expandedPrivateKey.Array, expandedPrivateKey.Offset);
}

public static byte[] Sign(byte[] message, byte[] expandedPrivateKey)
{
var signature = new byte[SignatureSizeInBytes];
Sign(new ArraySegment<byte>(signature), new ArraySegment<byte>(message), new ArraySegment<byte>(expandedPrivateKey));
return signature;
}
public static byte[] Sign(byte[] message, byte[] expandedPrivateKey)
{
var signature = new byte[SignatureSizeInBytes];
Sign(new ArraySegment<byte>(signature), new ArraySegment<byte>(message), new ArraySegment<byte>(expandedPrivateKey));
return signature;
}

public static byte[] PublicKeyFromSeed(byte[] privateKeySeed)
{
byte[] privateKey;
byte[] publicKey;
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
CryptoBytes.Wipe(privateKey);
return publicKey;
}
public static byte[] PublicKeyFromSeed(byte[] privateKeySeed)
{
byte[] privateKey;
byte[] publicKey;
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
CryptoBytes.Wipe(privateKey);
return publicKey;
}

public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed)
{
byte[] privateKey;
byte[] publicKey;
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
CryptoBytes.Wipe(publicKey);
return privateKey;
}
public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed)
{
byte[] privateKey;
byte[] publicKey;
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
CryptoBytes.Wipe(publicKey);
return privateKey;
}

public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed)
{
if (privateKeySeed == null)
throw new ArgumentNullException("privateKeySeed");
if (privateKeySeed.Length != PrivateKeySeedSizeInBytes)
throw new ArgumentException("privateKeySeed");
var pk = new byte[PublicKeySizeInBytes];
var sk = new byte[ExpandedPrivateKeySizeInBytes];
Ed25519Operations.crypto_sign_keypair(pk, 0, sk, 0, privateKeySeed, 0);
publicKey = pk;
expandedPrivateKey = sk;
}
public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed)
{
if (privateKeySeed == null)
throw new ArgumentNullException("privateKeySeed");
if (privateKeySeed.Length != PrivateKeySeedSizeInBytes)
throw new ArgumentException("privateKeySeed");
var pk = new byte[PublicKeySizeInBytes];
var sk = new byte[ExpandedPrivateKeySizeInBytes];
Ed25519Operations.crypto_sign_keypair(pk, 0, sk, 0, privateKeySeed, 0);
publicKey = pk;
expandedPrivateKey = sk;
}

public static void KeyPairFromSeed(ArraySegment<byte> publicKey, ArraySegment<byte> expandedPrivateKey, ArraySegment<byte> privateKeySeed)
{
if (publicKey.Array == null)
throw new ArgumentNullException("publicKey.Array");
if (expandedPrivateKey.Array == null)
throw new ArgumentNullException("expandedPrivateKey.Array");
if (privateKeySeed.Array == null)
throw new ArgumentNullException("privateKeySeed.Array");
if (publicKey.Count != PublicKeySizeInBytes)
throw new ArgumentException("publicKey.Count");
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
throw new ArgumentException("expandedPrivateKey.Count");
if (privateKeySeed.Count != PrivateKeySeedSizeInBytes)
throw new ArgumentException("privateKeySeed.Count");
Ed25519Operations.crypto_sign_keypair(
publicKey.Array, publicKey.Offset,
expandedPrivateKey.Array, expandedPrivateKey.Offset,
privateKeySeed.Array, privateKeySeed.Offset);
}
public static void KeyPairFromSeed(ArraySegment<byte> publicKey, ArraySegment<byte> expandedPrivateKey, ArraySegment<byte> privateKeySeed)
{
if (publicKey.Array == null)
throw new ArgumentNullException("publicKey.Array");
if (expandedPrivateKey.Array == null)
throw new ArgumentNullException("expandedPrivateKey.Array");
if (privateKeySeed.Array == null)
throw new ArgumentNullException("privateKeySeed.Array");
if (publicKey.Count != PublicKeySizeInBytes)
throw new ArgumentException("publicKey.Count");
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
throw new ArgumentException("expandedPrivateKey.Count");
if (privateKeySeed.Count != PrivateKeySeedSizeInBytes)
throw new ArgumentException("privateKeySeed.Count");
Ed25519Operations.crypto_sign_keypair(
publicKey.Array, publicKey.Offset,
expandedPrivateKey.Array, expandedPrivateKey.Offset,
privateKeySeed.Array, privateKeySeed.Offset);
}

[Obsolete("Needs more testing")]
public static byte[] KeyExchange(byte[] publicKey, byte[] privateKey)
{
var sharedKey = new byte[SharedKeySizeInBytes];
KeyExchange(new ArraySegment<byte>(sharedKey), new ArraySegment<byte>(publicKey), new ArraySegment<byte>(privateKey));
return sharedKey;
}
[Obsolete("Needs more testing")]
public static byte[] KeyExchange(byte[] publicKey, byte[] privateKey)
{
var sharedKey = new byte[SharedKeySizeInBytes];
KeyExchange(new ArraySegment<byte>(sharedKey), new ArraySegment<byte>(publicKey), new ArraySegment<byte>(privateKey));
return sharedKey;
}

[Obsolete("Needs more testing")]
public static void KeyExchange(ArraySegment<byte> sharedKey, ArraySegment<byte> publicKey, ArraySegment<byte> privateKey)
{
if (sharedKey.Array == null)
throw new ArgumentNullException("sharedKey.Array");
if (publicKey.Array == null)
throw new ArgumentNullException("publicKey.Array");
if (privateKey.Array == null)
throw new ArgumentNullException("privateKey");
if (sharedKey.Count != 32)
throw new ArgumentException("sharedKey.Count != 32");
if (publicKey.Count != 32)
throw new ArgumentException("publicKey.Count != 32");
if (privateKey.Count != 64)
throw new ArgumentException("privateKey.Count != 64");
[Obsolete("Needs more testing")]
public static void KeyExchange(ArraySegment<byte> sharedKey, ArraySegment<byte> publicKey, ArraySegment<byte> privateKey)
{
if (sharedKey.Array == null)
throw new ArgumentNullException("sharedKey.Array");
if (publicKey.Array == null)
throw new ArgumentNullException("publicKey.Array");
if (privateKey.Array == null)
throw new ArgumentNullException("privateKey");
if (sharedKey.Count != 32)
throw new ArgumentException("sharedKey.Count != 32");
if (publicKey.Count != 32)
throw new ArgumentException("publicKey.Count != 32");
if (privateKey.Count != 64)
throw new ArgumentException("privateKey.Count != 64");

FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX;
FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset);
FieldOperations.fe_1(out edwardsZ);
MontgomeryCurve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ);
MontgomeryOperations.scalarmult(out sharedMontgomeryX, privateKey.Array, privateKey.Offset, ref montgomeryX);
FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX);
MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset);
}
}
FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX;
FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset);
FieldOperations.fe_1(out edwardsZ);
MontgomeryCurve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ);
byte[] h = Sha512.Hash(privateKey.Array, privateKey.Offset, 32);//ToDo: Remove alloc
ScalarOperations.sc_clamp(h, 0);
MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX);
CryptoBytes.Wipe(h);
FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX);
MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset);
}
}
}
4 changes: 2 additions & 2 deletions Chaos.NaCl/Properties/AssemblyInfo.cs
Expand Up @@ -23,8 +23,8 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("0.1.0.0")]
[assembly: AssemblyFileVersion("0.1.0.0")]

[assembly: InternalsVisibleTo("Chaos.NaCl.Tests")]
[assembly: InternalsVisibleTo("Chaos.NaCl.Benchmark")]

0 comments on commit 5d67d2a

Please sign in to comment.