diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2023-03-16 13:11:30 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2023-04-13 17:16:19 +0700 |
commit | 931368e3fbf4611ed717f7cfe47bc884c4409876 (patch) | |
tree | 63ef941b75667dbea1f74b32321fe06ffa29191c | |
parent | RFC 9146: Add registry entries (diff) | |
download | BouncyCastle.NET-ed25519-931368e3fbf4611ed717f7cfe47bc884c4409876.tar.xz |
RFC 9146: connection_id extension negotiation
-rw-r--r-- | crypto/src/tls/AbstractTlsClient.cs | 28 | ||||
-rw-r--r-- | crypto/src/tls/AbstractTlsServer.cs | 26 | ||||
-rw-r--r-- | crypto/src/tls/DtlsClientProtocol.cs | 18 | ||||
-rw-r--r-- | crypto/src/tls/DtlsServerProtocol.cs | 18 | ||||
-rw-r--r-- | crypto/src/tls/SecurityParameters.cs | 9 | ||||
-rw-r--r-- | crypto/src/tls/TlsExtensionsUtilities.cs | 28 |
6 files changed, 126 insertions, 1 deletions
diff --git a/crypto/src/tls/AbstractTlsClient.cs b/crypto/src/tls/AbstractTlsClient.cs index 3061f3642..af53e9fbf 100644 --- a/crypto/src/tls/AbstractTlsClient.cs +++ b/crypto/src/tls/AbstractTlsClient.cs @@ -77,6 +77,15 @@ namespace Org.BouncyCastle.Tls throw new TlsFatalAlert(AlertDescription.illegal_parameter); } + /// <summary>RFC 9146 DTLS connection ID.</summary> + /// <remarks> + /// The default <see cref="GetClientExtensions"/> implementation calls this to get the connection_id extension + /// the client will send. As future communication doesn't include the connection IDs length, this should either + /// be fixed-length or include the connection ID's length. (see explanation in RFC 9146 4. "cid:") + /// </remarks> + /// <returns>The connection ID to use.</returns> + protected virtual byte[] GetNewConnectionID() => null; + /// <exception cref="IOException"/> public virtual TlsPskIdentity GetPskIdentity() { @@ -239,11 +248,13 @@ namespace Org.BouncyCastle.Tls bool offeringTlsV13Plus = false; bool offeringPreTlsV13 = false; + bool offeringDtlsV12 = false; { ProtocolVersion[] supportedVersions = GetProtocolVersions(); for (int i = 0; i < supportedVersions.Length; ++i) { - if (TlsUtilities.IsTlsV13(supportedVersions[i])) + var supportedVersion = supportedVersions[i]; + if (TlsUtilities.IsTlsV13(supportedVersion)) { offeringTlsV13Plus = true; } @@ -251,6 +262,8 @@ namespace Org.BouncyCastle.Tls { offeringPreTlsV13 = true; } + + offeringDtlsV12 |= ProtocolVersion.DTLSv12.Equals(supportedVersion); } } @@ -371,6 +384,19 @@ namespace Org.BouncyCastle.Tls TlsExtensionsUtilities.AddServerCertificateTypeExtensionClient(clientExtensions, serverCertTypes); } + if (offeringDtlsV12) + { + /* + * RFC 9146 3. When a DTLS session is resumed or renegotiated, the "connection_id" extension is + * negotiated afresh. + */ + var clientConnectionID = GetNewConnectionID(); + if (clientConnectionID != null) + { + TlsExtensionsUtilities.AddConnectionIDExtension(clientExtensions, clientConnectionID); + } + } + return clientExtensions; } diff --git a/crypto/src/tls/AbstractTlsServer.cs b/crypto/src/tls/AbstractTlsServer.cs index 3c62793b6..9f107d905 100644 --- a/crypto/src/tls/AbstractTlsServer.cs +++ b/crypto/src/tls/AbstractTlsServer.cs @@ -217,6 +217,16 @@ namespace Org.BouncyCastle.Tls return null; } + /// <summary>RFC 9146 DTLS connection ID.</summary> + /// <remarks> + /// This method will be called if a connection_id extension was sent by the client. + /// If the return value is non-null, the server will send this connection ID to the client to use in future packets. + /// As future communication doesn't include the connection IDs length, this should either be fixed-length + /// or include the connection ID's length. (see explanation in RFC 9146 4. "cid:") + /// </remarks> + /// <returns>The connection ID to use.</returns> + protected virtual byte[] GetNewConnectionID() => null; + public virtual void Init(TlsServerContext context) { this.m_context = context; @@ -587,6 +597,22 @@ namespace Org.BouncyCastle.Tls { TlsExtensionsUtilities.AddAlpnExtensionServer(serverExtensions, m_selectedProtocolName); } + + if (ProtocolVersion.DTLSv12.Equals(m_context.ServerVersion)) + { + /* + * RFC 9146 3. When a DTLS session is resumed or renegotiated, the "connection_id" extension is + * negotiated afresh. + */ + if (m_clientExtensions.ContainsKey(ExtensionType.connection_id)) + { + var serverConnectionID = GetNewConnectionID(); + if (serverConnectionID != null) + { + TlsExtensionsUtilities.AddConnectionIDExtension(m_serverExtensions, serverConnectionID); + } + } + } } public virtual IList<SupplementalDataEntry> GetServerSupplementalData() diff --git a/crypto/src/tls/DtlsClientProtocol.cs b/crypto/src/tls/DtlsClientProtocol.cs index 3b32c044e..88a077168 100644 --- a/crypto/src/tls/DtlsClientProtocol.cs +++ b/crypto/src/tls/DtlsClientProtocol.cs @@ -825,6 +825,24 @@ namespace Org.BouncyCastle.Tls state.serverExtensions); securityParameters.m_applicationProtocolSet = true; + // Connection ID + if (ProtocolVersion.DTLSv12.Equals(securityParameters.NegotiatedVersion)) + { + /* + * RFC 9146 3. When a DTLS session is resumed or renegotiated, the "connection_id" extension is + * negotiated afresh. + */ + var serverConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.serverExtensions); + if (serverConnectionID != null) + { + var clientConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.clientExtensions) + ?? throw new TlsFatalAlert(AlertDescription.internal_error); + + securityParameters.m_connectionIDLocal = serverConnectionID; + securityParameters.m_connectionIDPeer = clientConnectionID; + } + } + // Heartbeats { HeartbeatExtension heartbeatExtension = TlsExtensionsUtilities.GetHeartbeatExtension( diff --git a/crypto/src/tls/DtlsServerProtocol.cs b/crypto/src/tls/DtlsServerProtocol.cs index 3bf92395b..cf16bc302 100644 --- a/crypto/src/tls/DtlsServerProtocol.cs +++ b/crypto/src/tls/DtlsServerProtocol.cs @@ -549,6 +549,24 @@ namespace Org.BouncyCastle.Tls state.serverExtensions); securityParameters.m_applicationProtocolSet = true; + // Connection ID + if (ProtocolVersion.DTLSv12.Equals(securityParameters.NegotiatedVersion)) + { + /* + * RFC 9146 3. When a DTLS session is resumed or renegotiated, the "connection_id" extension is + * negotiated afresh. + */ + var serverConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.serverExtensions); + if (serverConnectionID != null) + { + var clientConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.clientExtensions) + ?? throw new TlsFatalAlert(AlertDescription.internal_error); + + securityParameters.m_connectionIDLocal = clientConnectionID; + securityParameters.m_connectionIDPeer = serverConnectionID; + } + } + /* * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore * extensions appearing in the client hello, and send a server hello containing no diff --git a/crypto/src/tls/SecurityParameters.cs b/crypto/src/tls/SecurityParameters.cs index 7775ca7c7..7deeeb72b 100644 --- a/crypto/src/tls/SecurityParameters.cs +++ b/crypto/src/tls/SecurityParameters.cs @@ -57,6 +57,11 @@ namespace Org.BouncyCastle.Tls internal byte[] m_localVerifyData = null; internal byte[] m_peerVerifyData = null; + /// <summary>Connection ID we use during communication to the peer.</summary> + internal byte[] m_connectionIDLocal; + /// <summary>Connection ID our peer uses for communication to us.</summary> + internal byte[] m_connectionIDPeer; + internal void Clear() { this.m_sessionHash = null; @@ -135,6 +140,10 @@ namespace Org.BouncyCastle.Tls get { return m_clientSupportedGroups; } } + public byte[] ConnectionIDLocal => m_connectionIDLocal; + + public byte[] ConnectionIDPeer => m_connectionIDPeer; + public TlsSecret EarlyExporterMasterSecret { get { return m_earlyExporterMasterSecret; } diff --git a/crypto/src/tls/TlsExtensionsUtilities.cs b/crypto/src/tls/TlsExtensionsUtilities.cs index 836c1b506..bc4ca2714 100644 --- a/crypto/src/tls/TlsExtensionsUtilities.cs +++ b/crypto/src/tls/TlsExtensionsUtilities.cs @@ -64,6 +64,12 @@ namespace Org.BouncyCastle.Tls } /// <exception cref="IOException"/> + public static void AddConnectionIDExtension(IDictionary<int, byte[]> extensions, byte[] connectionID) + { + extensions[ExtensionType.connection_id] = CreateConnectionIDExtension(connectionID); + } + + /// <exception cref="IOException"/> public static void AddCookieExtension(IDictionary<int, byte[]> extensions, byte[] cookie) { extensions[ExtensionType.cookie] = CreateCookieExtension(cookie); @@ -317,6 +323,13 @@ namespace Org.BouncyCastle.Tls } /// <exception cref="IOException"/> + public static byte[] GetConnectionIDExtension(IDictionary<int, byte[]> extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.connection_id); + return extensionData == null ? null : ReadConnectionIDExtension(extensionData); + } + + /// <exception cref="IOException"/> public static byte[] GetCookieExtension(IDictionary<int, byte[]> extensions) { byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.cookie); @@ -631,6 +644,15 @@ namespace Org.BouncyCastle.Tls } /// <exception cref="IOException"/> + public static byte[] CreateConnectionIDExtension(byte[] connectionID) + { + if (connectionID == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return TlsUtilities.EncodeOpaque8(connectionID); + } + + /// <exception cref="IOException"/> public static byte[] CreateCookieExtension(byte[] cookie) { if (TlsUtilities.IsNullOrEmpty(cookie) || cookie.Length >= (1 << 16)) @@ -1059,6 +1081,12 @@ namespace Org.BouncyCastle.Tls } /// <exception cref="IOException"/> + public static byte[] ReadConnectionIDExtension(byte[] extensionData) + { + return TlsUtilities.DecodeOpaque8(extensionData); + } + + /// <exception cref="IOException"/> public static byte[] ReadCookieExtension(byte[] extensionData) { return TlsUtilities.DecodeOpaque16(extensionData, 1); |