diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2015-12-16 22:50:41 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2015-12-16 22:50:41 +0700 |
commit | ae227e65ab48b8747c80e1b336a3e6896e9e346e (patch) | |
tree | bff90e085f89872399328db0391f9e8b4f7368f0 /crypto | |
parent | Update version for release (diff) | |
download | BouncyCastle.NET-ed25519-ae227e65ab48b8747c80e1b336a3e6896e9e346e.tar.xz |
Validate CertificateVerify signature algorithm (TLS 1.2+)
- check the algorithm is in the CertificateRequest list - add (D)TLS test scenarios for various failure modes
Diffstat (limited to 'crypto')
18 files changed, 426 insertions, 74 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index e6ab94ee2..e4bd2e036 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -11840,6 +11840,16 @@ BuildAction = "Compile" /> <File + RelPath = "test\src\crypto\tls\test\DtlsTestClientProtocol.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "test\src\crypto\tls\test\DtlsTestServerProtocol.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "test\src\crypto\tls\test\DtlsTestSuite.cs" SubType = "Code" BuildAction = "Compile" @@ -11950,6 +11960,11 @@ BuildAction = "Compile" /> <File + RelPath = "test\src\crypto\tls\test\TlsTestClientProtocol.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "test\src\crypto\tls\test\TlsTestConfig.cs" SubType = "Code" BuildAction = "Compile" @@ -11960,6 +11975,11 @@ BuildAction = "Compile" /> <File + RelPath = "test\src\crypto\tls\test\TlsTestServerProtocol.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "test\src\crypto\tls\test\TlsTestSuite.cs" SubType = "Code" BuildAction = "Compile" diff --git a/crypto/src/crypto/tls/DeferredHash.cs b/crypto/src/crypto/tls/DeferredHash.cs index 1112d4a3c..f402f26d2 100644 --- a/crypto/src/crypto/tls/DeferredHash.cs +++ b/crypto/src/crypto/tls/DeferredHash.cs @@ -103,7 +103,7 @@ namespace Org.BouncyCastle.Crypto.Tls { IDigest d = (IDigest)mHashes[hashAlgorithm]; if (d == null) - throw new InvalidOperationException("HashAlgorithm " + hashAlgorithm + " is not being tracked"); + throw new InvalidOperationException("HashAlgorithm." + HashAlgorithm.GetText(hashAlgorithm) + " is not being tracked"); d = TlsUtilities.CloneHash(hashAlgorithm, d); if (mBuf != null) diff --git a/crypto/src/crypto/tls/DtlsServerProtocol.cs b/crypto/src/crypto/tls/DtlsServerProtocol.cs index 9c7caf290..e2e9eddfc 100644 --- a/crypto/src/crypto/tls/DtlsServerProtocol.cs +++ b/crypto/src/crypto/tls/DtlsServerProtocol.cs @@ -458,6 +458,9 @@ namespace Org.BouncyCastle.Crypto.Tls protected virtual void ProcessCertificateVerify(ServerHandshakeState state, byte[] body, TlsHandshakeHash prepareFinishHash) { + if (state.certificateRequest == null) + throw new InvalidOperationException(); + MemoryStream buf = new MemoryStream(body, false); TlsServerContextImpl context = state.serverContext; @@ -466,13 +469,15 @@ namespace Org.BouncyCastle.Crypto.Tls TlsProtocol.AssertEmpty(buf); // Verify the CertificateVerify message contains a correct signature. - bool verified = false; try { + SignatureAndHashAlgorithm signatureAlgorithm = clientCertificateVerify.Algorithm; + byte[] hash; if (TlsUtilities.IsTlsV12(context)) { - hash = prepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash); + TlsUtilities.VerifySupportedSignatureAlgorithm(state.certificateRequest.SupportedSignatureAlgorithms, signatureAlgorithm); + hash = prepareFinishHash.GetFinalHash(signatureAlgorithm.Hash); } else { @@ -485,15 +490,17 @@ namespace Org.BouncyCastle.Crypto.Tls TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)state.clientCertificateType); tlsSigner.Init(context); - verified = tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm, - clientCertificateVerify.Signature, publicKey, hash); + if (!tlsSigner.VerifyRawSignature(signatureAlgorithm, clientCertificateVerify.Signature, publicKey, hash)) + throw new TlsFatalAlert(AlertDescription.decrypt_error); } - catch (Exception) + catch (TlsFatalAlert e) { + throw e; + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error, e); } - - if (!verified) - throw new TlsFatalAlert(AlertDescription.decrypt_error); } protected virtual void ProcessClientHello(ServerHandshakeState state, byte[] body) diff --git a/crypto/src/crypto/tls/HashAlgorithm.cs b/crypto/src/crypto/tls/HashAlgorithm.cs index ac6def26f..ff540d2e0 100644 --- a/crypto/src/crypto/tls/HashAlgorithm.cs +++ b/crypto/src/crypto/tls/HashAlgorithm.cs @@ -12,5 +12,33 @@ namespace Org.BouncyCastle.Crypto.Tls public const byte sha256 = 4; public const byte sha384 = 5; public const byte sha512 = 6; + + public static string GetName(byte hashAlgorithm) + { + switch (hashAlgorithm) + { + case none: + return "none"; + case md5: + return "md5"; + case sha1: + return "sha1"; + case sha224: + return "sha224"; + case sha256: + return "sha256"; + case sha384: + return "sha384"; + case sha512: + return "sha512"; + default: + return "UNKNOWN"; + } + } + + public static string GetText(byte hashAlgorithm) + { + return GetName(hashAlgorithm) + "(" + hashAlgorithm + ")"; + } } } diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs index 4ab628b13..5716c0cd1 100644 --- a/crypto/src/crypto/tls/TlsServerProtocol.cs +++ b/crypto/src/crypto/tls/TlsServerProtocol.cs @@ -460,6 +460,9 @@ namespace Org.BouncyCastle.Crypto.Tls protected virtual void ReceiveCertificateVerifyMessage(MemoryStream buf) { + if (mCertificateRequest == null) + throw new InvalidOperationException(); + DigitallySigned clientCertificateVerify = DigitallySigned.Parse(Context, buf); AssertEmpty(buf); @@ -467,10 +470,13 @@ namespace Org.BouncyCastle.Crypto.Tls // Verify the CertificateVerify message contains a correct signature. try { + SignatureAndHashAlgorithm signatureAlgorithm = clientCertificateVerify.Algorithm; + byte[] hash; if (TlsUtilities.IsTlsV12(Context)) { - hash = mPrepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash); + TlsUtilities.VerifySupportedSignatureAlgorithm(mCertificateRequest.SupportedSignatureAlgorithms, signatureAlgorithm); + hash = mPrepareFinishHash.GetFinalHash(signatureAlgorithm.Hash); } else { @@ -483,11 +489,12 @@ namespace Org.BouncyCastle.Crypto.Tls TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)mClientCertificateType); tlsSigner.Init(Context); - if (!tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm, - clientCertificateVerify.Signature, publicKey, hash)) - { + if (!tlsSigner.VerifyRawSignature(signatureAlgorithm, clientCertificateVerify.Signature, publicKey, hash)) throw new TlsFatalAlert(AlertDescription.decrypt_error); - } + } + catch (TlsFatalAlert e) + { + throw e; } catch (Exception e) { diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs index 26fb0d5e8..7d1d488d7 100644 --- a/crypto/src/crypto/tls/TlsUtilities.cs +++ b/crypto/src/crypto/tls/TlsUtilities.cs @@ -129,14 +129,24 @@ namespace Org.BouncyCastle.Crypto.Tls return context.ServerVersion.IsSsl; } + public static bool IsTlsV11(ProtocolVersion version) + { + return ProtocolVersion.TLSv11.IsEqualOrEarlierVersionOf(version.GetEquivalentTLSVersion()); + } + public static bool IsTlsV11(TlsContext context) { - return ProtocolVersion.TLSv11.IsEqualOrEarlierVersionOf(context.ServerVersion.GetEquivalentTLSVersion()); + return IsTlsV11(context.ServerVersion); + } + + public static bool IsTlsV12(ProtocolVersion version) + { + return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(version.GetEquivalentTLSVersion()); } public static bool IsTlsV12(TlsContext context) { - return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(context.ServerVersion.GetEquivalentTLSVersion()); + return IsTlsV12(context.ServerVersion); } public static void WriteUint8(byte i, Stream output) @@ -712,11 +722,10 @@ namespace Org.BouncyCastle.Crypto.Tls public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, bool allowAnonymous, Stream output) { - if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.Count < 1 - || supportedSignatureAlgorithms.Count >= (1 << 15)) - { + if (supportedSignatureAlgorithms == null) + throw new ArgumentNullException("supportedSignatureAlgorithms"); + if (supportedSignatureAlgorithms.Count < 1 || supportedSignatureAlgorithms.Count >= (1 << 15)) throw new ArgumentException("must have length from 1 to (2^15 - 1)", "supportedSignatureAlgorithms"); - } // supported_signature_algorithms int length = 2 * supportedSignatureAlgorithms.Count; @@ -762,6 +771,27 @@ namespace Org.BouncyCastle.Crypto.Tls return supportedSignatureAlgorithms; } + public static void VerifySupportedSignatureAlgorithm(IList supportedSignatureAlgorithms, SignatureAndHashAlgorithm signatureAlgorithm) + { + if (supportedSignatureAlgorithms == null) + throw new ArgumentNullException("supportedSignatureAlgorithms"); + if (supportedSignatureAlgorithms.Count < 1 || supportedSignatureAlgorithms.Count >= (1 << 15)) + throw new ArgumentException("must have length from 1 to (2^15 - 1)", "supportedSignatureAlgorithms"); + if (signatureAlgorithm == null) + throw new ArgumentNullException("signatureAlgorithm"); + + if (signatureAlgorithm.Signature != SignatureAlgorithm.anonymous) + { + foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms) + { + if (entry.Hash == signatureAlgorithm.Hash && entry.Signature == signatureAlgorithm.Signature) + return; + } + } + + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + public static byte[] PRF(TlsContext context, byte[] secret, string asciiLabel, byte[] seed, int size) { ProtocolVersion version = context.ServerVersion; diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj index 71896d203..24a60346e 100644 --- a/crypto/test/UnitTests.csproj +++ b/crypto/test/UnitTests.csproj @@ -277,6 +277,8 @@ <Compile Include="src\crypto\tls\test\ByteQueueStreamTest.cs" /> <Compile Include="src\crypto\tls\test\DtlsProtocolTest.cs" /> <Compile Include="src\crypto\tls\test\DtlsTestCase.cs" /> + <Compile Include="src\crypto\tls\test\DtlsTestClientProtocol.cs" /> + <Compile Include="src\crypto\tls\test\DtlsTestServerProtocol.cs" /> <Compile Include="src\crypto\tls\test\DtlsTestSuite.cs" /> <Compile Include="src\crypto\tls\test\LoggingDatagramTransport.cs" /> <Compile Include="src\crypto\tls\test\MockDatagramAssociation.cs" /> @@ -299,8 +301,10 @@ <Compile Include="src\crypto\tls\test\TlsSrpProtocolTest.cs" /> <Compile Include="src\crypto\tls\test\TlsTestCase.cs" /> <Compile Include="src\crypto\tls\test\TlsTestClientImpl.cs" /> + <Compile Include="src\crypto\tls\test\TlsTestClientProtocol.cs" /> <Compile Include="src\crypto\tls\test\TlsTestConfig.cs" /> <Compile Include="src\crypto\tls\test\TlsTestServerImpl.cs" /> + <Compile Include="src\crypto\tls\test\TlsTestServerProtocol.cs" /> <Compile Include="src\crypto\tls\test\TlsTestSuite.cs" /> <Compile Include="src\crypto\tls\test\TlsTestUtilities.cs" /> <Compile Include="src\crypto\tls\test\UnreliableDatagramTransport.cs" /> diff --git a/crypto/test/src/crypto/tls/test/DtlsTestCase.cs b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs index d4af04fac..5e43337f4 100644 --- a/crypto/test/src/crypto/tls/test/DtlsTestCase.cs +++ b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs @@ -28,8 +28,8 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests SecureRandom secureRandom = new SecureRandom(); - DtlsClientProtocol clientProtocol = new DtlsClientProtocol(secureRandom); - DtlsServerProtocol serverProtocol = new DtlsServerProtocol(secureRandom); + DtlsTestClientProtocol clientProtocol = new DtlsTestClientProtocol(secureRandom, config); + DtlsTestServerProtocol serverProtocol = new DtlsTestServerProtocol(secureRandom, config); MockDatagramAssociation network = new MockDatagramAssociation(1500); @@ -101,14 +101,15 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests internal class Server { private readonly DtlsTestCase mOuter; - private readonly DtlsServerProtocol mServerProtocol; + private readonly DtlsTestServerProtocol mServerProtocol; private readonly DatagramTransport mServerTransport; private readonly TlsTestServerImpl mServerImpl; private volatile bool isShutdown = false; internal Exception mCaught = null; - internal Server(DtlsTestCase outer, DtlsServerProtocol serverProtocol, DatagramTransport serverTransport, TlsTestServerImpl serverImpl) + internal Server(DtlsTestCase outer, DtlsTestServerProtocol serverProtocol, + DatagramTransport serverTransport, TlsTestServerImpl serverImpl) { this.mOuter = outer; this.mServerProtocol = serverProtocol; diff --git a/crypto/test/src/crypto/tls/test/DtlsTestClientProtocol.cs b/crypto/test/src/crypto/tls/test/DtlsTestClientProtocol.cs new file mode 100644 index 000000000..41ed93eb0 --- /dev/null +++ b/crypto/test/src/crypto/tls/test/DtlsTestClientProtocol.cs @@ -0,0 +1,28 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls.Tests +{ + internal class DtlsTestClientProtocol + : DtlsClientProtocol + { + protected readonly TlsTestConfig config; + + public DtlsTestClientProtocol(SecureRandom secureRandom, TlsTestConfig config) + : base(secureRandom) + { + this.config = config; + } + + protected override byte[] GenerateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify) + { + if (certificateVerify.Algorithm != null && config.clientAuthSigAlgClaimed != null) + { + certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.Signature); + } + + return base.GenerateCertificateVerify(state, certificateVerify); + } + } +} diff --git a/crypto/test/src/crypto/tls/test/DtlsTestServerProtocol.cs b/crypto/test/src/crypto/tls/test/DtlsTestServerProtocol.cs new file mode 100644 index 000000000..006473cef --- /dev/null +++ b/crypto/test/src/crypto/tls/test/DtlsTestServerProtocol.cs @@ -0,0 +1,18 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls.Tests +{ + internal class DtlsTestServerProtocol + : DtlsServerProtocol + { + protected readonly TlsTestConfig config; + + public DtlsTestServerProtocol(SecureRandom secureRandom, TlsTestConfig config) + : base(secureRandom) + { + this.config = config; + } + } +} diff --git a/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs index eb9d42e5f..69ce36071 100644 --- a/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs +++ b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs @@ -31,7 +31,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12); c.clientFallback = true; - testSuite.Add(new TestCaseData(c).SetName("FallbackGood")); + AddTestCase(testSuite, c, "FallbackGood"); } /* @@ -40,20 +40,22 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests * alerts being raised */ - //{ - // TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12); - // c.clientOfferVersion = ProtocolVersion.DTLSv10; - // c.clientFallback = true; - // c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback); +#if false + { + TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12); + c.clientOfferVersion = ProtocolVersion.DTLSv10; + c.clientFallback = true; + c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback); - // testSuite.Add(new TestCaseData(c).SetName("FallbackBad")); - //} + AddTestCase(testSuite, c, "FallbackBad"); + } +#endif { TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12); c.clientOfferVersion = ProtocolVersion.DTLSv10; - testSuite.Add(new TestCaseData(c).SetName("FallbackNone")); + AddTestCase(testSuite, c, "FallbackNone"); } } @@ -71,52 +73,110 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests * alerts being raised */ - //{ - // TlsTestConfig c = CreateDtlsTestConfig(version); - // c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY; - // c.ExpectServerFatalAlert(AlertDescription.decrypt_error); +#if false + /* + * Server only declares support for SHA1/RSA, client selects MD5/RSA. Since the client is + * NOT actually tracking MD5 over the handshake, we expect fatal alert from the client. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_VALID; + c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa); + c.serverCertReqSigAlgs = TlsUtilities.GetDefaultRsaSignatureAlgorithms(); + c.ExpectClientFatalAlert(AlertDescription.internal_error); - // testSuite.Add(new TestCaseData(c).SetName(prefix + "BadCertificateVerify")); - //} + AddTestCase(testSuite, c, prefix + "BadCertificateVerifyHashAlg"); + } - //{ - // TlsTestConfig c = CreateDtlsTestConfig(version); - // c.clientAuth = C.CLIENT_AUTH_INVALID_CERT; - // c.ExpectServerFatalAlert(AlertDescription.bad_certificate); + /* + * Server only declares support for SHA1/ECDSA, client selects SHA1/RSA. Since the client is + * actually tracking SHA1 over the handshake, we expect fatal alert to come from the server + * when it verifies the selected algorithm against the CertificateRequest supported + * algorithms. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_VALID; + c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa); + c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms(); + c.ExpectServerFatalAlert(AlertDescription.illegal_parameter); - // testSuite.Add(new TestCaseData(c).SetName(prefix + "BadClientCertificate")); - //} + AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlg"); + } - //{ - // TlsTestConfig c = CreateDtlsTestConfig(version); - // c.clientAuth = C.CLIENT_AUTH_NONE; - // c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY; - // c.ExpectServerFatalAlert(AlertDescription.handshake_failure); + /* + * Server only declares support for SHA1/ECDSA, client signs with SHA1/RSA, but sends + * SHA1/ECDSA in the CertificateVerify. Since the client is actually tracking SHA1 over the + * handshake, and the claimed algorithm is in the CertificateRequest supported algorithms, + * we expect fatal alert to come from the server when it finds the claimed algorithm + * doesn't match the client certificate. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_VALID; + c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa); + c.clientAuthSigAlgClaimed = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa); + c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms(); + c.ExpectServerFatalAlert(AlertDescription.decrypt_error); - // testSuite.Add(new TestCaseData(c).SetName(prefix + "BadMandatoryCertReqDeclined")); - //} + AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch"); + } { TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY; + c.ExpectServerFatalAlert(AlertDescription.decrypt_error); - testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodDefault")); + AddTestCase(testSuite, c, prefix + "BadCertificateVerifySignature"); + } + + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_INVALID_CERT; + c.ExpectServerFatalAlert(AlertDescription.bad_certificate); + + AddTestCase(testSuite, c, prefix + "BadClientCertificate"); + } + + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_NONE; + c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY; + c.ExpectServerFatalAlert(AlertDescription.handshake_failure); + + AddTestCase(testSuite, c, prefix + "BadMandatoryCertReqDeclined"); + } +#endif + + { + TlsTestConfig c = CreateDtlsTestConfig(version); + + AddTestCase(testSuite, c, prefix + "GoodDefault"); } { TlsTestConfig c = CreateDtlsTestConfig(version); c.serverCertReq = C.SERVER_CERT_REQ_NONE; - testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodNoCertReq")); + AddTestCase(testSuite, c, prefix + "GoodNoCertReq"); } { TlsTestConfig c = CreateDtlsTestConfig(version); c.clientAuth = C.CLIENT_AUTH_NONE; - testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodOptionalCertReqDeclined")); + AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined"); } } + private static void AddTestCase(IList testSuite, TlsTestConfig config, String name) + { + testSuite.Add(new TestCaseData(config).SetName(name)); + } + private static TlsTestConfig CreateDtlsTestConfig(ProtocolVersion version) { TlsTestConfig c = new TlsTestConfig(); diff --git a/crypto/test/src/crypto/tls/test/TlsTestCase.cs b/crypto/test/src/crypto/tls/test/TlsTestCase.cs index 4b0c12710..7fb5db6ce 100644 --- a/crypto/test/src/crypto/tls/test/TlsTestCase.cs +++ b/crypto/test/src/crypto/tls/test/TlsTestCase.cs @@ -35,8 +35,8 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests NetworkStream clientNet = new NetworkStream(clientPipe); NetworkStream serverNet = new NetworkStream(serverPipe); - TlsClientProtocol clientProtocol = new TlsClientProtocol(clientNet, secureRandom); - TlsServerProtocol serverProtocol = new TlsServerProtocol(serverNet, secureRandom); + TlsTestClientProtocol clientProtocol = new TlsTestClientProtocol(clientNet, secureRandom, config); + TlsTestServerProtocol serverProtocol = new TlsTestServerProtocol(serverNet, secureRandom, config); TlsTestClientImpl clientImpl = new TlsTestClientImpl(config); TlsTestServerImpl serverImpl = new TlsTestServerImpl(config); @@ -104,13 +104,13 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests internal class Server { protected readonly TlsTestCase mOuter; - protected readonly TlsServerProtocol mServerProtocol; + protected readonly TlsTestServerProtocol mServerProtocol; protected readonly TlsTestServerImpl mServerImpl; internal bool mCanExit = false; internal Exception mCaught = null; - internal Server(TlsTestCase outer, TlsServerProtocol serverProtocol, TlsTestServerImpl serverImpl) + internal Server(TlsTestCase outer, TlsTestServerProtocol serverProtocol, TlsTestServerImpl serverImpl) { this.mOuter = outer; this.mServerProtocol = serverProtocol; diff --git a/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs b/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs index 0cc1883ba..f313f75fe 100644 --- a/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs +++ b/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.IO; using Org.BouncyCastle.Asn1; @@ -201,9 +202,15 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests return null; } + IList supportedSigAlgs = certificateRequest.SupportedSignatureAlgorithms; + if (supportedSigAlgs != null && mOuter.mConfig.clientAuthSigAlg != null) + { + supportedSigAlgs = new ArrayList(1); + supportedSigAlgs.Add(mOuter.mConfig.clientAuthSigAlg); + } + TlsSignerCredentials signerCredentials = TlsTestUtilities.LoadSignerCredentials(mContext, - certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa, - "x509-client.pem", "x509-client-key.pem"); + supportedSigAlgs, SignatureAlgorithm.rsa, "x509-client.pem", "x509-client-key.pem"); if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_VALID) { diff --git a/crypto/test/src/crypto/tls/test/TlsTestClientProtocol.cs b/crypto/test/src/crypto/tls/test/TlsTestClientProtocol.cs new file mode 100644 index 000000000..97b7c91bc --- /dev/null +++ b/crypto/test/src/crypto/tls/test/TlsTestClientProtocol.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls.Tests +{ + internal class TlsTestClientProtocol + : TlsClientProtocol + { + protected readonly TlsTestConfig config; + + public TlsTestClientProtocol(Stream stream, SecureRandom secureRandom, TlsTestConfig config) + : base(stream, secureRandom) + { + this.config = config; + } + + protected override void SendCertificateVerifyMessage(DigitallySigned certificateVerify) + { + if (certificateVerify.Algorithm != null && config.clientAuthSigAlgClaimed != null) + { + certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.Signature); + } + + base.SendCertificateVerifyMessage(certificateVerify); + } + } +} diff --git a/crypto/test/src/crypto/tls/test/TlsTestConfig.cs b/crypto/test/src/crypto/tls/test/TlsTestConfig.cs index 0d1e7badb..212d2b63c 100644 --- a/crypto/test/src/crypto/tls/test/TlsTestConfig.cs +++ b/crypto/test/src/crypto/tls/test/TlsTestConfig.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; namespace Org.BouncyCastle.Crypto.Tls.Tests { @@ -47,6 +48,18 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests public int clientAuth = CLIENT_AUTH_VALID; /** + * If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/hash algorithm to + * be used for the CertificateVerify signature (if one is sent). + */ + public SignatureAndHashAlgorithm clientAuthSigAlg = null; + + /** + * If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/hash algorithm to + * be _claimed_ in the CertificateVerify (if one is sent), independently of what was actually used. + */ + public SignatureAndHashAlgorithm clientAuthSigAlgClaimed = null; + + /** * Configures the minimum protocol version the client will accept. If null, uses the library's default. */ public ProtocolVersion clientMinimumVersion = null; @@ -67,6 +80,12 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests public int serverCertReq = SERVER_CERT_REQ_OPTIONAL; /** + * If TLS 1.2 or higher is negotiated, configures the set of supported signature algorithms in the + * CertificateRequest (if one is sent). If null, uses a default set. + */ + public IList serverCertReqSigAlgs = null; + + /** * Configures the maximum protocol version the server will accept. If null, uses the library's default. */ public ProtocolVersion serverMaximumVersion = null; diff --git a/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs b/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs index 152d5dbdc..6f9d35b5c 100644 --- a/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs +++ b/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.IO; +using System.Threading; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.X509; @@ -72,11 +73,11 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests + ", " + AlertDescription.GetText(alertDescription)); if (message != null) { - output.WriteLine("> " + message); + SafeWriteLine(output, "> " + message); } if (cause != null) { - output.WriteLine(cause); + SafeWriteLine(output, cause); } } } @@ -92,7 +93,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests if (TlsTestConfig.DEBUG) { TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; - output.WriteLine("TLS server received alert: " + AlertLevel.GetText(alertLevel) + SafeWriteLine(output, "TLS server received alert: " + AlertLevel.GetText(alertLevel) + ", " + AlertDescription.GetText(alertDescription)); } } @@ -122,7 +123,11 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests IList serverSigAlgs = null; if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mServerVersion)) { - serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(); + serverSigAlgs = mConfig.serverCertReqSigAlgs; + if (serverSigAlgs == null) + { + serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(); + } } IList certificateAuthorities = new ArrayList(); @@ -190,5 +195,19 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa, "x509-server.pem", "x509-server-key.pem"); } + + private static void SafeWriteLine(TextWriter output, object line) + { + try + { + output.WriteLine(line); + } + catch (ThreadInterruptedException) + { + /* + * For some reason the NUnit plugin in Visual Studio started throwing these during alert logging + */ + } + } } } diff --git a/crypto/test/src/crypto/tls/test/TlsTestServerProtocol.cs b/crypto/test/src/crypto/tls/test/TlsTestServerProtocol.cs new file mode 100644 index 000000000..845b7f0b9 --- /dev/null +++ b/crypto/test/src/crypto/tls/test/TlsTestServerProtocol.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls.Tests +{ + internal class TlsTestServerProtocol + : TlsServerProtocol + { + protected readonly TlsTestConfig config; + + public TlsTestServerProtocol(Stream stream, SecureRandom secureRandom, TlsTestConfig config) + : base(stream, secureRandom) + { + this.config = config; + } + } +} diff --git a/crypto/test/src/crypto/tls/test/TlsTestSuite.cs b/crypto/test/src/crypto/tls/test/TlsTestSuite.cs index dfd09d06e..0770187d5 100644 --- a/crypto/test/src/crypto/tls/test/TlsTestSuite.cs +++ b/crypto/test/src/crypto/tls/test/TlsTestSuite.cs @@ -32,7 +32,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12); c.clientFallback = true; - testSuite.Add(new TestCaseData(c).SetName("FallbackGood")); + AddTestCase(testSuite, c, "FallbackGood"); } { @@ -41,14 +41,14 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests c.clientFallback = true; c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback); - testSuite.Add(new TestCaseData(c).SetName("FallbackBad")); + AddTestCase(testSuite, c, "FallbackBad"); } { TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12); c.clientOfferVersion = ProtocolVersion.TLSv11; - testSuite.Add(new TestCaseData(c).SetName("FallbackNone")); + AddTestCase(testSuite, c, "FallbackNone"); } } @@ -63,7 +63,58 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests { TlsTestConfig c = CreateTlsTestConfig(version); - testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodDefault")); + AddTestCase(testSuite, c, prefix + "GoodDefault"); + } + + /* + * Server only declares support for SHA1/RSA, client selects MD5/RSA. Since the client is + * NOT actually tracking MD5 over the handshake, we expect fatal alert from the client. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateTlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_VALID; + c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa); + c.serverCertReqSigAlgs = TlsUtilities.GetDefaultRsaSignatureAlgorithms(); + c.ExpectClientFatalAlert(AlertDescription.internal_error); + + AddTestCase(testSuite, c, prefix + "BadCertificateVerifyHashAlg"); + } + + /* + * Server only declares support for SHA1/ECDSA, client selects SHA1/RSA. Since the client is + * actually tracking SHA1 over the handshake, we expect fatal alert to come from the server + * when it verifies the selected algorithm against the CertificateRequest supported + * algorithms. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateTlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_VALID; + c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa); + c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms(); + c.ExpectServerFatalAlert(AlertDescription.illegal_parameter); + + AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlg"); + } + + /* + * Server only declares support for SHA1/ECDSA, client signs with SHA1/RSA, but sends + * SHA1/ECDSA in the CertificateVerify. Since the client is actually tracking SHA1 over the + * handshake, and the claimed algorithm is in the CertificateRequest supported algorithms, + * we expect fatal alert to come from the server when it finds the claimed algorithm + * doesn't match the client certificate. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateTlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_VALID; + c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa); + c.clientAuthSigAlgClaimed = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa); + c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms(); + c.ExpectServerFatalAlert(AlertDescription.decrypt_error); + + AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch"); } { @@ -71,7 +122,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY; c.ExpectServerFatalAlert(AlertDescription.decrypt_error); - testSuite.Add(new TestCaseData(c).SetName(prefix + "BadCertificateVerify")); + AddTestCase(testSuite, c, prefix + "BadCertificateVerifySignature"); } { @@ -79,7 +130,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests c.clientAuth = C.CLIENT_AUTH_INVALID_CERT; c.ExpectServerFatalAlert(AlertDescription.bad_certificate); - testSuite.Add(new TestCaseData(c).SetName(prefix + "BadClientCertificate")); + AddTestCase(testSuite, c, prefix + "BadClientCertificate"); } { @@ -88,24 +139,29 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY; c.ExpectServerFatalAlert(AlertDescription.handshake_failure); - testSuite.Add(new TestCaseData(c).SetName(prefix + "BadMandatoryCertReqDeclined")); + AddTestCase(testSuite, c, prefix + "BadMandatoryCertReqDeclined"); } { TlsTestConfig c = CreateTlsTestConfig(version); c.serverCertReq = C.SERVER_CERT_REQ_NONE; - testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodNoCertReq")); + AddTestCase(testSuite, c, prefix + "GoodNoCertReq"); } { TlsTestConfig c = CreateTlsTestConfig(version); c.clientAuth = C.CLIENT_AUTH_NONE; - testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodOptionalCertReqDeclined")); + AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined"); } } + private static void AddTestCase(IList testSuite, TlsTestConfig config, string name) + { + testSuite.Add(new TestCaseData(config).SetName(name)); + } + private static TlsTestConfig CreateTlsTestConfig(ProtocolVersion version) { TlsTestConfig c = new TlsTestConfig(); |