diff --git a/crypto/src/crypto/tls/AbstractTlsClient.cs b/crypto/src/crypto/tls/AbstractTlsClient.cs
index 9484afa7d..9944b0b18 100644
--- a/crypto/src/crypto/tls/AbstractTlsClient.cs
+++ b/crypto/src/crypto/tls/AbstractTlsClient.cs
@@ -67,6 +67,16 @@ namespace Org.BouncyCastle.Crypto.Tls
get { return ProtocolVersion.TLSv12; }
}
+ public virtual bool IsFallback
+ {
+ /*
+ * draft-bmoeller-tls-downgrade-scsv-02 4. [..] is meant for use by clients that repeat a
+ * connection attempt with a downgraded protocol in order to avoid interoperability problems
+ * with legacy servers.
+ */
+ get { return false; }
+ }
+
public virtual IDictionary GetClientExtensions()
{
IDictionary clientExtensions = null;
diff --git a/crypto/src/crypto/tls/AbstractTlsServer.cs b/crypto/src/crypto/tls/AbstractTlsServer.cs
index c2c6fd57c..6cd9a881e 100644
--- a/crypto/src/crypto/tls/AbstractTlsServer.cs
+++ b/crypto/src/crypto/tls/AbstractTlsServer.cs
@@ -110,6 +110,18 @@ namespace Org.BouncyCastle.Crypto.Tls
this.mClientVersion = clientVersion;
}
+ public virtual void NotifyFallback(bool isFallback)
+ {
+ /*
+ * draft-bmoeller-tls-downgrade-scsv-02 3. If TLS_FALLBACK_SCSV appears in
+ * ClientHello.cipher_suites and the highest protocol version supported by the server is
+ * higher than the version indicated in ClientHello.client_version, the server MUST respond
+ * with an inappropriate_fallback alert.
+ */
+ if (isFallback && MaximumVersion.IsLaterVersionOf(mClientVersion))
+ throw new TlsFatalAlert(AlertDescription.inappropriate_fallback);
+ }
+
public virtual void NotifyOfferedCipherSuites(int[] offeredCipherSuites)
{
this.mOfferedCipherSuites = offeredCipherSuites;
diff --git a/crypto/src/crypto/tls/AlertDescription.cs b/crypto/src/crypto/tls/AlertDescription.cs
index 5b6e88bf7..ab6924567 100644
--- a/crypto/src/crypto/tls/AlertDescription.cs
+++ b/crypto/src/crypto/tls/AlertDescription.cs
@@ -214,6 +214,17 @@ namespace Org.BouncyCastle.Crypto.Tls
*/
public const byte unknown_psk_identity = 115;
+ /*
+ * draft-bmoeller-tls-downgrade-scsv-02
+ */
+
+ /**
+ * If TLS_FALLBACK_SCSV appears in ClientHello.cipher_suites and the highest protocol version
+ * supported by the server is higher than the version indicated in ClientHello.client_version,
+ * the server MUST respond with an inappropriate_fallback alert.
+ */
+ public const byte inappropriate_fallback = 86;
+
public static string GetName(byte alertDescription)
{
switch (alertDescription)
@@ -278,6 +289,8 @@ namespace Org.BouncyCastle.Crypto.Tls
return "bad_certificate_hash_value";
case unknown_psk_identity:
return "unknown_psk_identity";
+ case inappropriate_fallback:
+ return "inappropriate_fallback";
default:
return "UNKNOWN";
}
diff --git a/crypto/src/crypto/tls/CipherSuite.cs b/crypto/src/crypto/tls/CipherSuite.cs
index f034ab802..540b5d18d 100644
--- a/crypto/src/crypto/tls/CipherSuite.cs
+++ b/crypto/src/crypto/tls/CipherSuite.cs
@@ -348,5 +348,22 @@ namespace Org.BouncyCastle.Crypto.Tls
public const int TLS_DHE_PSK_WITH_SALSA20_SHA1 = 0xE41D;
public const int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE41E;
public const int TLS_DHE_RSA_WITH_SALSA20_SHA1 = 0xE41F;
+
+ /*
+ * draft-bmoeller-tls-downgrade-scsv-02
+ */
+ public const int TLS_FALLBACK_SCSV = 0x5600;
+
+ public static bool IsScsv(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ case TLS_EMPTY_RENEGOTIATION_INFO_SCSV:
+ case TLS_FALLBACK_SCSV:
+ return true;
+ default:
+ return false;
+ }
+ }
}
}
diff --git a/crypto/src/crypto/tls/TlsClient.cs b/crypto/src/crypto/tls/TlsClient.cs
index cd5dfad13..116f6a779 100644
--- a/crypto/src/crypto/tls/TlsClient.cs
+++ b/crypto/src/crypto/tls/TlsClient.cs
@@ -27,6 +27,8 @@ namespace Org.BouncyCastle.Crypto.Tls
ProtocolVersion ClientVersion { get; }
+ bool IsFallback { get; }
+
/// <summary>
/// Get the list of cipher suites that this client supports.
/// </summary>
diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs
index 9fe50add8..ea8a1b2e4 100644
--- a/crypto/src/crypto/tls/TlsClientProtocol.cs
+++ b/crypto/src/crypto/tls/TlsClientProtocol.cs
@@ -607,7 +607,7 @@ namespace Org.BouncyCastle.Crypto.Tls
int selectedCipherSuite = TlsUtilities.ReadUint16(buf);
if (!Arrays.Contains(this.mOfferedCipherSuites, selectedCipherSuite)
|| selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
- || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ || CipherSuite.IsScsv(selectedCipherSuite)
|| !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, server_version))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
@@ -815,6 +815,8 @@ namespace Org.BouncyCastle.Crypto.Tls
}
}
+ bool fallback = this.mTlsClient.IsFallback;
+
this.mOfferedCipherSuites = this.mTlsClient.GetCipherSuites();
this.mOfferedCompressionMethods = this.mTlsClient.GetCompressionMethods();
@@ -850,9 +852,9 @@ namespace Org.BouncyCastle.Crypto.Tls
byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info);
bool noRenegExt = (null == renegExtData);
- bool noSCSV = !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+ bool noRenegScsv = !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
- if (noRenegExt && noSCSV)
+ if (noRenegExt && noRenegScsv)
{
// TODO Consider whether to default to a client extension instead
// this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mClientExtensions);
@@ -860,6 +862,17 @@ namespace Org.BouncyCastle.Crypto.Tls
this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
}
+ /*
+ * draft-bmoeller-tls-downgrade-scsv-02 4. If a client sends a
+ * ClientHello.client_version containing a lower value than the latest (highest-valued)
+ * version supported by the client, it SHOULD include the TLS_FALLBACK_SCSV cipher suite
+ * value in ClientHello.cipher_suites.
+ */
+ if (fallback && !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV))
+ {
+ this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV);
+ }
+
TlsUtilities.WriteUint16ArrayWithUint16Length(mOfferedCipherSuites, message);
}
diff --git a/crypto/src/crypto/tls/TlsServer.cs b/crypto/src/crypto/tls/TlsServer.cs
index 93e62b9ac..e791f93a9 100644
--- a/crypto/src/crypto/tls/TlsServer.cs
+++ b/crypto/src/crypto/tls/TlsServer.cs
@@ -13,6 +13,9 @@ namespace Org.BouncyCastle.Crypto.Tls
void NotifyClientVersion(ProtocolVersion clientVersion);
/// <exception cref="IOException"></exception>
+ void NotifyFallback(bool isFallback);
+
+ /// <exception cref="IOException"></exception>
void NotifyOfferedCipherSuites(int[] offeredCipherSuites);
/// <exception cref="IOException"></exception>
diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs
index 165d6a147..b1fb830b6 100644
--- a/crypto/src/crypto/tls/TlsServerProtocol.cs
+++ b/crypto/src/crypto/tls/TlsServerProtocol.cs
@@ -453,6 +453,8 @@ namespace Org.BouncyCastle.Crypto.Tls
protected virtual void ReceiveClientHelloMessage(MemoryStream buf)
{
ProtocolVersion client_version = TlsUtilities.ReadVersion(buf);
+ mRecordStream.SetWriteVersion(client_version);
+
if (client_version.IsDtls)
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
@@ -499,6 +501,7 @@ namespace Org.BouncyCastle.Crypto.Tls
ContextAdmin.SetClientVersion(client_version);
mTlsServer.NotifyClientVersion(client_version);
+ mTlsServer.NotifyFallback(Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV));
mSecurityParameters.clientRandom = client_random;
@@ -626,7 +629,7 @@ namespace Org.BouncyCastle.Crypto.Tls
int selectedCipherSuite = mTlsServer.GetSelectedCipherSuite();
if (!Arrays.Contains(mOfferedCipherSuites, selectedCipherSuite)
|| selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
- || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ || CipherSuite.IsScsv(selectedCipherSuite)
|| !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, server_version))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
|