aboutsummaryrefslogtreecommitdiff
path: root/Tools/Hazel-Networking/Hazel/Dtls/X25519EcdheRsaSha256.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/Hazel-Networking/Hazel/Dtls/X25519EcdheRsaSha256.cs')
-rw-r--r--Tools/Hazel-Networking/Hazel/Dtls/X25519EcdheRsaSha256.cs202
1 files changed, 202 insertions, 0 deletions
diff --git a/Tools/Hazel-Networking/Hazel/Dtls/X25519EcdheRsaSha256.cs b/Tools/Hazel-Networking/Hazel/Dtls/X25519EcdheRsaSha256.cs
new file mode 100644
index 0000000..f567252
--- /dev/null
+++ b/Tools/Hazel-Networking/Hazel/Dtls/X25519EcdheRsaSha256.cs
@@ -0,0 +1,202 @@
+using Hazel.Crypto;
+using System;
+using System.Diagnostics;
+using System.Security.Cryptography;
+
+namespace Hazel.Dtls
+{
+ /// <summary>
+ /// ECDHE_RSA_*_256 cipher suite
+ /// </summary>
+ public class X25519EcdheRsaSha256 : IHandshakeCipherSuite
+ {
+ private readonly ByteSpan privateAgreementKey;
+ private SHA256 sha256 = SHA256.Create();
+
+ /// <summary>
+ /// Create a new instance of the x25519 key exchange
+ /// </summary>
+ /// <param name="random">Random data source</param>
+ public X25519EcdheRsaSha256(RandomNumberGenerator random)
+ {
+ byte[] buffer = new byte[X25519.KeySize];
+ random.GetBytes(buffer);
+ this.privateAgreementKey = buffer;
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ this.sha256?.Dispose();
+ this.sha256 = null;
+ }
+
+ /// <inheritdoc />
+ public int SharedKeySize()
+ {
+ return X25519.KeySize;
+ }
+
+ /// <summary>
+ /// Calculate the server message size given an RSA key size
+ /// </summary>
+ /// <param name="keySize">
+ /// Size of the private key (in bits)
+ /// </param>
+ /// <returns>
+ /// Size of the ServerKeyExchange message in bytes
+ /// </returns>
+ private static int CalculateServerMessageSize(int keySize)
+ {
+ int signatureSize = keySize / 8;
+
+ return 0
+ + 1 // ECCurveType ServerKeyExchange.params.curve_params.curve_type
+ + 2 // NamedCurve ServerKeyExchange.params.curve_params.namedcurve
+ + 1 + X25519.KeySize // ECPoint ServerKeyExchange.params.public
+ + 1 // HashAlgorithm ServerKeyExchange.algorithm.hash
+ + 1 // SignatureAlgorithm ServerKeyExchange.signed_params.algorithm.signature
+ + 2 // ServerKeyExchange.signed_params.size
+ + signatureSize // ServerKeyExchange.signed_params.opaque
+ ;
+ }
+
+ /// <inheritdoc />
+ public int CalculateServerMessageSize(object privateKey)
+ {
+ RSA rsaPrivateKey = privateKey as RSA;
+ if (rsaPrivateKey == null)
+ {
+ throw new ArgumentException("Invalid private key", nameof(privateKey));
+ }
+
+ return CalculateServerMessageSize(rsaPrivateKey.KeySize);
+ }
+
+ /// <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));
+ }
+
+ /// <inheritdoc />
+ public bool VerifyServerMessageAndGenerateSharedKey(ByteSpan output, ByteSpan serverKeyExchangeMessage, object publicKey)
+ {
+ RSA rsaPublicKey = publicKey as RSA;
+ if (rsaPublicKey == null)
+ {
+ return false;
+ }
+ else if (output.Length != X25519.KeySize)
+ {
+ return false;
+ }
+
+ // Verify message is compatible with this cipher suite
+ if (serverKeyExchangeMessage.Length != CalculateServerMessageSize(rsaPublicKey.KeySize))
+ {
+ return false;
+ }
+ else if (serverKeyExchangeMessage[0] != (byte)ECCurveType.NamedCurve)
+ {
+ return false;
+ }
+ else if (serverKeyExchangeMessage.ReadBigEndian16(1) != (ushort)NamedCurve.x25519)
+ {
+ return false;
+ }
+ else if (serverKeyExchangeMessage[3] != X25519.KeySize)
+ {
+ return false;
+ }
+ else if (serverKeyExchangeMessage[4 + X25519.KeySize] != (byte)HashAlgorithm.Sha256)
+ {
+ return false;
+ }
+ else if (serverKeyExchangeMessage[5 + X25519.KeySize] != (byte)SignatureAlgorithm.RSA)
+ {
+ return false;
+ }
+
+ ByteSpan keyParameters = serverKeyExchangeMessage.Slice(0, 4+X25519.KeySize);
+ ByteSpan othersPublicKey = keyParameters.Slice(4);
+ ushort signatureSize = serverKeyExchangeMessage.ReadBigEndian16(6 + X25519.KeySize);
+ ByteSpan signature = serverKeyExchangeMessage.Slice(4+keyParameters.Length);
+
+ if (signatureSize != signature.Length)
+ {
+ return false;
+ }
+
+ // Hash the key parameters
+ byte[] parameterDigest = this.sha256.ComputeHash(keyParameters.GetUnderlyingArray(), keyParameters.Offset, keyParameters.Length);
+
+ // Verify the signature
+ RSAPKCS1SignatureDeformatter verifier = new RSAPKCS1SignatureDeformatter(rsaPublicKey);
+ verifier.SetHashAlgorithm("SHA256");
+ if (!verifier.VerifySignature(parameterDigest, signature.ToArray()))
+ {
+ return false;
+ }
+
+ // Signature has been validated, generate the shared key
+ return X25519.Func(output, this.privateAgreementKey, othersPublicKey);
+ }
+
+ private static int ClientMessageSize = 0
+ + 1 + X25519.KeySize // ECPoint ClientKeyExchange.ecdh_Yc
+ ;
+
+ /// <inheritdoc />
+ public int CalculateClientMessageSize()
+ {
+ return ClientMessageSize;
+ }
+
+ /// <inheritdoc />
+ public void EncodeClientKeyExchangeMessage(ByteSpan output)
+ {
+ output[0] = (byte)X25519.KeySize;
+ X25519.Func(output.Slice(1, X25519.KeySize), this.privateAgreementKey);
+ }
+
+ /// <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);
+ }
+ }
+}