// TODO add exceptions for operations with missing keys public Curve25519KeyPair() { byte[] privateKey; byte[] edPublicKey; byte[] xPrivateKey; byte[] xPublicKey; // Generates keys until the conversion algorithm works for that key (there is probably a <1/100000 chance of this, but hey) while (true) { privateKey = new byte[Ed25519PrivateKeyParameters.KeySize]; Ed25519.GeneratePrivateKey(new SecureRandom(), privateKey); edPublicKey = new byte[Ed25519PublicKeyParameters.KeySize]; Ed25519.GeneratePublicKey(privateKey, 0, edPublicKey, 0); xPrivateKey = ConvertEdPrivateKeyToMontgomery(privateKey); xPublicKey = new byte[X25519PublicKeyParameters.KeySize]; X25519.GeneratePublicKey(xPrivateKey, 0, xPublicKey, 0); if (xPublicKey.SequenceEqual(ConvertEdPublicKeyToMontgomery(edPublicKey))) { break; } } _edPrivateKey = privateKey; _XprivateKey = xPrivateKey; _edPublicKey = edPublicKey; _XpublicKey = xPublicKey; }
private static void CheckIterated(int count) { byte[] k = new byte[32]; k[0] = 9; byte[] u = new byte[32]; u[0] = 9; byte[] r = new byte[32]; int iterations = 0; while (iterations < count) { X25519.ScalarMult(k, 0, u, 0, r, 0); Array.Copy(k, 0, u, 0, 32); Array.Copy(r, 0, k, 0, 32); switch (++iterations) { case 1: CheckValue(k, "Iterated @1", "422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079"); break; case 1000: CheckValue(k, "Iterated @1000", "684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51"); break; case 1000000: CheckValue(k, "Iterated @1000000", "7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424"); break; default: break; } } }
public void TestECDH() { byte[] kA = new byte[32]; byte[] kB = new byte[32]; byte[] qA = new byte[32]; byte[] qB = new byte[32]; byte[] sA = new byte[32]; byte[] sB = new byte[32]; for (int i = 1; i <= 100; ++i) { // Each party generates an ephemeral private key, ... Random.NextBytes(kA); Random.NextBytes(kB); // ... publishes their public key, ... X25519.ScalarMultBase(kA, 0, qA, 0); X25519.ScalarMultBase(kB, 0, qB, 0); // ... computes the shared secret, ... X25519.ScalarMult(kA, 0, qB, 0, sA, 0); X25519.ScalarMult(kB, 0, qA, 0, sB, 0); // ... which is the same for both parties. //Assert.IsTrue(Arrays.AreEqual(sA, sB), "ECDH #" + i); if (!Arrays.AreEqual(sA, sB)) { Console.WriteLine(" " + i); } } }
/// <inheritdoc /> public void EncodeServerKeyExchangeMessage(ByteSpan output, object privateKey) { RSA rsaPrivateKey = privateKey as RSA; if (rsaPrivateKey == null) { throw new ArgumentException("Invalid private key", nameof(privateKey)); } output[0] = (byte)ECCurveType.NamedCurve; output.WriteBigEndian16((ushort)NamedCurve.x25519, 1); output[3] = (byte)X25519.KeySize; X25519.Func(output.Slice(4, X25519.KeySize), this.privateAgreementKey); // Hash the key parameters byte[] paramterDigest = this.sha256.ComputeHash(output.GetUnderlyingArray(), output.Offset, 4 + X25519.KeySize); // Sign the paramter digest RSAPKCS1SignatureFormatter signer = new RSAPKCS1SignatureFormatter(rsaPrivateKey); signer.SetHashAlgorithm("SHA256"); ByteSpan signature = signer.CreateSignature(paramterDigest); Debug.Assert(signature.Length == rsaPrivateKey.KeySize / 8); output[4 + X25519.KeySize] = (byte)HashAlgorithm.Sha256; output[5 + X25519.KeySize] = (byte)SignatureAlgorithm.RSA; output.Slice(6 + X25519.KeySize).WriteBigEndian16((ushort)signature.Length); signature.CopyTo(output.Slice(8 + X25519.KeySize)); }
public void TestAgreement() { using (RandomNumberGenerator random = RandomNumberGenerator.Create()) { byte[] clientPrivateKey = new byte[X25519.KeySize]; random.GetBytes(clientPrivateKey); byte[] clientPublicKey = new byte[X25519.KeySize]; X25519.Func(clientPublicKey, clientPrivateKey); byte[] serverPrivateKey = new byte[X25519.KeySize]; random.GetBytes(serverPrivateKey); byte[] serverPublickey = new byte[X25519.KeySize]; X25519.Func(serverPublickey, serverPrivateKey); // client key aggreement byte[] clientSharedSecret = new byte[X25519.KeySize]; Assert.IsTrue(X25519.Func(clientSharedSecret, clientPrivateKey, serverPublickey)); // server key agreement byte[] serverSharedSecret = new byte[X25519.KeySize]; Assert.IsTrue(X25519.Func(serverSharedSecret, serverPrivateKey, clientPublicKey)); CollectionAssert.AreEqual(clientSharedSecret, serverSharedSecret); } }
public static void Properties() { var a = new X25519(); Assert.Equal(32, a.PublicKeySize); Assert.Equal(32, a.PrivateKeySize); Assert.Equal(32, a.SharedSecretSize); }
private static void CheckX25519Vector(string sk, string su, string se, string text) { byte[] k = Hex.Decode(sk); byte[] u = Hex.Decode(su); byte[] r = new byte[32]; X25519.ScalarMult(k, 0, u, 0, r, 0); CheckValue(r, text, se); }
public byte[] CalculateSharedSecret(byte[] otherPublicKey) { var output = new byte[32]; X25519.CalculateAgreement(_XprivateKey, 0, otherPublicKey, 0, output, 0); return(output); }
public void GenerateSecret(X25519PublicKeyParameters publicKey, byte[] buf, int off) { byte[] encoded = new byte[X25519.PointSize]; publicKey.Encode(encoded, 0); if (!X25519.CalculateAgreement(data, 0, encoded, 0, buf, off)) { throw new InvalidOperationException("X25519 agreement failed"); } }
public void TestVectors() { for (int ii = 0, nn = TestVectorData.Length; ii != nn; ++ii) { byte[] actual = new byte[32]; bool result = X25519.Func(actual, TestVectorData[ii].In, TestVectorData[ii].Base); Assert.IsTrue(result); CollectionAssert.AreEqual(TestVectorData[ii].Expect, actual, $"Test vector {ii} mismatch"); } }
public static void DeriveBytesWithMaxSpan(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); var x = new X25519(); using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { a.DeriveBytes(s, ReadOnlySpan <byte> .Empty, ReadOnlySpan <byte> .Empty, new byte[a.MaxOutputSize]); } }
public static void DeriveKeyWithNullAlgorithm(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); var x = new X25519(); using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { Assert.Throws <ArgumentNullException>("algorithm", () => a.DeriveKey(s, ReadOnlySpan <byte> .Empty, ReadOnlySpan <byte> .Empty, null)); } }
public static void DeriveBytesWithNegativeCount(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); var x = new X25519(); using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { Assert.Throws <ArgumentOutOfRangeException>("count", () => a.DeriveBytes(s, ReadOnlySpan <byte> .Empty, ReadOnlySpan <byte> .Empty, -1)); } }
private static void CheckX25519Vector(string sk, string su, string se, string text) { byte[] k = Hex.Decode(sk); Assert.AreEqual(X25519.ScalarSize, k.Length); byte[] u = Hex.Decode(su); Assert.AreEqual(X25519.PointSize, u.Length); byte[] r = new byte[X25519.PointSize]; X25519.ScalarMult(k, 0, u, 0, r, 0); CheckValue(r, text, se); }
public static void DeriveBytesWithZeroCount(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); var x = new X25519(); using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { var b = a.DeriveBytes(s, ReadOnlySpan <byte> .Empty, ReadOnlySpan <byte> .Empty, 0); Assert.NotNull(b); Assert.Equal(0, b.Length); } }
public static void DeriveBytesWithInfoOverlapping(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); var x = new X25519(); using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { var b = new byte[200]; Assert.Throws <ArgumentException>("bytes", () => a.DeriveBytes(s, ReadOnlySpan <byte> .Empty, b.AsSpan().Slice(10, 100), b.AsSpan().Slice(60, 100))); Assert.Throws <ArgumentException>("bytes", () => a.DeriveBytes(s, ReadOnlySpan <byte> .Empty, b.AsSpan().Slice(60, 100), b.AsSpan().Slice(10, 100))); } }
public static void DeriveBytesWithCountTooLarge(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); var x = new X25519(); if (a.MaxOutputSize < int.MaxValue) { using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { Assert.Throws <ArgumentOutOfRangeException>("count", () => a.DeriveBytes(s, ReadOnlySpan <byte> .Empty, ReadOnlySpan <byte> .Empty, a.MaxOutputSize + 1)); } } }
public static void TestAllZeros(string privateKey, string publicKey) { var a = new X25519(); var pk = PublicKey.Import(a, publicKey.DecodeHex(), KeyBlobFormat.RawPublicKey); using (var k = Key.Import(a, privateKey.DecodeHex(), KeyBlobFormat.RawPrivateKey)) { Assert.False(a.TryAgree(k, pk, out SharedSecret s)); Assert.Null(s); Assert.Throws <CryptographicException>(() => a.Agree(k, pk)); } }
public static void DeriveBytesWithUnusedSalt(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); if (!a.SupportsSalt) { var x = new X25519(); using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { Assert.Throws <ArgumentException>("salt", () => a.DeriveBytes(s, new byte[1], ReadOnlySpan <byte> .Empty, 0)); } } }
public void TestLowOrderPoints() { using (RandomNumberGenerator random = RandomNumberGenerator.Create()) { byte[] scalar = new byte[X25519.KeySize]; random.GetBytes(scalar); for (int ii = 0, nn = LowOrderPoints.Length; ii != nn; ++ii) { ByteSpan output = new byte[X25519.KeySize]; bool result = X25519.Func(output, scalar, LowOrderPoints[ii]); Assert.IsFalse(result, $"Multiplication by low order point {ii} succeeded: should have failed"); } } }
public static void Test(string privateKey, string publicKey, string sharedSecret) { var a = new X25519(); var kdf = new HkdfSha256(); using (var k = Key.Import(a, privateKey.DecodeHex(), KeyBlobFormat.RawPrivateKey)) using (var sharedSecretExpected = SharedSecret.Import(sharedSecret.DecodeHex())) using (var sharedSecretActual = a.Agree(k, PublicKey.Import(a, publicKey.DecodeHex(), KeyBlobFormat.RawPublicKey))) { var expected = kdf.Extract(sharedSecretExpected, ReadOnlySpan <byte> .Empty); var actual = kdf.Extract(sharedSecretActual, ReadOnlySpan <byte> .Empty); Assert.Equal(expected, actual); } }
public void TestConsistency() { byte[] u = new byte[32]; u[0] = 9; byte[] k = new byte[32]; byte[] rF = new byte[32]; byte[] rV = new byte[32]; for (int i = 1; i <= 100; ++i) { Random.NextBytes(k); X25519.ScalarMultBase(k, 0, rF, 0); X25519.ScalarMult(k, 0, u, 0, rV, 0); Assert.IsTrue(Arrays.AreEqual(rF, rV), "Consistency #" + i); } }
public static void BitMaskedEquals(string privateKey, string publicKey, string sharedSecret) { var a = new X25519(); var pk1 = publicKey.DecodeHex(); var pk2 = publicKey.DecodeHex(); pk1[pk1.Length - 1] &= 0x7F; pk2[pk2.Length - 1] |= 0x80; var p1 = PublicKey.Import(a, pk1, KeyBlobFormat.RawPublicKey); var p2 = PublicKey.Import(a, pk2, KeyBlobFormat.RawPublicKey); Assert.True(p1.Equals(p2)); }
/// <inheritdoc /> public bool VerifyClientMessageAndGenerateSharedKey(ByteSpan output, ByteSpan clientKeyExchangeMessage) { if (clientKeyExchangeMessage.Length != ClientMessageSize) { return(false); } else if (clientKeyExchangeMessage[0] != (byte)X25519.KeySize) { return(false); } ByteSpan othersPublicKey = clientKeyExchangeMessage.Slice(1); return(X25519.Func(output, this.privateAgreementKey, othersPublicKey)); }
public static void DeriveBytesWithSpanTooLarge(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); var x = new X25519(); if (a.MaxOutputSize == int.MaxValue) { return; } using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { Assert.Throws <ArgumentException>("bytes", () => a.DeriveBytes(s, ReadOnlySpan <byte> .Empty, ReadOnlySpan <byte> .Empty, new byte[a.MaxOutputSize + 1])); } }
public static void ExpandWithInfoOverlapping() { var a = new HkdfSha256(); var x = new X25519(); using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { var b = new byte[200]; var prk = a.Extract(s, ReadOnlySpan <byte> .Empty); Assert.Throws <ArgumentException>("bytes", () => a.Expand(prk, b.AsSpan().Slice(10, 100), b.AsSpan().Slice(60, 100))); Assert.Throws <ArgumentException>("bytes", () => a.Expand(prk, b.AsSpan().Slice(60, 100), b.AsSpan().Slice(10, 100))); } }
public static void DeriveBytesWithMaxCount(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); var x = new X25519(); using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { var count = Math.Min(a.MaxOutputSize, 500173); var b = a.DeriveBytes(s, ReadOnlySpan <byte> .Empty, ReadOnlySpan <byte> .Empty, count); Assert.NotNull(b); Assert.Equal(count, b.Length); } }
public static void PkixPublicKeyText() { var a = new X25519(); var b = Utilities.RandomBytes.Slice(0, a.PrivateKeySize); using (var k = Key.Import(a, b, KeyBlobFormat.RawPrivateKey)) { var expected = Encoding.UTF8.GetBytes( "-----BEGIN PUBLIC KEY-----\r\n" + Convert.ToBase64String(k.Export(KeyBlobFormat.PkixPublicKey)) + "\r\n" + "-----END PUBLIC KEY-----\r\n"); var actual = k.Export(KeyBlobFormat.PkixPublicKeyText); Assert.Equal(expected, actual); } }
public static void DeriveBytesWithSaltOverlapping(Type algorithmType) { var a = (KeyDerivationAlgorithm)Activator.CreateInstance(algorithmType); var x = new X25519(); using (var k = new Key(x)) using (var s = x.Agree(k, k.PublicKey)) { var actual = new byte[100]; var expected = new byte[100]; Utilities.RandomBytes.Slice(0, 100).CopyTo(actual); a.DeriveBytes(s, actual, ReadOnlySpan <byte> .Empty, expected); a.DeriveBytes(s, actual, ReadOnlySpan <byte> .Empty, actual); Assert.Equal(expected, actual); } }
public static void PkixPublicKey() { var a = new X25519(); var b = Utilities.RandomBytes.Slice(0, a.PrivateKeySize); using (var k = Key.Import(a, b, KeyBlobFormat.RawPrivateKey)) { var publicKeyBytes = k.Export(KeyBlobFormat.RawPublicKey); var blob = k.Export(KeyBlobFormat.PkixPublicKey); var reader = new Asn1Reader(blob); reader.BeginSequence(); reader.BeginSequence(); Assert.Equal(s_oid, reader.ObjectIdentifier().ToArray()); reader.End(); Assert.Equal(publicKeyBytes, reader.BitString().ToArray()); reader.End(); Assert.True(reader.SuccessComplete); } }