summary refs log tree commit diff
path: root/crypto/src/tls/DtlsVerifier.cs
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-05-17 18:16:36 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-05-17 18:16:36 +0700
commitd7557597d18a313c7e573b11e48ba8648d8a50a9 (patch)
treec22f9efc8bb7940e1961e4d2ee2e1a50a5027747 /crypto/src/tls/DtlsVerifier.cs
parentTLS: NotifyConnectionClosed after failure (diff)
downloadBouncyCastle.NET-ed25519-d7557597d18a313c7e573b11e48ba8648d8a50a9.tar.xz
DTLS: Improve DtlsVerifier performance
Diffstat (limited to 'crypto/src/tls/DtlsVerifier.cs')
-rw-r--r--crypto/src/tls/DtlsVerifier.cs108
1 files changed, 49 insertions, 59 deletions
diff --git a/crypto/src/tls/DtlsVerifier.cs b/crypto/src/tls/DtlsVerifier.cs
index e691685e6..01437d648 100644
--- a/crypto/src/tls/DtlsVerifier.cs
+++ b/crypto/src/tls/DtlsVerifier.cs
@@ -1,89 +1,79 @@
-using System;
-using System.IO;
+using System.IO;
 
+using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Tls.Crypto;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Tls
 {
+    /// <summary>
+    /// Implements cookie generation/verification for a DTLS server as described in RFC 4347,
+    /// 4.2.1. Denial of Service Countermeasures.
+    /// </summary>
+    /// <remarks>
+    /// RFC 4347 4.2.1 additionally recommends changing the secret frequently. This class does not handle that
+    /// internally, so the instance should be replaced instead.
+    /// </remarks>
     public class DtlsVerifier
     {
-        private static TlsMac CreateCookieMac(TlsCrypto crypto)
-        {
-            TlsMac mac = crypto.CreateHmac(MacAlgorithm.hmac_sha256);
-
-            byte[] secret = new byte[mac.MacLength];
-            crypto.SecureRandom.NextBytes(secret);
-
-            mac.SetKey(secret, 0, secret.Length);
-
-            return mac;
-        }
-
-        private readonly TlsMac m_cookieMac;
-        private readonly TlsMacSink m_cookieMacSink;
+        private readonly TlsCrypto m_crypto;
+        private readonly byte[] m_macKey;
 
         public DtlsVerifier(TlsCrypto crypto)
         {
-            this.m_cookieMac = CreateCookieMac(crypto);
-            this.m_cookieMacSink = new TlsMacSink(m_cookieMac);
+            m_crypto = crypto;
+            m_macKey = SecureRandom.GetNextBytes(crypto.SecureRandom, 32);
         }
 
         public virtual DtlsRequest VerifyRequest(byte[] clientID, byte[] data, int dataOff, int dataLen,
             DatagramSender sender)
         {
-            lock (this)
+            try
             {
-                bool resetCookieMac = true;
+                int msgLen = DtlsRecordLayer.ReceiveClientHelloRecord(data, dataOff, dataLen);
+                if (msgLen < 0)
+                    return null;
 
-                try
-                {
-                    m_cookieMac.Update(clientID, 0, clientID.Length);
+                int bodyLength = msgLen - DtlsReliableHandshake.MessageHeaderLength;
+                if (bodyLength < 39) // Minimum (syntactically) valid DTLS ClientHello length
+                    return null;
 
-                    DtlsRequest request = DtlsReliableHandshake.ReadClientRequest(data, dataOff, dataLen,
-                        m_cookieMacSink);
-                    if (null != request)
-                    {
-                        byte[] expectedCookie = m_cookieMac.CalculateMac();
-                        resetCookieMac = false;
+                int msgOff = dataOff + DtlsRecordLayer.RecordHeaderLength;
 
-                        // TODO Consider stricter HelloVerifyRequest protocol
-                        //switch (request.MessageSeq)
-                        //{
-                        //case 0:
-                        //{
-                        //    DtlsReliableHandshake.SendHelloVerifyRequest(sender, request.RecordSeq, expectedCookie);
-                        //    break;
-                        //}
-                        //case 1:
-                        //{
-                        //    if (Arrays.FixedTimeEquals(expectedCookie, request.ClientHello.Cookie))
-                        //        return request;
+                var buf = DtlsReliableHandshake.ReceiveClientHelloMessage(msg: data, msgOff, msgLen);
+                if (buf == null)
+                    return null;
 
-                        //    break;
-                        //}
-                        //}
+                var macInput = new MemoryStream(bodyLength);
+                ClientHello clientHello = ClientHello.Parse(buf, dtlsOutput: macInput);
+                if (clientHello == null)
+                    return null;
 
-                        if (Arrays.FixedTimeEquals(expectedCookie, request.ClientHello.Cookie))
-                            return request;
+                long recordSeq = TlsUtilities.ReadUint48(data, dataOff + 5);
 
-                        DtlsReliableHandshake.SendHelloVerifyRequest(sender, request.RecordSeq, expectedCookie);
-                    }
-                }
-                catch (IOException)
-                {
-                    // Ignore
-                }
-                finally
+                byte[] cookie = clientHello.Cookie;
+
+                TlsMac mac = m_crypto.CreateHmac(MacAlgorithm.hmac_sha256);
+                mac.SetKey(m_macKey, 0, m_macKey.Length);
+                mac.Update(clientID, 0, clientID.Length);
+                macInput.WriteTo(new TlsMacSink(mac));
+                byte[] expectedCookie = mac.CalculateMac();
+
+                if (Arrays.FixedTimeEquals(expectedCookie, cookie))
                 {
-                    if (resetCookieMac)
-                    {
-                        m_cookieMac.Reset();
-                    }
+                    byte[] message = TlsUtilities.CopyOfRangeExact(data, msgOff, msgOff + msgLen);
+
+                    return new DtlsRequest(recordSeq, message, clientHello);
                 }
 
-                return null;
+                DtlsReliableHandshake.SendHelloVerifyRequest(sender, recordSeq, expectedCookie);
+            }
+            catch (IOException)
+            {
+                // Ignore
             }
+
+            return null;
         }
     }
 }