}
}
+ class MalformedDTLSClient : DtlsUnityConnection
+ {
+ public MalformedDTLSClient(ILogger logger, IPEndPoint remoteEndPoint, IPMode ipMode = IPMode.IPv4) : base(logger, remoteEndPoint, ipMode)
+ {
+
+ }
+
+ protected override void SendClientHello()
+ {
+ Test_SendClientHello((clientHello, writer) =>
+ {
+ ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)ProtocolVersion.DTLS1_2);
+ writer = writer.Slice(2);
+
+ clientHello.Random.CopyTo(writer);
+ writer = writer.Slice(Hazel.Dtls.Random.Size);
+
+ // Do not encode session ids
+ writer[0] = (byte)0;
+ writer = writer.Slice(1);
+
+ writer[0] = (byte)clientHello.Cookie.Length;
+ clientHello.Cookie.CopyTo(writer.Slice(1));
+ writer = writer.Slice(1 + clientHello.Cookie.Length);
+
+ ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)clientHello.CipherSuites.Length);
+ clientHello.CipherSuites.CopyTo(writer.Slice(2));
+ writer = writer.Slice(2 + clientHello.CipherSuites.Length);
+
+ // ============ Here is the corruption. writer[0] should be 1. ============
+ writer[0] = 255;
+ writer[1] = (byte)CompressionMethod.Null;
+ writer = writer.Slice(2);
+
+ // Extensions size
+ ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)(6 + clientHello.SupportedCurves.Length));
+ writer = writer.Slice(2);
+
+ // Supported curves extension
+ ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)ExtensionType.EllipticCurves);
+ ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)(2 + clientHello.SupportedCurves.Length), 2);
+ ByteSpanBigEndianExtensions.WriteBigEndian16(writer, (ushort)clientHello.SupportedCurves.Length, 4);
+ clientHello.SupportedCurves.CopyTo(writer.Slice(6));
+
+ return writer;
+ });
+ }
+ }
+
[TestMethod]
public void TestMalformedApplicationData()
{
}
}
+ [TestMethod]
+ public void TestMalformedConnectionData()
+ {
+ IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 27510);
+
+ IPEndPoint connectionEndPoint = ep;
+ DtlsConnectionListener.ConnectionId connectionId = new ThreadLimitedUdpConnectionListener.ConnectionId();
+
+ Semaphore signal = new Semaphore(0, int.MaxValue);
+
+ using (DtlsConnectionListener listener = new DtlsConnectionListener(2, new IPEndPoint(IPAddress.Any, ep.Port), new TestLogger()))
+ using (MalformedDTLSClient connection = new MalformedDTLSClient(new TestLogger(), ep))
+ {
+ listener.SetCertificate(GetCertificateForServer());
+ connection.SetValidServerCertificates(GetCertificateForClient());
+
+ listener.NewConnection += (evt) =>
+ {
+ connectionEndPoint = evt.Connection.EndPoint;
+ connectionId = ((ThreadLimitedUdpServerConnection)evt.Connection).ConnectionId;
+
+ signal.Release();
+ evt.Connection.Disconnected += (o, et) => {
+ };
+ };
+ connection.Disconnected += (o, evt) => {
+ signal.Release();
+ };
+
+ listener.Start();
+ connection.Connect();
+
+ Assert.IsTrue(listener.ReceiveThreadRunning, "Listener should be able to handle a malformed hello packet");
+ Assert.AreEqual(ConnectionState.NotConnected, connection.State);
+
+ // wait for the client to disconnect
+ listener.Dispose();
+ signal.WaitOne(100);
+ }
+ }
+
[TestMethod]
public void TestResentHandshakeConnects()
{
/// <summary>
/// Send (resend) a ClientHello message to the server
/// </summary>
- private void SendClientHello()
+ protected virtual void SendClientHello()
{
// Reset our verification stream
this.nextEpoch.VerificationStream.Reset();
base.WriteBytesToConnection(packet.GetUnderlyingArray(), packet.Length);
}
+ protected void Test_SendClientHello(Func<ClientHello, ByteSpan, ByteSpan> encodeCallback)
+ {
+ // Reset our verification stream
+ this.nextEpoch.VerificationStream.Reset();
+
+ // Describe our ClientHello flight
+ ClientHello clientHello = new ClientHello();
+ clientHello.Random = this.nextEpoch.ClientRandom;
+ clientHello.Cookie = this.nextEpoch.Cookie;
+ clientHello.CipherSuites = new byte[2];
+ clientHello.CipherSuites.WriteBigEndian16((ushort)CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
+ clientHello.SupportedCurves = new byte[2];
+ clientHello.SupportedCurves.WriteBigEndian16((ushort)NamedCurve.x25519);
+
+ Handshake handshake = new Handshake();
+ handshake.MessageType = HandshakeType.ClientHello;
+ handshake.Length = (uint)clientHello.CalculateSize();
+ handshake.MessageSequence = 0;
+ handshake.FragmentOffset = 0;
+ handshake.FragmentLength = handshake.Length;
+
+ // Describe the record
+ int plaintextLength = (int)(Handshake.Size + handshake.Length);
+ Record outgoingRecord = new Record();
+ outgoingRecord.ContentType = ContentType.Handshake;
+ outgoingRecord.Epoch = this.epoch;
+ outgoingRecord.SequenceNumber = this.currentEpoch.NextOutgoingSequence;
+ outgoingRecord.Length = (ushort)this.currentEpoch.RecordProtection.GetEncryptedSize(plaintextLength);
+ ++this.currentEpoch.NextOutgoingSequence;
+
+ // Convert the record to wire format
+ ByteSpan packet = new byte[Record.Size + outgoingRecord.Length];
+ ByteSpan writer = packet;
+ outgoingRecord.Encode(packet);
+ writer = writer.Slice(Record.Size);
+ handshake.Encode(writer);
+ writer = writer.Slice(Handshake.Size);
+
+ writer = encodeCallback(clientHello, writer);
+
+ // Write ClientHello to the verification stream
+ this.nextEpoch.VerificationStream.AddData(
+ packet.Slice(
+ Record.Size
+ , Handshake.Size + (int)handshake.Length
+ )
+ );
+
+ // Protect the record
+ this.currentEpoch.RecordProtection.EncryptClientPlaintext(
+ packet.Slice(Record.Size, outgoingRecord.Length)
+ , packet.Slice(Record.Size, plaintextLength)
+ , ref outgoingRecord
+ );
+
+ this.nextEpoch.State = HandshakeState.ExpectingServerHello;
+ this.nextEpoch.NextPacketResendTime = DateTime.UtcNow + this.handshakeResendTimeout;
+ base.WriteBytesToConnection(packet.GetUnderlyingArray(), packet.Length);
+ }
+
/// <summary>
/// Send (resend) the ClientKeyExchange flight
/// </summary>