diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-07-12 15:15:36 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-07-12 15:15:36 +0700 |
commit | 68c795fe81277f73aeb90d8ad4c6f4305f32c906 (patch) | |
tree | 59643344aafef91bbd4c4a3a7973deba3d837a00 /crypto/test | |
parent | TLS test tweaks (diff) | |
download | BouncyCastle.NET-ed25519-68c795fe81277f73aeb90d8ad4c6f4305f32c906.tar.xz |
Port of new TLS API from bc-java
Diffstat (limited to 'crypto/test')
43 files changed, 6504 insertions, 0 deletions
diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj index 0c0184d42..c5fdecd54 100644 --- a/crypto/test/UnitTests.csproj +++ b/crypto/test/UnitTests.csproj @@ -467,6 +467,48 @@ <Compile Include="src\test\nist\NistCertPathTest.cs" /> <Compile Include="src\test\nist\NistCertPathTest2.cs" /> <Compile Include="src\test\rsa3\RSA3CertTest.cs" /> + <Compile Include="src\tls\crypto\test\BcTlsCryptoTest.cs" /> + <Compile Include="src\tls\test\ByteQueueInputStreamTest.cs" /> + <Compile Include="src\tls\test\CertChainUtilities.cs" /> + <Compile Include="src\tls\test\DtlsProtocolTest.cs" /> + <Compile Include="src\tls\test\DtlsPskProtocolTest.cs" /> + <Compile Include="src\tls\test\DtlsTestCase.cs" /> + <Compile Include="src\tls\test\DtlsTestClientProtocol.cs" /> + <Compile Include="src\tls\test\DtlsTestServerProtocol.cs" /> + <Compile Include="src\tls\test\DtlsTestSuite.cs" /> + <Compile Include="src\tls\test\LoggingDatagramTransport.cs" /> + <Compile Include="src\tls\test\MockDatagramAssociation.cs" /> + <Compile Include="src\tls\test\MockDtlsClient.cs" /> + <Compile Include="src\tls\test\MockDtlsServer.cs" /> + <Compile Include="src\tls\test\MockPskDtlsClient.cs" /> + <Compile Include="src\tls\test\MockPskDtlsServer.cs" /> + <Compile Include="src\tls\test\MockPskTlsClient.cs" /> + <Compile Include="src\tls\test\MockPskTlsServer.cs" /> + <Compile Include="src\tls\test\MockSrpTlsClient.cs" /> + <Compile Include="src\tls\test\MockSrpTlsServer.cs" /> + <Compile Include="src\tls\test\MockTlsClient.cs" /> + <Compile Include="src\tls\test\MockTlsServer.cs" /> + <Compile Include="src\tls\test\NetworkStream.cs" /> + <Compile Include="src\tls\test\PipedStream.cs" /> + <Compile Include="src\tls\test\PrfTest.cs" /> + <Compile Include="src\tls\test\PskTlsClientTest.cs" /> + <Compile Include="src\tls\test\PskTlsServerTest.cs" /> + <Compile Include="src\tls\test\TlsClientTest.cs" /> + <Compile Include="src\tls\test\TlsProtocolNonBlockingTest.cs" /> + <Compile Include="src\tls\test\TlsProtocolTest.cs" /> + <Compile Include="src\tls\test\TlsPskProtocolTest.cs" /> + <Compile Include="src\tls\test\TlsServerTest.cs" /> + <Compile Include="src\tls\test\TlsSrpProtocolTest.cs" /> + <Compile Include="src\tls\test\TlsTestCase.cs" /> + <Compile Include="src\tls\test\TlsTestClientImpl.cs" /> + <Compile Include="src\tls\test\TlsTestClientProtocol.cs" /> + <Compile Include="src\tls\test\TlsTestConfig.cs" /> + <Compile Include="src\tls\test\TlsTestServerImpl.cs" /> + <Compile Include="src\tls\test\TlsTestServerProtocol.cs" /> + <Compile Include="src\tls\test\TlsTestSuite.cs" /> + <Compile Include="src\tls\test\TlsTestUtilities.cs" /> + <Compile Include="src\tls\test\TlsUtilitiesTest.cs" /> + <Compile Include="src\tls\test\UnreliableDatagramTransport.cs" /> <Compile Include="src\tsp\test\AllTests.cs" /> <Compile Include="src\tsp\test\GenTimeAccuracyTest.cs" /> <Compile Include="src\tsp\test\ParseTest.cs" /> diff --git a/crypto/test/src/tls/crypto/test/BcTlsCryptoTest.cs b/crypto/test/src/tls/crypto/test/BcTlsCryptoTest.cs new file mode 100644 index 000000000..a57212c73 --- /dev/null +++ b/crypto/test/src/tls/crypto/test/BcTlsCryptoTest.cs @@ -0,0 +1,820 @@ +using System; +using System.Collections; +using System.IO; + +using NUnit.Framework; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Tls.Tests; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tls.Crypto.Tests +{ + [TestFixture] + public class BcTlsCryptoTest + { + private static readonly byte[] ClientHello = Hex("01 00 00 c0 03 03 cb 34 ec b1 e7 81 63" + + "ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83" + + "02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b" + + "00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00" + + "12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23" + + "00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2" + + "3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a" + + "af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03" + + "02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06" + + "02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"); + private static readonly byte[] ServerHello = Hex("02 00 00 56 03 03 a6 af 06 a4 12 18 60" + + "dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e" + + "d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88" + + "76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1" + + "dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"); + private static readonly byte[] EncryptedExtensions = Hex("08 00 00 24 00 22 00 0a 00 14 00" + + "12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c" + + "00 02 40 01 00 00 00 00"); + private static readonly byte[] Certificate = Hex("0b 00 01 b9 00 00 01 b5 00 01 b0 30 82" + + "01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48" + + "86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03" + + "72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17" + + "0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06" + + "03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7" + + "0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f" + + "82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26" + + "d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c" + + "1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52" + + "4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74" + + "80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93" + + "ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03" + + "01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06" + + "03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01" + + "01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a" + + "72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea" + + "e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01" + + "51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be" + + "c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b" + + "1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8" + + "96 12 29 ac 91 87 b4 2b 4d e1 00 00"); + private static readonly byte[] CertificateVerify = Hex("0f 00 00 84 08 04 00 80 5a 74 7c" + + "5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a" + + "b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07" + + "86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b" + + "be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44" + + "5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a" + + "3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3"); + private static readonly byte[] ServerFinished = Hex("14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb" + + "dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07" + + "18"); + private static readonly byte[] ClientFinished = Hex("14 00 00 20 a8 ec 43 6d 67 76 34 ae 52 5a" + + "c1 fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce" + + "61"); + + private readonly TlsCrypto m_crypto = new BcTlsCrypto(new SecureRandom()); + + protected TlsCredentialedSigner LoadCredentialedSigner(TlsCryptoParameters cryptoParams, string resource, + SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + return TlsTestUtilities.LoadSignerCredentials(cryptoParams, m_crypto, + new string[]{ "x509-server-" + resource + ".pem" }, "x509-server-key-" + resource + ".pem", + signatureAndHashAlgorithm); + } + + protected TlsCredentialedSigner LoadCredentialedSignerLegacy(TlsCryptoParameters cryptoParams, + short signatureAlgorithm) + { + switch (signatureAlgorithm) + { + case SignatureAlgorithm.dsa: + return LoadCredentialedSigner(cryptoParams, "dsa", null); + case SignatureAlgorithm.ecdsa: + return LoadCredentialedSigner(cryptoParams, "ecdsa", null); + case SignatureAlgorithm.rsa: + return LoadCredentialedSigner(cryptoParams, "rsa-sign", null); + default: + return null; + } + } + + protected TlsCredentialedSigner LoadCredentialedSigner12(TlsCryptoParameters cryptoParams, + SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + switch (signatureAndHashAlgorithm.Signature) + { + case SignatureAlgorithm.dsa: + return LoadCredentialedSigner(cryptoParams, "dsa", signatureAndHashAlgorithm); + case SignatureAlgorithm.ecdsa: + return LoadCredentialedSigner(cryptoParams, "ecdsa", signatureAndHashAlgorithm); + case SignatureAlgorithm.ed25519: + return LoadCredentialedSigner(cryptoParams, "ed25519", signatureAndHashAlgorithm); + case SignatureAlgorithm.ed448: + return LoadCredentialedSigner(cryptoParams, "ed448", signatureAndHashAlgorithm); + case SignatureAlgorithm.rsa_pss_pss_sha256: + return LoadCredentialedSigner(cryptoParams, "rsa_pss_256", signatureAndHashAlgorithm); + case SignatureAlgorithm.rsa_pss_pss_sha384: + return LoadCredentialedSigner(cryptoParams, "rsa_pss_384", signatureAndHashAlgorithm); + case SignatureAlgorithm.rsa_pss_pss_sha512: + return LoadCredentialedSigner(cryptoParams, "rsa_pss_512", signatureAndHashAlgorithm); + case SignatureAlgorithm.rsa: + case SignatureAlgorithm.rsa_pss_rsae_sha256: + case SignatureAlgorithm.rsa_pss_rsae_sha384: + case SignatureAlgorithm.rsa_pss_rsae_sha512: + return LoadCredentialedSigner(cryptoParams, "rsa-sign", signatureAndHashAlgorithm); + + // TODO[draft-smyshlyaev-tls12-gost-suites-10] Add test resources for these + case SignatureAlgorithm.gostr34102012_256: + case SignatureAlgorithm.gostr34102012_512: + + default: + return null; + } + } + + protected TlsCredentialedSigner LoadCredentialedSigner13(TlsCryptoParameters cryptoParams, int signatureScheme) + { + SignatureAndHashAlgorithm signatureAndHashAlgorithm = SignatureScheme.GetSignatureAndHashAlgorithm( + signatureScheme); + + switch (signatureScheme) + { + case SignatureScheme.ecdsa_secp256r1_sha256: + return LoadCredentialedSigner(cryptoParams, "ecdsa", signatureAndHashAlgorithm); + case SignatureScheme.ed25519: + return LoadCredentialedSigner(cryptoParams, "ed25519", signatureAndHashAlgorithm); + case SignatureScheme.ed448: + return LoadCredentialedSigner(cryptoParams, "ed448", signatureAndHashAlgorithm); + case SignatureScheme.rsa_pss_pss_sha256: + return LoadCredentialedSigner(cryptoParams, "rsa_pss_256", signatureAndHashAlgorithm); + case SignatureScheme.rsa_pss_pss_sha384: + return LoadCredentialedSigner(cryptoParams, "rsa_pss_384", signatureAndHashAlgorithm); + case SignatureScheme.rsa_pss_pss_sha512: + return LoadCredentialedSigner(cryptoParams, "rsa_pss_512", signatureAndHashAlgorithm); + case SignatureScheme.rsa_pss_rsae_sha256: + case SignatureScheme.rsa_pss_rsae_sha384: + case SignatureScheme.rsa_pss_rsae_sha512: + return LoadCredentialedSigner(cryptoParams, "rsa-sign", signatureAndHashAlgorithm); + + // TODO[tls] Add test resources for these + case SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256: + case SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384: + case SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512: + case SignatureScheme.ecdsa_secp384r1_sha384: + case SignatureScheme.ecdsa_secp521r1_sha512: + case SignatureScheme.sm2sig_sm3: + + default: + return null; + } + } + + [Test] + public void TestDHDomain() + { + if (!m_crypto.HasDHAgreement()) + { + return; + } + + for (int namedGroup = 256; namedGroup < 512; ++namedGroup) + { + if (!NamedGroup.RefersToASpecificFiniteField(namedGroup) || !m_crypto.HasNamedGroup(namedGroup)) + { + continue; + } + + ImplTestDHDomain(new TlsDHConfig(namedGroup, false)); + ImplTestDHDomain(new TlsDHConfig(namedGroup, true)); + } + + IList groups = new TestTlsDHGroupVerifier().Groups; + foreach (DHGroup dhGroup in groups) + { + int namedGroup = TlsDHUtilities.GetNamedGroupForDHParameters(dhGroup.P, dhGroup.G); + if (NamedGroup.RefersToASpecificFiniteField(namedGroup)) + { + // Already tested the named groups + continue; + } + + ImplTestDHDomain(new TlsDHConfig(dhGroup)); + } + } + + [Test] + public void TestECDomain() + { + if (!m_crypto.HasECDHAgreement()) + { + return; + } + + for (int namedGroup = 0; namedGroup < 256; ++namedGroup) + { + if (!NamedGroup.RefersToAnECDHCurve(namedGroup) || !m_crypto.HasNamedGroup(namedGroup)) + { + continue; + } + + ImplTestECDomain(new TlsECConfig(namedGroup)); + } + } + + [Test] + public void TestHkdf() + { + /* + * Test vectors drawn from the server-side calculations of example handshake trace in RFC 8448, section 3. + */ + + int hash = CryptoHashAlgorithm.sha256; + int hashLen = TlsCryptoUtilities.GetHashOutputSize(hash); + + TlsSecret init = m_crypto.HkdfInit(hash), early, handshake, master, c_hs_t, s_hs_t, c_ap_t, s_ap_t, exp_master, res_master; + + TlsHash prfHash = m_crypto.CreateHash(hash); + + byte[] emptyTranscriptHash = GetCurrentHash(prfHash); + Expect(emptyTranscriptHash, "e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55"); + + // {server} extract secret "early": + { + byte[] ikm = new byte[32]; + early = init.HkdfExtract(hash, ikm); + Expect(early, "33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a"); + } + + // {server} derive secret for handshake "tls13 derived": + { + string label = "derived"; + handshake = TlsCryptoUtilities.HkdfExpandLabel(early, hash, label, emptyTranscriptHash, hashLen); + Expect(handshake, "6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba"); + } + + // {server} extract secret "handshake": + { + byte[] ikm = Hex("8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"); + handshake = handshake.HkdfExtract(hash, ikm); + Expect(handshake, "1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac"); + } + + prfHash.Update(ClientHello, 0, ClientHello.Length); + prfHash.Update(ServerHello, 0, ServerHello.Length); + + byte[] serverHelloTranscriptHash = GetCurrentHash(prfHash); + Expect(serverHelloTranscriptHash, "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"); + + // {server} derive secret "tls13 c hs traffic": + { + string label = "c hs traffic"; + c_hs_t = TlsCryptoUtilities.HkdfExpandLabel(handshake, hash, label, serverHelloTranscriptHash, hashLen); + Expect(c_hs_t, "b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21"); + } + + // {server} derive secret "tls13 s hs traffic": + { + string label = "s hs traffic"; + s_hs_t = TlsCryptoUtilities.HkdfExpandLabel(handshake, hash, label, serverHelloTranscriptHash, hashLen); + Expect(s_hs_t, "b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38"); + } + + // {server} derive secret for master "tls13 derived": + { + string label = "derived"; + master = TlsCryptoUtilities.HkdfExpandLabel(handshake, hash, label, emptyTranscriptHash, hashLen); + Expect(master, "43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4"); + } + + // {server} extract secret "master": + { + byte[] ikm = new byte[32]; + master = master.HkdfExtract(hash, ikm); + Expect(master, "18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19"); + } + + // {server} derive write traffic keys for handshake data: + { + TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(s_hs_t, hash, "key", TlsUtilities.EmptyBytes, 16); + Expect(key, "3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc"); + + TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(s_hs_t, hash, "iv", TlsUtilities.EmptyBytes, 12); + Expect(iv, "5d 31 3e b2 67 12 76 ee 13 00 0b 30"); + } + + prfHash.Update(EncryptedExtensions, 0, EncryptedExtensions.Length); + prfHash.Update(Certificate, 0, Certificate.Length); + prfHash.Update(CertificateVerify, 0, CertificateVerify.Length); + + // {server} calculate (server) finished "tls13 finished": + { + TlsSecret expanded = TlsCryptoUtilities.HkdfExpandLabel(s_hs_t, hash, "finished", TlsUtilities.EmptyBytes, hashLen); + Expect(expanded, "00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8"); + + // TODO Mention this transcript hash in RFC 8448 data? + byte[] transcriptHash = GetCurrentHash(prfHash); + Expect(transcriptHash, "ed b7 72 5f a7 a3 47 3b 03 1e c8 ef 65 a2 48 54 93 90 01 38 a2 b9 12 91 40 7d 79 51 a0 61 10 ed"); + + byte[] finished = CalculateHmac(hash, expanded, transcriptHash); + Expect(finished, Hex("9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18")); + } + + prfHash.Update(ServerFinished, 0, ServerFinished.Length); + + byte[] serverFinishedTranscriptHash = GetCurrentHash(prfHash); + Expect(serverFinishedTranscriptHash, "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"); + + // {server} derive secret "tls13 c ap traffic": + { + string label = "c ap traffic"; + c_ap_t = TlsCryptoUtilities.HkdfExpandLabel(master, hash, label, serverFinishedTranscriptHash, hashLen); + Expect(c_ap_t, "9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5"); + } + + // {server} derive secret "tls13 s ap traffic": + { + string label = "s ap traffic"; + s_ap_t = TlsCryptoUtilities.HkdfExpandLabel(master, hash, label, serverFinishedTranscriptHash, hashLen); + Expect(s_ap_t, "a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43"); + } + + // {server} derive secret "tls13 exp master": + { + string label = "exp master"; + exp_master = TlsCryptoUtilities.HkdfExpandLabel(master, hash, label, serverFinishedTranscriptHash, hashLen); + Expect(exp_master, "fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50"); + } + + // {server} derive write traffic keys for application data: + { + TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(s_ap_t, hash, "key", TlsUtilities.EmptyBytes, 16); + Expect(key, "9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56"); + + TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(s_ap_t, hash, "iv", TlsUtilities.EmptyBytes, 12); + Expect(iv, "cf 78 2b 88 dd 83 54 9a ad f1 e9 84"); + } + + // {server} derive read traffic keys for handshake data: + { + TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(c_hs_t, hash, "key", TlsUtilities.EmptyBytes, 16); + Expect(key, "db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01"); + + TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(c_hs_t, hash, "iv", TlsUtilities.EmptyBytes, 12); + Expect(iv, "5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f"); + } + + // {server} calculate (client) finished "tls13 finished": + { + TlsSecret expanded = TlsCryptoUtilities.HkdfExpandLabel(c_hs_t, hash, "finished", TlsUtilities.EmptyBytes, hashLen); + Expect(expanded, "b8 0a d0 10 15 fb 2f 0b d6 5f f7 d4 da 5d 6b f8 3f 84 82 1d 1f 87 fd c7 d3 c7 5b 5a 7b 42 d9 c4"); + + // TODO Mention this transcript hash in RFC 8448 data? + byte[] finished = CalculateHmac(hash, expanded, serverFinishedTranscriptHash); + Expect(finished, Hex("a8 ec 43 6d 67 76 34 ae 52 5a c1 fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61")); + } + + prfHash.Update(ClientFinished, 0, ClientFinished.Length); + + byte[] clientFinishedTranscriptHash = GetCurrentHash(prfHash); + Expect(clientFinishedTranscriptHash, "20 91 45 a9 6e e8 e2 a1 22 ff 81 00 47 cc 95 26 84 65 8d 60 49 e8 64 29 42 6d b8 7c 54 ad 14 3d"); + + // {server} derive read traffic keys for application data: + { + TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(c_ap_t, hash, "key", TlsUtilities.EmptyBytes, 16); + Expect(key, "17 42 2d da 59 6e d5 d9 ac d8 90 e3 c6 3f 50 51"); + + TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(c_ap_t, hash, "iv", TlsUtilities.EmptyBytes, 12); + Expect(iv, "5b 78 92 3d ee 08 57 90 33 e5 23 d9"); + } + + // {server} derive secret "tls13 res master": + { + res_master = TlsCryptoUtilities.HkdfExpandLabel(master, hash, "res master", clientFinishedTranscriptHash, hashLen); + Expect(res_master, "7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c"); + } + + // {server} generate resumption secret "tls13 resumption": + { + byte[] context = Hex("00 00"); + TlsSecret expanded = TlsCryptoUtilities.HkdfExpandLabel(res_master, hash, "resumption", context, hashLen); + Expect(expanded, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); + } + } + + [Test] + public void TestHkdfExpandLimit() + { + int[] hashes = new int[] { CryptoHashAlgorithm.md5, CryptoHashAlgorithm.sha1, CryptoHashAlgorithm.sha224, + CryptoHashAlgorithm.sha256, CryptoHashAlgorithm.sha384, CryptoHashAlgorithm.sha512, + CryptoHashAlgorithm.sm3 }; + + for (int i = 0; i < hashes.Length; ++i) + { + int hash = hashes[i]; + int hashLen = TlsCryptoUtilities.GetHashOutputSize(hash); + byte[] zeroes = new byte[hashLen]; + + int limit = 255 * hashLen; + + TlsSecret secret = m_crypto.HkdfInit(hash).HkdfExtract(hash, zeroes); + + try + { + secret.HkdfExpand(hash, TlsUtilities.EmptyBytes, limit); + } + catch (Exception e) + { + Assert.Fail("Unexpected exception: " + e.Message); + } + + try + { + secret.HkdfExpand(hash, TlsUtilities.EmptyBytes, limit + 1); + Assert.Fail("Expected an exception!"); + } + catch (ArgumentException) + { + // Expected + } + catch (Exception e) + { + Assert.Fail("Unexpected exception: " + e.Message); + } + } + } + + [Test] + public void TestSignaturesLegacy() + { + short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.dsa, SignatureAlgorithm.ecdsa, + SignatureAlgorithm.rsa }; + + TlsCryptoParameters cryptoParams = new TestTlsCryptoParameters(ProtocolVersion.TLSv11); + + for (int i = 0; i < signatureAlgorithms.Length; ++i) + { + short signatureAlgorithm = signatureAlgorithms[i]; + if (!m_crypto.HasSignatureAlgorithm(signatureAlgorithm)) + continue; + + TlsCredentialedSigner credentialedSigner = LoadCredentialedSignerLegacy(cryptoParams, + signatureAlgorithm); + if (null != credentialedSigner) + { + ImplTestSignatureLegacy(credentialedSigner); + } + } + } + + [Test] + public void TestSignatures12() + { + short[] hashAlgorithms = new short[]{ HashAlgorithm.md5, HashAlgorithm.sha1, HashAlgorithm.sha224, + HashAlgorithm.sha256, HashAlgorithm.sha384, HashAlgorithm.sha512 }; + short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.dsa, SignatureAlgorithm.ecdsa, + SignatureAlgorithm.rsa }; + + TlsCryptoParameters cryptoParams = new TestTlsCryptoParameters(ProtocolVersion.TLSv12); + + for (int i = 0; i < signatureAlgorithms.Length; ++i) + { + short signatureAlgorithm = signatureAlgorithms[i]; + + for (int j = 0; j < hashAlgorithms.Length; ++j) + { + SignatureAndHashAlgorithm signatureAndHashAlgorithm = SignatureAndHashAlgorithm.GetInstance( + hashAlgorithms[j], signatureAlgorithm); + if (!m_crypto.HasSignatureAndHashAlgorithm(signatureAndHashAlgorithm)) + continue; + + TlsCredentialedSigner credentialedSigner = LoadCredentialedSigner12(cryptoParams, + signatureAndHashAlgorithm); + if (null != credentialedSigner) + { + ImplTestSignature12(credentialedSigner, signatureAndHashAlgorithm); + } + } + } + + // Signature algorithms usable with HashAlgorithm.Intrinsic in TLS 1.2 + short[] intrinsicSignatureAlgorithms = new short[] { SignatureAlgorithm.ed25519, SignatureAlgorithm.ed448, + SignatureAlgorithm.gostr34102012_256, SignatureAlgorithm.gostr34102012_512, + SignatureAlgorithm.rsa_pss_pss_sha256, SignatureAlgorithm.rsa_pss_pss_sha384, + SignatureAlgorithm.rsa_pss_pss_sha512, SignatureAlgorithm.rsa_pss_rsae_sha256, + SignatureAlgorithm.rsa_pss_rsae_sha384, SignatureAlgorithm.rsa_pss_rsae_sha512, }; + + for (int i = 0; i < intrinsicSignatureAlgorithms.Length; ++i) + { + SignatureAndHashAlgorithm signatureAndHashAlgorithm = SignatureAndHashAlgorithm.GetInstance( + HashAlgorithm.Intrinsic, intrinsicSignatureAlgorithms[i]); + if (!m_crypto.HasSignatureAndHashAlgorithm(signatureAndHashAlgorithm)) + continue; + + TlsCredentialedSigner credentialedSigner = LoadCredentialedSigner12(cryptoParams, + signatureAndHashAlgorithm); + if (null != credentialedSigner) + { + ImplTestSignature12(credentialedSigner, signatureAndHashAlgorithm); + } + } + } + + [Test] + public void TestSignatures13() + { + int[] signatureSchemes = new int[] { SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256, + SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384, SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512, + SignatureScheme.ecdsa_secp256r1_sha256, SignatureScheme.ecdsa_secp384r1_sha384, + SignatureScheme.ecdsa_secp521r1_sha512, SignatureScheme.ed25519, SignatureScheme.ed448, + SignatureScheme.rsa_pss_pss_sha256, SignatureScheme.rsa_pss_pss_sha384, SignatureScheme.rsa_pss_pss_sha512, + SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_rsae_sha384, + SignatureScheme.rsa_pss_rsae_sha512, SignatureScheme.sm2sig_sm3, + // These are only used for certs in 1.3 (cert verification is not done by TlsCrypto) + //SignatureScheme.ecdsa_sha1, SignatureScheme.rsa_pkcs1_sha1, SignatureScheme.rsa_pkcs1_sha256, + //SignatureScheme.rsa_pkcs1_sha384, SignatureScheme.rsa_pkcs1_sha512, + }; + + TlsCryptoParameters cryptoParams = new TestTlsCryptoParameters(ProtocolVersion.TLSv13); + + for (int i = 0; i < signatureSchemes.Length; ++i) + { + int signatureScheme = signatureSchemes[i]; + if (!m_crypto.HasSignatureScheme(signatureScheme)) + continue; + + TlsCredentialedSigner credentialedSigner = LoadCredentialedSigner13(cryptoParams, signatureScheme); + if (null != credentialedSigner) + { + ImplTestSignature13(credentialedSigner, signatureScheme); + } + } + } + + private byte[] CalculateHmac(int cryptoHashAlgorithm, TlsSecret hmacKey, byte[] hmacInput) + { + byte[] keyBytes = Extract(hmacKey); + + TlsHmac hmac = m_crypto.CreateHmacForHash(cryptoHashAlgorithm); + hmac.SetKey(keyBytes, 0, keyBytes.Length); + hmac.Update(hmacInput, 0, hmacInput.Length); + return hmac.CalculateMac(); + } + + private void Expect(TlsSecret secret, string expectedHex) + { + Expect(Extract(secret), Hex(expectedHex)); + } + + private void Expect(byte[] actualOctets, string expectedHex) + { + Expect(actualOctets, Hex(expectedHex)); + } + + private void Expect(byte[] actualOctets, byte[] expectedOctets) + { + AssertArrayEquals(actualOctets, expectedOctets); + } + + private byte[] Extract(TlsSecret secret) + { + return m_crypto.AdoptSecret(secret).Extract(); + } + + private byte[] GetCurrentHash(TlsHash hash) + { + return hash.CloneHash().CalculateHash(); + } + + private static void AssertArrayEquals(byte[] a, byte[] b) + { + Assert.IsTrue(Arrays.AreEqual(a, b)); + } + + private static byte[] Hex(string s) + { + return Utilities.Encoders.Hex.Decode(s.Replace(" ", "")); + } + + private void ImplTestAgreement(TlsAgreement aA, TlsAgreement aB) + { + byte[] pA = aA.GenerateEphemeral(); + byte[] pB = aB.GenerateEphemeral(); + + aA.ReceivePeerValue(pB); + aB.ReceivePeerValue(pA); + + TlsSecret sA = aA.CalculateSecret(); + TlsSecret sB = aB.CalculateSecret(); + + AssertArrayEquals(Extract(sA), Extract(sB)); + } + + private void ImplTestDHDomain(TlsDHConfig dhConfig) + { + int namedGroup = dhConfig.NamedGroup; + int bits = namedGroup >= 0 + ? NamedGroup.GetFiniteFieldBits(namedGroup) + : dhConfig.ExplicitGroup.P.BitLength; + + int rounds = System.Math.Max(2, 11 - (bits >> 10)); + + TlsDHDomain d = m_crypto.CreateDHDomain(dhConfig); + + for (int i = 0; i < rounds; ++i) + { + TlsAgreement aA = d.CreateDH(); + TlsAgreement aB = d.CreateDH(); + + ImplTestAgreement(aA, aB); + } + } + + private void ImplTestECDomain(TlsECConfig ecConfig) + { + int bits = NamedGroup.GetCurveBits(ecConfig.NamedGroup); + int rounds = System.Math.Max(2, 12 - (bits >> 6)); + + TlsECDomain d = m_crypto.CreateECDomain(ecConfig); + + for (int i = 0; i < rounds; ++i) + { + TlsAgreement aA = d.CreateECDH(); + TlsAgreement aB = d.CreateECDH(); + + ImplTestAgreement(aA, aB); + } + } + + private void ImplTestSignatureLegacy(TlsCredentialedSigner credentialedSigner) + { + byte[] message = m_crypto.CreateNonceGenerator(TlsUtilities.EmptyBytes).GenerateNonce(100); + + byte[] signature; + TlsStreamSigner tlsStreamSigner = credentialedSigner.GetStreamSigner(); + if (null != tlsStreamSigner) + { + Stream output = tlsStreamSigner.GetOutputStream(); + output.Write(message, 0, message.Length); + signature = tlsStreamSigner.GetSignature(); + } + else + { + TlsHash tlsHash = new CombinedHash(m_crypto); + tlsHash.Update(message, 0, message.Length); + byte[] hash = tlsHash.CalculateHash(); + signature = credentialedSigner.GenerateRawSignature(hash); + } + + DigitallySigned digitallySigned = new DigitallySigned(null, signature); + + TlsCertificate tlsCertificate = credentialedSigner.Certificate.GetCertificateAt(0); + TlsVerifier tlsVerifier = tlsCertificate.CreateVerifier(tlsCertificate.GetLegacySignatureAlgorithm()); + + bool verified; + TlsStreamVerifier tlsStreamVerifier = tlsVerifier.GetStreamVerifier(digitallySigned); + if (null != tlsStreamVerifier) + { + Stream output = tlsStreamVerifier.GetOutputStream(); + output.Write(message, 0, message.Length); + verified = tlsStreamVerifier.IsVerified(); + } + else + { + TlsHash tlsHash = new CombinedHash(m_crypto); + tlsHash.Update(message, 0, message.Length); + byte[] hash = tlsHash.CalculateHash(); + verified = tlsVerifier.VerifyRawSignature(digitallySigned, hash); + } + + Assert.IsTrue(verified); + } + + private void ImplTestSignature12(TlsCredentialedSigner credentialedSigner, + SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + short hashAlgorithm = signatureAndHashAlgorithm.Hash; + + byte[] message = m_crypto.CreateNonceGenerator(TlsUtilities.EmptyBytes).GenerateNonce(100); + + byte[] signature; + TlsStreamSigner tlsStreamSigner = credentialedSigner.GetStreamSigner(); + if (null != tlsStreamSigner) + { + Stream output = tlsStreamSigner.GetOutputStream(); + output.Write(message, 0, message.Length); + signature = tlsStreamSigner.GetSignature(); + } + else + { + // Currently 1.2 relies on these being handled by stream signers + Assert.IsTrue(HashAlgorithm.Intrinsic != hashAlgorithm); + + int cryptoHashAlgorithm = TlsCryptoUtilities.GetHash(hashAlgorithm); + + TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm); + tlsHash.Update(message, 0, message.Length); + byte[] hash = tlsHash.CalculateHash(); + signature = credentialedSigner.GenerateRawSignature(hash); + } + + DigitallySigned digitallySigned = new DigitallySigned(signatureAndHashAlgorithm, signature); + + TlsCertificate tlsCertificate = credentialedSigner.Certificate.GetCertificateAt(0); + TlsVerifier tlsVerifier = tlsCertificate.CreateVerifier(signatureAndHashAlgorithm.Signature); + + bool verified; + TlsStreamVerifier tlsStreamVerifier = tlsVerifier.GetStreamVerifier(digitallySigned); + if (null != tlsStreamVerifier) + { + Stream output = tlsStreamVerifier.GetOutputStream(); + output.Write(message, 0, message.Length); + verified = tlsStreamVerifier.IsVerified(); + } + else + { + // Currently 1.2 relies on these being handled by stream verifiers + Assert.IsTrue(HashAlgorithm.Intrinsic != hashAlgorithm); + + int cryptoHashAlgorithm = TlsCryptoUtilities.GetHash(hashAlgorithm); + + TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm); + tlsHash.Update(message, 0, message.Length); + byte[] hash = tlsHash.CalculateHash(); + verified = tlsVerifier.VerifyRawSignature(digitallySigned, hash); + } + + Assert.IsTrue(verified); + } + + private void ImplTestSignature13(TlsCredentialedSigner credentialedSigner, int signatureScheme) + { + byte[] message = m_crypto.CreateNonceGenerator(TlsUtilities.EmptyBytes).GenerateNonce(100); + + byte[] signature; + TlsStreamSigner tlsStreamSigner = credentialedSigner.GetStreamSigner(); + if (null != tlsStreamSigner) + { + Stream output = tlsStreamSigner.GetOutputStream(); + output.Write(message, 0, message.Length); + signature = tlsStreamSigner.GetSignature(); + } + else + { + int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme); + + TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm); + tlsHash.Update(message, 0, message.Length); + byte[] hash = tlsHash.CalculateHash(); + signature = credentialedSigner.GenerateRawSignature(hash); + } + + DigitallySigned digitallySigned = new DigitallySigned( + SignatureScheme.GetSignatureAndHashAlgorithm(signatureScheme), signature); + + TlsCertificate tlsCertificate = credentialedSigner.Certificate.GetCertificateAt(0); + TlsVerifier tlsVerifier = tlsCertificate.CreateVerifier(signatureScheme); + + bool verified; + TlsStreamVerifier tlsStreamVerifier = tlsVerifier.GetStreamVerifier(digitallySigned); + if (null != tlsStreamVerifier) + { + Stream output = tlsStreamVerifier.GetOutputStream(); + output.Write(message, 0, message.Length); + verified = tlsStreamVerifier.IsVerified(); + } + else + { + int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme); + + TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm); + tlsHash.Update(message, 0, message.Length); + byte[] hash = tlsHash.CalculateHash(); + verified = tlsVerifier.VerifyRawSignature(digitallySigned, hash); + } + + Assert.IsTrue(verified); + } + + private class TestTlsCryptoParameters + : TlsCryptoParameters + { + private readonly ProtocolVersion m_serverVersion; + + internal TestTlsCryptoParameters(ProtocolVersion serverVersion) + : base(null) + { + this.m_serverVersion = serverVersion; + } + + public override ProtocolVersion ServerVersion + { + get { return m_serverVersion; } + } + } + + private class TestTlsDHGroupVerifier + : DefaultTlsDHGroupVerifier + { + internal IList Groups + { + get { return m_groups; } + } + } + } +} diff --git a/crypto/test/src/tls/test/ByteQueueInputStreamTest.cs b/crypto/test/src/tls/test/ByteQueueInputStreamTest.cs new file mode 100644 index 000000000..9edb3109b --- /dev/null +++ b/crypto/test/src/tls/test/ByteQueueInputStreamTest.cs @@ -0,0 +1,134 @@ +using System; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class ByteQueueInputStreamTest + { + [Test] + public void TestAvailable() + { + ByteQueueInputStream input = new ByteQueueInputStream(); + + // buffer is empty + Assert.AreEqual(0, input.Available); + + // after adding once + input.AddBytes(new byte[10]); + Assert.AreEqual(10, input.Available); + + // after adding more than once + input.AddBytes(new byte[5]); + Assert.AreEqual(15, input.Available); + + // after reading a single byte + input.ReadByte(); + Assert.AreEqual(14, input.Available); + + // after reading into a byte array + input.Read(new byte[4], 0, 4); + Assert.AreEqual(10, input.Available); + + input.Close(); + } + + [Test] + public void TestSkip() + { + ByteQueueInputStream input = new ByteQueueInputStream(); + + // skip when buffer is empty + Assert.AreEqual(0, input.Skip(10)); + + // skip equal to available + input.AddBytes(new byte[2]); + Assert.AreEqual(2, input.Skip(2)); + Assert.AreEqual(0, input.Available); + + // skip less than available + input.AddBytes(new byte[10]); + Assert.AreEqual(5, input.Skip(5)); + Assert.AreEqual(5, input.Available); + + // skip more than available + Assert.AreEqual(5, input.Skip(20)); + Assert.AreEqual(0, input.Available); + + input.Close(); + } + + [Test] + public void TestRead() + { + ByteQueueInputStream input = new ByteQueueInputStream(); + input.AddBytes(new byte[]{ 0x01, 0x02 }); + input.AddBytes(new byte[]{ 0x03 }); + + Assert.AreEqual(0x01, input.ReadByte()); + Assert.AreEqual(0x02, input.ReadByte()); + Assert.AreEqual(0x03, input.ReadByte()); + Assert.AreEqual(-1, input.ReadByte()); + + input.Close(); + } + + [Test] + public void TestReadArray() + { + ByteQueueInputStream input = new ByteQueueInputStream(); + input.AddBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }); + + byte[] buffer = new byte[5]; + + // read less than available into specified position + Assert.AreEqual(1, input.Read(buffer, 2, 1)); + AssertArrayEquals(new byte[]{ 0x00, 0x00, 0x01, 0x00, 0x00 }, buffer); + + // read equal to available + Assert.AreEqual(5, input.Read(buffer, 0, buffer.Length)); + AssertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer); + + // read more than available + input.AddBytes(new byte[]{ 0x01, 0x02, 0x03 }); + Assert.AreEqual(3, input.Read(buffer, 0, buffer.Length)); + AssertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x05, 0x06 }, buffer); + + input.Close(); + } + + [Test] + public void TestPeek() + { + ByteQueueInputStream input = new ByteQueueInputStream(); + + byte[] buffer = new byte[5]; + + // peek more than available + Assert.AreEqual(0, input.Peek(buffer)); + AssertArrayEquals(new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00 }, buffer); + + // peek less than available + input.AddBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }); + Assert.AreEqual(5, input.Peek(buffer)); + AssertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05 }, buffer); + Assert.AreEqual(6, input.Available); + + // peek equal to available + input.ReadByte(); + Assert.AreEqual(5, input.Peek(buffer)); + AssertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer); + Assert.AreEqual(5, input.Available); + + input.Close(); + } + + private static void AssertArrayEquals(byte[] a, byte[] b) + { + Assert.IsTrue(Arrays.AreEqual(a, b)); + } + } +} diff --git a/crypto/test/src/tls/test/CertChainUtilities.cs b/crypto/test/src/tls/test/CertChainUtilities.cs new file mode 100644 index 000000000..8df8e48bc --- /dev/null +++ b/crypto/test/src/tls/test/CertChainUtilities.cs @@ -0,0 +1,123 @@ +using System; +using System.Threading; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.Tls.Tests +{ + public class CertChainUtilities + { + private static readonly SecureRandom Random = new SecureRandom(); + + private static long serialNumber = 1L; + + /// <summary>We generate the CA's certificate.</summary> + public static X509Certificate CreateMasterCert(string rootDN, AsymmetricCipherKeyPair keyPair) + { + // + // create the certificate - version 1 + // + X509V1CertificateGenerator gen = new X509V1CertificateGenerator(); + gen.SetIssuerDN(new X509Name(rootDN)); + gen.SetSerialNumber(BigInteger.ValueOf(Interlocked.Increment(ref serialNumber))); + gen.SetNotBefore(DateTime.UtcNow.AddDays(-30)); + gen.SetNotAfter(DateTime.UtcNow.AddDays(30)); + gen.SetSubjectDN(new X509Name(rootDN)); + gen.SetPublicKey(keyPair.Public); + + return SignV1(gen, keyPair.Private); + } + + /// <summary>We generate an intermediate certificate signed by our CA.</summary> + public static X509Certificate CreateIntermediateCert(string interDN, AsymmetricKeyParameter pubKey, + AsymmetricKeyParameter caPrivKey, X509Certificate caCert) + { + // + // create the certificate - version 3 + // + X509V3CertificateGenerator gen = new X509V3CertificateGenerator(); + gen.SetIssuerDN(caCert.SubjectDN); + gen.SetSerialNumber(BigInteger.ValueOf(Interlocked.Increment(ref serialNumber))); + gen.SetNotBefore(DateTime.UtcNow.AddDays(-30)); + gen.SetNotAfter(DateTime.UtcNow.AddDays(30)); + gen.SetSubjectDN(new X509Name(interDN)); + gen.SetPublicKey(pubKey); + + // + // extensions + // + gen.AddExtension(X509Extensions.SubjectKeyIdentifier, false, + new SubjectKeyIdentifierStructure(pubKey)); + gen.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, + new AuthorityKeyIdentifierStructure(caCert)); + gen.AddExtension(X509Extensions.BasicConstraints, true, + new BasicConstraints(0)); + + return SignV3(gen, caPrivKey); + } + + /// <summary>We generate a certificate signed by our CA's intermediate certificate.</summary> + public static X509Certificate CreateEndEntityCert(string endEntityDN, AsymmetricKeyParameter pubKey, + AsymmetricKeyParameter caPrivKey, X509Certificate caCert) + { + X509V3CertificateGenerator gen = CreateBaseEndEntityGenerator(endEntityDN, pubKey, caCert); + + return SignV3(gen, caPrivKey); + } + + /// <summary>We generate a certificate signed by our CA's intermediate certificate with ExtendedKeyUsage + /// extension.</summary> + public static X509Certificate CreateEndEntityCert(string endEntityDN, AsymmetricKeyParameter pubKey, + AsymmetricKeyParameter caPrivKey, X509Certificate caCert, KeyPurposeID keyPurposeID) + { + X509V3CertificateGenerator gen = CreateBaseEndEntityGenerator(endEntityDN, pubKey, caCert); + + gen.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(keyPurposeID)); + + return SignV3(gen, caPrivKey); + } + + private static X509V3CertificateGenerator CreateBaseEndEntityGenerator(string endEntityDN, + AsymmetricKeyParameter pubKey, X509Certificate caCert) + { + // + // create the certificate - version 3 + // + X509V3CertificateGenerator gen = new X509V3CertificateGenerator(); + gen.SetIssuerDN(caCert.SubjectDN); + gen.SetSerialNumber(BigInteger.ValueOf(Interlocked.Increment(ref serialNumber))); + gen.SetNotBefore(DateTime.UtcNow.AddDays(-30)); + gen.SetNotAfter(DateTime.UtcNow.AddDays(30)); + gen.SetSubjectDN(new X509Name(endEntityDN)); + gen.SetPublicKey(pubKey); + + // + // add the extensions + // + gen.AddExtension(X509Extensions.SubjectKeyIdentifier, false, + new SubjectKeyIdentifierStructure(pubKey)); + gen.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, + new AuthorityKeyIdentifierStructure(caCert.GetPublicKey())); + gen.AddExtension(X509Extensions.BasicConstraints, true, + new BasicConstraints(false)); + + return gen; + } + + private static X509Certificate SignV1(X509V1CertificateGenerator gen, AsymmetricKeyParameter caPrivKey) + { + return gen.Generate(new Asn1SignatureFactory("SHA256withRSA", caPrivKey, Random)); + } + + private static X509Certificate SignV3(X509V3CertificateGenerator gen, AsymmetricKeyParameter caPrivKey) + { + return gen.Generate(new Asn1SignatureFactory("SHA256withRSA", caPrivKey, Random)); + } + } +} diff --git a/crypto/test/src/tls/test/DtlsProtocolTest.cs b/crypto/test/src/tls/test/DtlsProtocolTest.cs new file mode 100644 index 000000000..388003666 --- /dev/null +++ b/crypto/test/src/tls/test/DtlsProtocolTest.cs @@ -0,0 +1,102 @@ +using System; +using System.Threading; + +using NUnit.Framework; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class DtlsProtocolTest + { + [Test] + public void TestClientServer() + { + SecureRandom secureRandom = new SecureRandom(); + + DtlsClientProtocol clientProtocol = new DtlsClientProtocol(); + DtlsServerProtocol serverProtocol = new DtlsServerProtocol(); + + MockDatagramAssociation network = new MockDatagramAssociation(1500); + + Server server = new Server(serverProtocol, network.Server); + + Thread serverThread = new Thread(new ThreadStart(server.Run)); + serverThread.Start(); + + DatagramTransport clientTransport = network.Client; + + clientTransport = new UnreliableDatagramTransport(clientTransport, secureRandom, 0, 0); + + clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out); + + MockDtlsClient client = new MockDtlsClient(null); + + DtlsTransport dtlsClient = clientProtocol.Connect(client, clientTransport); + + for (int i = 1; i <= 10; ++i) + { + byte[] data = new byte[i]; + Arrays.Fill(data, (byte)i); + dtlsClient.Send(data, 0, data.Length); + } + + byte[] buf = new byte[dtlsClient.GetReceiveLimit()]; + while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0) + { + } + + dtlsClient.Close(); + + server.Shutdown(serverThread); + } + + internal class Server + { + private readonly DtlsServerProtocol m_serverProtocol; + private readonly DatagramTransport m_serverTransport; + private volatile bool m_isShutdown = false; + + internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport) + { + this.m_serverProtocol = serverProtocol; + this.m_serverTransport = serverTransport; + } + + public void Run() + { + try + { + MockDtlsServer server = new MockDtlsServer(); + DtlsTransport dtlsServer = m_serverProtocol.Accept(server, m_serverTransport); + byte[] buf = new byte[dtlsServer.GetReceiveLimit()]; + while (!m_isShutdown) + { + int length = dtlsServer.Receive(buf, 0, buf.Length, 1000); + if (length >= 0) + { + dtlsServer.Send(buf, 0, length); + } + } + dtlsServer.Close(); + } + catch (Exception e) + { + Console.Error.WriteLine(e); + Console.Error.Flush(); + } + } + + internal void Shutdown(Thread serverThread) + { + if (!m_isShutdown) + { + this.m_isShutdown = true; + serverThread.Join(); + } + } + } + } +} diff --git a/crypto/test/src/tls/test/DtlsPskProtocolTest.cs b/crypto/test/src/tls/test/DtlsPskProtocolTest.cs new file mode 100644 index 000000000..0da6cb661 --- /dev/null +++ b/crypto/test/src/tls/test/DtlsPskProtocolTest.cs @@ -0,0 +1,102 @@ +using System; +using System.Threading; + +using NUnit.Framework; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class DlsPskProtocolTest + { + [Test] + public void TestClientServer() + { + SecureRandom secureRandom = new SecureRandom(); + + DtlsClientProtocol clientProtocol = new DtlsClientProtocol(); + DtlsServerProtocol serverProtocol = new DtlsServerProtocol(); + + MockDatagramAssociation network = new MockDatagramAssociation(1500); + + Server server = new Server(serverProtocol, network.Server); + + Thread serverThread = new Thread(new ThreadStart(server.Run)); + serverThread.Start(); + + DatagramTransport clientTransport = network.Client; + + clientTransport = new UnreliableDatagramTransport(clientTransport, secureRandom, 0, 0); + + clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out); + + MockPskDtlsClient client = new MockPskDtlsClient(null); + + DtlsTransport dtlsClient = clientProtocol.Connect(client, clientTransport); + + for (int i = 1; i <= 10; ++i) + { + byte[] data = new byte[i]; + Arrays.Fill(data, (byte)i); + dtlsClient.Send(data, 0, data.Length); + } + + byte[] buf = new byte[dtlsClient.GetReceiveLimit()]; + while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0) + { + } + + dtlsClient.Close(); + + server.Shutdown(serverThread); + } + + internal class Server + { + private readonly DtlsServerProtocol m_serverProtocol; + private readonly DatagramTransport m_serverTransport; + private volatile bool m_isShutdown = false; + + internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport) + { + this.m_serverProtocol = serverProtocol; + this.m_serverTransport = serverTransport; + } + + public void Run() + { + try + { + MockPskDtlsServer server = new MockPskDtlsServer(); + DtlsTransport dtlsServer = m_serverProtocol.Accept(server, m_serverTransport); + byte[] buf = new byte[dtlsServer.GetReceiveLimit()]; + while (!m_isShutdown) + { + int length = dtlsServer.Receive(buf, 0, buf.Length, 1000); + if (length >= 0) + { + dtlsServer.Send(buf, 0, length); + } + } + dtlsServer.Close(); + } + catch (Exception e) + { + Console.Error.WriteLine(e); + Console.Error.Flush(); + } + } + + internal void Shutdown(Thread serverThread) + { + if (!m_isShutdown) + { + this.m_isShutdown = true; + serverThread.Join(); + } + } + } + } +} diff --git a/crypto/test/src/tls/test/DtlsTestCase.cs b/crypto/test/src/tls/test/DtlsTestCase.cs new file mode 100644 index 000000000..d93f17c27 --- /dev/null +++ b/crypto/test/src/tls/test/DtlsTestCase.cs @@ -0,0 +1,164 @@ +using System; +using System.Threading; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class DtlsTestCase + { + private static void CheckDtlsVersions(ProtocolVersion[] versions) + { + if (versions != null) + { + for (int i = 0; i < versions.Length; ++i) + { + if (!versions[i].IsDtls) + throw new InvalidOperationException("Non-DTLS version"); + } + } + } + + [Test, TestCaseSource(typeof(DtlsTestSuite), "Suite")] + public void RunTest(TlsTestConfig config) + { + CheckDtlsVersions(config.clientSupportedVersions); + CheckDtlsVersions(config.serverSupportedVersions); + + DtlsTestClientProtocol clientProtocol = new DtlsTestClientProtocol(config); + DtlsTestServerProtocol serverProtocol = new DtlsTestServerProtocol(config); + + MockDatagramAssociation network = new MockDatagramAssociation(1500); + + TlsTestClientImpl clientImpl = new TlsTestClientImpl(config); + TlsTestServerImpl serverImpl = new TlsTestServerImpl(config); + + Server server = new Server(this, serverProtocol, network.Server, serverImpl); + + Thread serverThread = new Thread(new ThreadStart(server.Run)); + serverThread.Start(); + + Exception caught = null; + try + { + DatagramTransport clientTransport = network.Client; + + if (TlsTestConfig.Debug) + { + clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out); + } + + DtlsTransport dtlsClient = clientProtocol.Connect(clientImpl, clientTransport); + + for (int i = 1; i <= 10; ++i) + { + byte[] data = new byte[i]; + Arrays.Fill(data, (byte)i); + dtlsClient.Send(data, 0, data.Length); + } + + byte[] buf = new byte[dtlsClient.GetReceiveLimit()]; + while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0) + { + } + + dtlsClient.Close(); + } + catch (Exception e) + { + caught = e; + LogException(caught); + } + + server.Shutdown(serverThread); + + // TODO Add checks that the various streams were closed + + Assert.AreEqual(config.expectFatalAlertConnectionEnd, clientImpl.FirstFatalAlertConnectionEnd, + "Client fatal alert connection end"); + Assert.AreEqual(config.expectFatalAlertConnectionEnd, serverImpl.FirstFatalAlertConnectionEnd, + "Server fatal alert connection end"); + + Assert.AreEqual(config.expectFatalAlertDescription, clientImpl.FirstFatalAlertDescription, + "Client fatal alert description"); + Assert.AreEqual(config.expectFatalAlertDescription, serverImpl.FirstFatalAlertDescription, + "Server fatal alert description"); + + if (config.expectFatalAlertConnectionEnd == -1) + { + Assert.IsNull(caught, "Unexpected client exception"); + Assert.IsNull(server.Caught, "Unexpected server exception"); + } + } + + protected void LogException(Exception e) + { + if (TlsTestConfig.Debug) + { + Console.Error.WriteLine(e); + Console.Error.Flush(); + } + } + + internal class Server + { + private readonly DtlsTestCase m_outer; + private readonly DtlsTestServerProtocol m_serverProtocol; + private readonly DatagramTransport m_serverTransport; + private readonly TlsTestServerImpl m_serverImpl; + + private volatile bool m_isShutdown = false; + private Exception m_caught = null; + + internal Server(DtlsTestCase outer, DtlsTestServerProtocol serverProtocol, + DatagramTransport serverTransport, TlsTestServerImpl serverImpl) + { + this.m_outer = outer; + this.m_serverProtocol = serverProtocol; + this.m_serverTransport = serverTransport; + this.m_serverImpl = serverImpl; + } + + public void Run() + { + try + { + DtlsTransport dtlsServer = m_serverProtocol.Accept(m_serverImpl, m_serverTransport); + byte[] buf = new byte[dtlsServer.GetReceiveLimit()]; + while (!m_isShutdown) + { + int length = dtlsServer.Receive(buf, 0, buf.Length, 100); + if (length >= 0) + { + dtlsServer.Send(buf, 0, length); + } + } + dtlsServer.Close(); + } + catch (Exception e) + { + this.m_caught = e; + m_outer.LogException(m_caught); + } + } + + internal void Shutdown(Thread serverThread) + { + if (!m_isShutdown) + { + this.m_isShutdown = true; + //serverThread.Interrupt(); + serverThread.Join(); + } + } + + internal Exception Caught + { + get { return m_caught; } + } + } + } +} diff --git a/crypto/test/src/tls/test/DtlsTestClientProtocol.cs b/crypto/test/src/tls/test/DtlsTestClientProtocol.cs new file mode 100644 index 000000000..99a7fa07b --- /dev/null +++ b/crypto/test/src/tls/test/DtlsTestClientProtocol.cs @@ -0,0 +1,27 @@ +using System; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class DtlsTestClientProtocol + : DtlsClientProtocol + { + protected readonly TlsTestConfig m_config; + + public DtlsTestClientProtocol(TlsTestConfig config) + : base() + { + this.m_config = config; + } + + protected override byte[] GenerateCertificateVerify(ClientHandshakeState state, + DigitallySigned certificateVerify) + { + if (certificateVerify.Algorithm != null && m_config.clientAuthSigAlgClaimed != null) + { + certificateVerify = new DigitallySigned(m_config.clientAuthSigAlgClaimed, certificateVerify.Signature); + } + + return base.GenerateCertificateVerify(state, certificateVerify); + } + } +} diff --git a/crypto/test/src/tls/test/DtlsTestServerProtocol.cs b/crypto/test/src/tls/test/DtlsTestServerProtocol.cs new file mode 100644 index 000000000..08fc2b6a9 --- /dev/null +++ b/crypto/test/src/tls/test/DtlsTestServerProtocol.cs @@ -0,0 +1,16 @@ +using System; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class DtlsTestServerProtocol + : DtlsServerProtocol + { + protected readonly TlsTestConfig m_config; + + public DtlsTestServerProtocol(TlsTestConfig config) + : base() + { + this.m_config = config; + } + } +} diff --git a/crypto/test/src/tls/test/DtlsTestSuite.cs b/crypto/test/src/tls/test/DtlsTestSuite.cs new file mode 100644 index 000000000..0af2be32c --- /dev/null +++ b/crypto/test/src/tls/test/DtlsTestSuite.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections; + +using NUnit.Framework; + +namespace Org.BouncyCastle.Tls.Tests +{ + public class DtlsTestSuite + { + // Make the access to constants less verbose + internal class C : TlsTestConfig {} + + public DtlsTestSuite() + { + } + + public static IEnumerable Suite() + { + IList testSuite = new ArrayList(); + + AddFallbackTests(testSuite); + AddVersionTests(testSuite, ProtocolVersion.DTLSv10); + AddVersionTests(testSuite, ProtocolVersion.DTLSv12); + + return testSuite; + } + + private static void AddFallbackTests(IList testSuite) + { + { + TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12); + c.clientFallback = true; + + AddTestCase(testSuite, c, "FallbackGood"); + } + + /* + * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit + * of the DTLS server after a fatal alert. As of writing, manual runs show the correct + * alerts being raised + */ + +#if false + { + TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12); + c.clientFallback = true; + c.clientSupportedVersions = ProtocolVersion.DTLSv10.Only(); + c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback); + + AddTestCase(testSuite, c, "FallbackBad"); + } +#endif + + { + TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12); + c.clientSupportedVersions = ProtocolVersion.DTLSv10.Only(); + + AddTestCase(testSuite, c, "FallbackNone"); + } + } + + private static void AddVersionTests(IList testSuite, ProtocolVersion version) + { + string prefix = version.ToString() + .Replace(" ", "") + .Replace("\\", "") + .Replace(".", "") + + "_"; + + /* + * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit + * of the DTLS server after a fatal alert. As of writing, manual runs show the correct + * alerts being raised + */ + +#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); + + 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 = CreateDtlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_VALID; + c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa); + c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms(); + c.serverCheckSigAlgOfClientCerts = false; + 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 = 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); + + AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch"); + } + + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY; + c.ExpectServerFatalAlert(AlertDescription.decrypt_error); + + 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"); + } + + /* + * Server sends SHA-256/RSA certificate, which is not the default {sha1,rsa} implied by the + * absent signature_algorithms extension. We expect fatal alert from the client when it + * verifies the certificate's 'signatureAlgorithm' against the implicit default signature_algorithms. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientSendSignatureAlgorithms = false; + c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.rsa); + c.ExpectClientFatalAlert(AlertDescription.certificate_unknown); + + AddTestCase(testSuite, c, prefix + "BadServerCertSigAlg"); + } + + /* + * Server selects MD5/RSA for ServerKeyExchange signature, which is not in the default + * supported signature algorithms that the client sent. We expect fatal alert from the + * client when it verifies the selected algorithm against the supported algorithms. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa); + c.ExpectClientFatalAlert(AlertDescription.illegal_parameter); + + AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg"); + } + + /* + * Server selects MD5/RSA for ServerKeyExchange signature, which is not the default {sha1,rsa} + * implied by the absent signature_algorithms extension. We expect fatal alert from the + * client when it verifies the selected algorithm against the implicit default. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientCheckSigAlgOfServerCerts = false; + c.clientSendSignatureAlgorithms = false; + c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa); + c.ExpectClientFatalAlert(AlertDescription.illegal_parameter); + + AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg2"); + } +#endif + + { + TlsTestConfig c = CreateDtlsTestConfig(version); + + AddTestCase(testSuite, c, prefix + "GoodDefault"); + } + + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.serverCertReq = C.SERVER_CERT_REQ_NONE; + + AddTestCase(testSuite, c, prefix + "GoodNoCertReq"); + } + + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.clientAuth = C.CLIENT_AUTH_NONE; + + AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined"); + } + +#if false + /* + * Server generates downgraded (RFC 8446) ServerHello. We expect fatal alert + * (illegal_parameter) from the client. + */ + if (!TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateDtlsTestConfig(version); + c.serverNegotiateVersion = version; + c.serverSupportedVersions = ProtocolVersion.DTLSv12.DownTo(version); + c.ExpectClientFatalAlert(AlertDescription.illegal_parameter); + + AddTestCase(testSuite, c, prefix + "BadDowngrade"); + } +#endif + } + + private static void AddTestCase(IList testSuite, TlsTestConfig config, string name) + { + testSuite.Add(new TestCaseData(config).SetName(name)); + } + + private static TlsTestConfig CreateDtlsTestConfig(ProtocolVersion serverMaxVersion) + { + TlsTestConfig c = new TlsTestConfig(); + c.clientSupportedVersions = ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10); + c.serverSupportedVersions = serverMaxVersion.DownTo(ProtocolVersion.DTLSv10); + return c; + } + + public static void RunTests() + { + foreach (TestCaseData data in Suite()) + { + Console.WriteLine(data.TestName); + new DtlsTestCase().RunTest((TlsTestConfig)data.Arguments[0]); + } + } + } +} diff --git a/crypto/test/src/tls/test/LoggingDatagramTransport.cs b/crypto/test/src/tls/test/LoggingDatagramTransport.cs new file mode 100644 index 000000000..f675b72fc --- /dev/null +++ b/crypto/test/src/tls/test/LoggingDatagramTransport.cs @@ -0,0 +1,87 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Tls.Tests +{ + public class LoggingDatagramTransport + : DatagramTransport + { + private static readonly string HEX_CHARS = "0123456789ABCDEF"; + + private readonly DatagramTransport m_transport; + private readonly TextWriter m_output; + private readonly long m_launchTimestamp; + + public LoggingDatagramTransport(DatagramTransport transport, TextWriter output) + { + this.m_transport = transport; + this.m_output = output; + this.m_launchTimestamp = DateTimeUtilities.CurrentUnixMs(); + } + + public virtual int GetReceiveLimit() + { + return m_transport.GetReceiveLimit(); + } + + public virtual int GetSendLimit() + { + return m_transport.GetSendLimit(); + } + + public virtual int Receive(byte[] buf, int off, int len, int waitMillis) + { + int length = m_transport.Receive(buf, off, len, waitMillis); + if (length >= 0) + { + DumpDatagram("Received", buf, off, length); + } + return length; + } + + public virtual void Send(byte[] buf, int off, int len) + { + DumpDatagram("Sending", buf, off, len); + m_transport.Send(buf, off, len); + } + + public virtual void Close() + { + m_transport.Close(); + } + + private void DumpDatagram(string verb, byte[] buf, int off, int len) + { + long timestamp = DateTimeUtilities.CurrentUnixMs() - m_launchTimestamp; + StringBuilder sb = new StringBuilder("(+" + timestamp + "ms) " + verb + " " + len + " byte datagram:"); + for (int pos = 0; pos < len; ++pos) + { + if (pos % 16 == 0) + { + sb.Append(Environment.NewLine); + sb.Append(" "); + } + else if (pos % 16 == 8) + { + sb.Append('-'); + } + else + { + sb.Append(' '); + } + int val = buf[off + pos] & 0xFF; + sb.Append(HEX_CHARS[val >> 4]); + sb.Append(HEX_CHARS[val & 0xF]); + } + Dump(sb.ToString()); + } + + private void Dump(string s) + { + lock (this) m_output.WriteLine(s); + } + } +} diff --git a/crypto/test/src/tls/test/MockDatagramAssociation.cs b/crypto/test/src/tls/test/MockDatagramAssociation.cs new file mode 100644 index 000000000..3e0c0f52b --- /dev/null +++ b/crypto/test/src/tls/test/MockDatagramAssociation.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections; +using System.Threading; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tls.Tests +{ + public class MockDatagramAssociation + { + private int m_mtu; + private MockDatagramTransport m_client, m_server; + + public MockDatagramAssociation(int mtu) + { + this.m_mtu = mtu; + + IList clientQueue = new ArrayList(); + IList serverQueue = new ArrayList(); + + this.m_client = new MockDatagramTransport(this, clientQueue, serverQueue); + this.m_server = new MockDatagramTransport(this, serverQueue, clientQueue); + } + + public virtual DatagramTransport Client + { + get { return m_client; } + } + + public virtual DatagramTransport Server + { + get { return m_server; } + } + + private class MockDatagramTransport + : DatagramTransport + { + private readonly MockDatagramAssociation m_outer; + private IList m_receiveQueue, m_sendQueue; + + internal MockDatagramTransport(MockDatagramAssociation outer, IList receiveQueue, IList sendQueue) + { + this.m_outer = outer; + this.m_receiveQueue = receiveQueue; + this.m_sendQueue = sendQueue; + } + + public virtual int GetReceiveLimit() + { + return m_outer.m_mtu; + } + + public virtual int GetSendLimit() + { + return m_outer.m_mtu; + } + + public virtual int Receive(byte[] buf, int off, int len, int waitMillis) + { + lock (m_receiveQueue) + { + if (m_receiveQueue.Count < 1) + { + try + { + Monitor.Wait(m_receiveQueue, waitMillis); + } + catch (ThreadInterruptedException) + { + // TODO Keep waiting until full wait expired? + } + + if (m_receiveQueue.Count < 1) + return -1; + } + + byte[] packet = (byte[])m_receiveQueue[0]; + m_receiveQueue.RemoveAt(0); + int copyLength = System.Math.Min(len, packet.Length); + Array.Copy(packet, 0, buf, off, copyLength); + return copyLength; + } + } + + public virtual void Send(byte[] buf, int off, int len) + { + if (len > m_outer.m_mtu) + { + // TODO Simulate rejection? + } + + byte[] packet = Arrays.CopyOfRange(buf, off, off + len); + + lock (m_sendQueue) + { + m_sendQueue.Add(packet); + Monitor.PulseAll(m_sendQueue); + } + } + + public virtual void Close() + { + // TODO? + } + } + } +} diff --git a/crypto/test/src/tls/test/MockDtlsClient.cs b/crypto/test/src/tls/test/MockDtlsClient.cs new file mode 100644 index 000000000..5aa1ebbd3 --- /dev/null +++ b/crypto/test/src/tls/test/MockDtlsClient.cs @@ -0,0 +1,170 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockDtlsClient + : DefaultTlsClient + { + internal TlsSession m_session; + + internal MockDtlsClient(TlsSession session) + : base(new BcTlsCrypto(new SecureRandom())) + { + this.m_session = session; + } + + public override TlsSession GetSessionToResume() + { + return this.m_session; + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("DTLS client raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("DTLS client received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override void NotifyServerVersion(ProtocolVersion serverVersion) + { + base.NotifyServerVersion(serverVersion); + + Console.WriteLine("DTLS client negotiated " + serverVersion); + } + + public override TlsAuthentication GetAuthentication() + { + return new MyTlsAuthentication(m_context); + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding()); + } + + TlsSession newSession = m_context.Session; + if (newSession != null) + { + if (newSession.IsResumable) + { + byte[] newSessionID = newSession.SessionID; + string hex = ToHexString(newSessionID); + + if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID)) + { + Console.WriteLine("Client resumed session: " + hex); + } + else + { + Console.WriteLine("Client established session: " + hex); + } + + this.m_session = newSession; + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + if (null != tlsServerEndPoint) + { + Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + } + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique)); + } + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + protected override ProtocolVersion[] GetSupportedVersions() + { + return ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10); + } + + internal class MyTlsAuthentication + : TlsAuthentication + { + private readonly TlsContext m_context; + + internal MyTlsAuthentication(TlsContext context) + { + this.m_context = context; + } + + public void NotifyServerCertificate(TlsServerCertificate serverCertificate) + { + TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList(); + + Console.WriteLine("DTLS client received server certificate chain of length " + chain.Length); + for (int i = 0; i != chain.Length; i++) + { + X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " (" + + entry.Subject + ")"); + } + + bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null + || serverCertificate.Certificate.IsEmpty; + + if (isEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + string[] trustedCertResources = new String[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem", + "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem", + "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem", + "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" }; + + TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0], + trustedCertResources); + + if (null == certPath) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + TlsUtilities.CheckPeerSigAlgs(m_context, certPath); + } + + public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) + { + short[] certificateTypes = certificateRequest.CertificateTypes; + if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign)) + return null; + + return TlsTestUtilities.LoadSignerCredentials(m_context, + certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa, "x509-client-rsa.pem", + "x509-client-key-rsa.pem"); + } + } + } +} diff --git a/crypto/test/src/tls/test/MockDtlsServer.cs b/crypto/test/src/tls/test/MockDtlsServer.cs new file mode 100644 index 000000000..18e53628e --- /dev/null +++ b/crypto/test/src/tls/test/MockDtlsServer.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockDtlsServer + : DefaultTlsServer + { + internal MockDtlsServer() + : base(new BcTlsCrypto(new SecureRandom())) + { + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("DTLS server raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("DTLS server received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override ProtocolVersion GetServerVersion() + { + ProtocolVersion serverVersion = base.GetServerVersion(); + + Console.WriteLine("DTLS server negotiated " + serverVersion); + + return serverVersion; + } + + public override CertificateRequest GetCertificateRequest() + { + short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign, + ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign }; + + IList serverSigAlgs = null; + if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion)) + { + serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context); + } + + IList certificateAuthorities = new ArrayList(); + //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-dsa.pem").Subject); + //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-ecdsa.pem").Subject); + //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-rsa.pem").Subject); + + // All the CA certificates are currently configured with this subject + certificateAuthorities.Add(new X509Name("CN=BouncyCastle TLS Test CA")); + + return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities); + } + + public override void NotifyClientCertificate(Certificate clientCertificate) + { + TlsCertificate[] chain = clientCertificate.GetCertificateList(); + + Console.WriteLine("DTLS server received client certificate chain of length " + chain.Length); + for (int i = 0; i != chain.Length; i++) + { + X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " (" + + entry.Subject + ")"); + } + + bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty); + + if (isEmpty) + return; + + string[] trustedCertResources = new string[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem", + "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem", + "x509-client-rsa_pss_256.pem", "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem", + "x509-client-rsa.pem" }; + + TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0], + trustedCertResources); + + if (null == certPath) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + TlsUtilities.CheckPeerSigAlgs(m_context, certPath); + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding()); + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique)); + } + + protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials() + { + return TlsTestUtilities.LoadEncryptionCredentials(m_context, + new string[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem"); + } + + protected override TlsCredentialedSigner GetRsaSignerCredentials() + { + IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs; + return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.rsa); + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + protected override ProtocolVersion[] GetSupportedVersions() + { + return ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10); + } + } +} diff --git a/crypto/test/src/tls/test/MockPskDtlsClient.cs b/crypto/test/src/tls/test/MockPskDtlsClient.cs new file mode 100644 index 000000000..c83c9e7fd --- /dev/null +++ b/crypto/test/src/tls/test/MockPskDtlsClient.cs @@ -0,0 +1,161 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockPskDtlsClient + : PskTlsClient + { + internal TlsSession m_session; + + internal MockPskDtlsClient(TlsSession session) + : this(session, new BasicTlsPskIdentity("client", Strings.ToUtf8ByteArray("TLS_TEST_PSK"))) + { + } + + internal MockPskDtlsClient(TlsSession session, TlsPskIdentity pskIdentity) + : base(new BcTlsCrypto(new SecureRandom()), pskIdentity) + { + this.m_session = session; + } + + public override TlsSession GetSessionToResume() + { + return m_session; + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("DTLS-PSK client raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("DTLS-PSK client received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override void NotifyServerVersion(ProtocolVersion serverVersion) + { + base.NotifyServerVersion(serverVersion); + + Console.WriteLine("DTLS-PSK client negotiated " + serverVersion); + } + + public override TlsAuthentication GetAuthentication() + { + return new MyTlsAuthentication(m_context); + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding()); + } + + TlsSession newSession = m_context.Session; + if (newSession != null) + { + if (newSession.IsResumable) + { + byte[] newSessionID = newSession.SessionID; + string hex = ToHexString(newSessionID); + + if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID)) + { + Console.WriteLine("Client resumed session: " + hex); + } + else + { + Console.WriteLine("Client established session: " + hex); + } + + this.m_session = newSession; + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + if (null != tlsServerEndPoint) + { + Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + } + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique)); + } + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + protected override ProtocolVersion[] GetSupportedVersions() + { + return ProtocolVersion.DTLSv12.Only(); + } + + internal class MyTlsAuthentication + : ServerOnlyTlsAuthentication + { + private readonly TlsContext m_context; + + internal MyTlsAuthentication(TlsContext context) + { + this.m_context = context; + } + + public override void NotifyServerCertificate(TlsServerCertificate serverCertificate) + { + TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList(); + + Console.WriteLine("DTLS-PSK client received server certificate chain of length " + chain.Length); + for (int i = 0; i != chain.Length; i++) + { + X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " (" + + entry.Subject + ")"); + } + + bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null + || serverCertificate.Certificate.IsEmpty; + + if (isEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + string[] trustedCertResources = new string[] { "x509-server-rsa-enc.pem" }; + + TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0], + trustedCertResources); + + if (null == certPath) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + TlsUtilities.CheckPeerSigAlgs(m_context, certPath); + } + } + } +} diff --git a/crypto/test/src/tls/test/MockPskDtlsServer.cs b/crypto/test/src/tls/test/MockPskDtlsServer.cs new file mode 100644 index 000000000..bb084535a --- /dev/null +++ b/crypto/test/src/tls/test/MockPskDtlsServer.cs @@ -0,0 +1,113 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockPskDtlsServer + : PskTlsServer + { + internal MockPskDtlsServer() + : base(new BcTlsCrypto(new SecureRandom()), new MyIdentityManager()) + { + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("DTLS-PSK server raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("DTLS-PSK server received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override ProtocolVersion GetServerVersion() + { + ProtocolVersion serverVersion = base.GetServerVersion(); + + Console.WriteLine("DTLS-PSK server negotiated " + serverVersion); + + return serverVersion; + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding()); + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique)); + + byte[] pskIdentity = m_context.SecurityParameters.PskIdentity; + if (pskIdentity != null) + { + string name = Strings.FromUtf8ByteArray(pskIdentity); + Console.WriteLine("DTLS-PSK server completed handshake for PSK identity: " + name); + } + } + + protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials() + { + return TlsTestUtilities.LoadEncryptionCredentials(m_context, + new string[] { "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem"); + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + protected override ProtocolVersion[] GetSupportedVersions() + { + return ProtocolVersion.DTLSv12.Only(); + } + + internal class MyIdentityManager + : TlsPskIdentityManager + { + public byte[] GetHint() + { + return Strings.ToUtf8ByteArray("hint"); + } + + public byte[] GetPsk(byte[] identity) + { + if (identity != null) + { + string name = Strings.FromUtf8ByteArray(identity); + if (name.Equals("client")) + { + return Strings.ToUtf8ByteArray("TLS_TEST_PSK"); + } + } + return null; + } + } + } +} diff --git a/crypto/test/src/tls/test/MockPskTlsClient.cs b/crypto/test/src/tls/test/MockPskTlsClient.cs new file mode 100644 index 000000000..46774266b --- /dev/null +++ b/crypto/test/src/tls/test/MockPskTlsClient.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockPskTlsClient + : PskTlsClient + { + internal TlsSession m_session; + + internal MockPskTlsClient(TlsSession session) + : this(session, new BasicTlsPskIdentity("client", Strings.ToUtf8ByteArray("TLS_TEST_PSK"))) + { + } + + internal MockPskTlsClient(TlsSession session, TlsPskIdentity pskIdentity) + : base(new BcTlsCrypto(new SecureRandom()), pskIdentity) + { + this.m_session = session; + } + + protected override IList GetProtocolNames() + { + IList protocolNames = new ArrayList(); + protocolNames.Add(ProtocolName.Http_1_1); + protocolNames.Add(ProtocolName.Http_2_Tls); + return protocolNames; + } + + public override TlsSession GetSessionToResume() + { + return m_session; + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS-PSK client raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS-PSK client received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override IDictionary GetClientExtensions() + { + IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised( + base.GetClientExtensions()); + + { + /* + * NOTE: If you are copying test code, do not blindly set these extensions in your own client. + */ + TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9); + TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, m_context.Crypto.SecureRandom.Next(16)); + TlsExtensionsUtilities.AddTruncatedHmacExtension(clientExtensions); + } + return clientExtensions; + } + + public override void NotifyServerVersion(ProtocolVersion serverVersion) + { + base.NotifyServerVersion(serverVersion); + + Console.WriteLine("TLS-PSK client negotiated " + serverVersion); + } + + public override TlsAuthentication GetAuthentication() + { + return new MyTlsAuthentication(m_context); + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding()); + } + + TlsSession newSession = m_context.Session; + if (newSession != null) + { + if (newSession.IsResumable) + { + byte[] newSessionID = newSession.SessionID; + string hex = ToHexString(newSessionID); + + if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID)) + { + Console.WriteLine("Client resumed session: " + hex); + } + else + { + Console.WriteLine("Client established session: " + hex); + } + + this.m_session = newSession; + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + if (null != tlsServerEndPoint) + { + Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + } + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique)); + } + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + protected override ProtocolVersion[] GetSupportedVersions() + { + return ProtocolVersion.TLSv12.Only(); + } + + internal class MyTlsAuthentication + : ServerOnlyTlsAuthentication + { + private readonly TlsContext m_context; + + internal MyTlsAuthentication(TlsContext context) + { + this.m_context = context; + } + + public override void NotifyServerCertificate(TlsServerCertificate serverCertificate) + { + TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList(); + + Console.WriteLine("TLS-PSK client received server certificate chain of length " + chain.Length); + for (int i = 0; i != chain.Length; i++) + { + X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " (" + + entry.Subject + ")"); + } + + bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null + || serverCertificate.Certificate.IsEmpty; + + if (isEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + string[] trustedCertResources = new string[] { "x509-server-rsa-enc.pem" }; + + TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0], + trustedCertResources); + + if (null == certPath) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + TlsUtilities.CheckPeerSigAlgs(m_context, certPath); + } + }; + } +} diff --git a/crypto/test/src/tls/test/MockPskTlsServer.cs b/crypto/test/src/tls/test/MockPskTlsServer.cs new file mode 100644 index 000000000..743073b04 --- /dev/null +++ b/crypto/test/src/tls/test/MockPskTlsServer.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockPskTlsServer + : PskTlsServer + { + internal MockPskTlsServer() + : base(new BcTlsCrypto(new SecureRandom()), new MyIdentityManager()) + { + } + + protected override IList GetProtocolNames() + { + IList protocolNames = new ArrayList(); + protocolNames.Add(ProtocolName.Http_2_Tls); + protocolNames.Add(ProtocolName.Http_1_1); + return protocolNames; + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS-PSK server raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS-PSK server received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override ProtocolVersion GetServerVersion() + { + ProtocolVersion serverVersion = base.GetServerVersion(); + + Console.WriteLine("TLS-PSK server negotiated " + serverVersion); + + return serverVersion; + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding()); + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique)); + + byte[] pskIdentity = m_context.SecurityParameters.PskIdentity; + if (pskIdentity != null) + { + string name = Strings.FromUtf8ByteArray(pskIdentity); + Console.WriteLine("TLS-PSK server completed handshake for PSK identity: " + name); + } + } + + protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials() + { + return TlsTestUtilities.LoadEncryptionCredentials(m_context, + new string[] { "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem"); + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + protected override ProtocolVersion[] GetSupportedVersions() + { + return ProtocolVersion.TLSv12.Only(); + } + + internal class MyIdentityManager + : TlsPskIdentityManager + { + public byte[] GetHint() + { + return Strings.ToUtf8ByteArray("hint"); + } + + public byte[] GetPsk(byte[] identity) + { + if (identity != null) + { + string name = Strings.FromUtf8ByteArray(identity); + if (name.Equals("client")) + { + return Strings.ToUtf8ByteArray("TLS_TEST_PSK"); + } + } + return null; + } + } + } +} diff --git a/crypto/test/src/tls/test/MockSrpTlsClient.cs b/crypto/test/src/tls/test/MockSrpTlsClient.cs new file mode 100644 index 000000000..3d2232893 --- /dev/null +++ b/crypto/test/src/tls/test/MockSrpTlsClient.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockSrpTlsClient + : SrpTlsClient + { + internal TlsSession m_session; + + internal MockSrpTlsClient(TlsSession session, TlsSrpIdentity srpIdentity) + : base(new BcTlsCrypto(new SecureRandom()), srpIdentity) + { + this.m_session = session; + } + + protected override IList GetProtocolNames() + { + IList protocolNames = new ArrayList(); + protocolNames.Add(ProtocolName.Http_1_1); + protocolNames.Add(ProtocolName.Http_2_Tls); + return protocolNames; + } + + public override TlsSession GetSessionToResume() + { + return m_session; + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS-SRP client raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS-SRP client received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override IDictionary GetClientExtensions() + { + IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised( + base.GetClientExtensions()); + + { + /* + * NOTE: If you are copying test code, do not blindly set these extensions in your own client. + */ + TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9); + TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, m_context.Crypto.SecureRandom.Next(16)); + TlsExtensionsUtilities.AddTruncatedHmacExtension(clientExtensions); + } + return clientExtensions; + } + + public override void NotifyServerVersion(ProtocolVersion serverVersion) + { + base.NotifyServerVersion(serverVersion); + + Console.WriteLine("TLS-SRP client negotiated " + serverVersion); + } + + public override TlsAuthentication GetAuthentication() + { + return new MyTlsAuthentication(m_context); + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding()); + } + + TlsSession newSession = m_context.Session; + if (newSession != null) + { + if (newSession.IsResumable) + { + byte[] newSessionID = newSession.SessionID; + string hex = ToHexString(newSessionID); + + if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID)) + { + Console.WriteLine("Client resumed session: " + hex); + } + else + { + Console.WriteLine("Client established session: " + hex); + } + + this.m_session = newSession; + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + if (null != tlsServerEndPoint) + { + Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + } + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique)); + } + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + protected override ProtocolVersion[] GetSupportedVersions() + { + return ProtocolVersion.TLSv12.Only(); + } + + internal class MyTlsAuthentication + : ServerOnlyTlsAuthentication + { + private readonly TlsContext m_context; + + internal MyTlsAuthentication(TlsContext context) + { + this.m_context = context; + } + + public override void NotifyServerCertificate(TlsServerCertificate serverCertificate) + { + TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList(); + + Console.WriteLine("TLS-SRP client received server certificate chain of length " + chain.Length); + for (int i = 0; i != chain.Length; i++) + { + X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " (" + + entry.Subject + ")"); + } + + bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null + || serverCertificate.Certificate.IsEmpty; + + if (isEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + string[] trustedCertResources = new string[] { "x509-server-dsa.pem", "x509-server-rsa_pss_256.pem", + "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem", "x509-server-rsa-sign.pem" }; + + TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0], + trustedCertResources); + + if (null == certPath) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + TlsUtilities.CheckPeerSigAlgs(m_context, certPath); + } + }; + } +} diff --git a/crypto/test/src/tls/test/MockSrpTlsServer.cs b/crypto/test/src/tls/test/MockSrpTlsServer.cs new file mode 100644 index 000000000..725901811 --- /dev/null +++ b/crypto/test/src/tls/test/MockSrpTlsServer.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto.Agreement.Srp; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockSrpTlsServer + : SrpTlsServer + { + internal static readonly Srp6Group TEST_GROUP = Tls.Crypto.Srp6StandardGroups.rfc5054_1024; + internal static readonly byte[] TEST_IDENTITY = Strings.ToUtf8ByteArray("client"); + internal static readonly byte[] TEST_PASSWORD = Strings.ToUtf8ByteArray("password"); + internal static readonly TlsSrpIdentity TEST_SRP_IDENTITY = new BasicTlsSrpIdentity(TEST_IDENTITY, + TEST_PASSWORD); + internal static readonly byte[] TEST_SALT = Strings.ToUtf8ByteArray("salt"); + internal static readonly byte[] TEST_SEED_KEY = Strings.ToUtf8ByteArray("seed_key"); + + internal MockSrpTlsServer() + : base(new BcTlsCrypto(new SecureRandom()), new MyIdentityManager(new BcTlsCrypto(new SecureRandom()))) + { + } + + protected override IList GetProtocolNames() + { + IList protocolNames = new ArrayList(); + protocolNames.Add(ProtocolName.Http_2_Tls); + protocolNames.Add(ProtocolName.Http_1_1); + return protocolNames; + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS-SRP server raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS-SRP server received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override ProtocolVersion GetServerVersion() + { + ProtocolVersion serverVersion = base.GetServerVersion(); + + Console.WriteLine("TLS-SRP server negotiated " + serverVersion); + + return serverVersion; + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding()); + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique)); + + byte[] srpIdentity = m_context.SecurityParameters.SrpIdentity; + if (srpIdentity != null) + { + string name = Strings.FromUtf8ByteArray(srpIdentity); + Console.WriteLine("TLS-SRP server completed handshake for SRP identity: " + name); + } + } + + protected override TlsCredentialedSigner GetDsaSignerCredentials() + { + IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs; + return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.dsa); + } + + protected override TlsCredentialedSigner GetRsaSignerCredentials() + { + IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs; + return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.rsa); + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + internal class MyIdentityManager + : TlsSrpIdentityManager + { + protected SimulatedTlsSrpIdentityManager m_unknownIdentityManager; + + internal MyIdentityManager(TlsCrypto crypto) + { + m_unknownIdentityManager = SimulatedTlsSrpIdentityManager.GetRfc5054Default(crypto, TEST_GROUP, + TEST_SEED_KEY); + } + + public TlsSrpLoginParameters GetLoginParameters(byte[] identity) + { + if (Arrays.ConstantTimeAreEqual(TEST_IDENTITY, identity)) + { + Srp6VerifierGenerator verifierGenerator = new Srp6VerifierGenerator(); + verifierGenerator.Init(TEST_GROUP.N, TEST_GROUP.G, new Sha1Digest()); + + BigInteger verifier = verifierGenerator.GenerateVerifier(TEST_SALT, identity, TEST_PASSWORD); + + TlsSrpConfig srpConfig = new TlsSrpConfig(); + srpConfig.SetExplicitNG(new BigInteger[]{ TEST_GROUP.N, TEST_GROUP.G }); + + return new TlsSrpLoginParameters(identity, srpConfig, verifier, TEST_SALT); + } + + return m_unknownIdentityManager.GetLoginParameters(identity); + } + } + } +} diff --git a/crypto/test/src/tls/test/MockTlsClient.cs b/crypto/test/src/tls/test/MockTlsClient.cs new file mode 100644 index 000000000..62b699590 --- /dev/null +++ b/crypto/test/src/tls/test/MockTlsClient.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockTlsClient + : DefaultTlsClient + { + internal TlsSession m_session; + + internal MockTlsClient(TlsSession session) + : base(new BcTlsCrypto(new SecureRandom())) + { + this.m_session = session; + } + + protected override IList GetProtocolNames() + { + IList protocolNames = new ArrayList(); + protocolNames.Add(ProtocolName.Http_1_1); + protocolNames.Add(ProtocolName.Http_2_Tls); + return protocolNames; + } + + public override TlsSession GetSessionToResume() + { + return m_session; + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS client raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS client received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override IDictionary GetClientExtensions() + { + IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised( + base.GetClientExtensions()); + + { + /* + * NOTE: If you are copying test code, do not blindly set these extensions in your own client. + */ + TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9); + TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, m_context.Crypto.SecureRandom.Next(16)); + TlsExtensionsUtilities.AddTruncatedHmacExtension(clientExtensions); + } + return clientExtensions; + } + + public override void NotifyServerVersion(ProtocolVersion serverVersion) + { + base.NotifyServerVersion(serverVersion); + + Console.WriteLine("TLS client negotiated " + serverVersion); + } + + public override TlsAuthentication GetAuthentication() + { + return new MyTlsAuthentication(m_context); + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding()); + } + + TlsSession newSession = m_context.Session; + if (newSession != null) + { + if (newSession.IsResumable) + { + byte[] newSessionID = newSession.SessionID; + string hex = ToHexString(newSessionID); + + if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID)) + { + Console.WriteLine("Client resumed session: " + hex); + } + else + { + Console.WriteLine("Client established session: " + hex); + } + + this.m_session = newSession; + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + if (null != tlsServerEndPoint) + { + Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + } + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique)); + } + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + internal class MyTlsAuthentication + : TlsAuthentication + { + private readonly TlsContext m_context; + + internal MyTlsAuthentication(TlsContext context) + { + this.m_context = context; + } + + public void NotifyServerCertificate(TlsServerCertificate serverCertificate) + { + TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList(); + + Console.WriteLine("TLS client received server certificate chain of length " + chain.Length); + for (int i = 0; i != chain.Length; i++) + { + X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " (" + + entry.Subject + ")"); + } + + bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null + || serverCertificate.Certificate.IsEmpty; + + if (isEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + string[] trustedCertResources = new string[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem", + "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem", + "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem", + "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" }; + + TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0], + trustedCertResources); + + if (null == certPath) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + TlsUtilities.CheckPeerSigAlgs(m_context, certPath); + } + + public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) + { + short[] certificateTypes = certificateRequest.CertificateTypes; + if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign)) + return null; + + return TlsTestUtilities.LoadSignerCredentials(m_context, + certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa, "x509-client-rsa.pem", + "x509-client-key-rsa.pem"); + } + }; + } +} diff --git a/crypto/test/src/tls/test/MockTlsServer.cs b/crypto/test/src/tls/test/MockTlsServer.cs new file mode 100644 index 000000000..94d4c7dfd --- /dev/null +++ b/crypto/test/src/tls/test/MockTlsServer.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class MockTlsServer + : DefaultTlsServer + { + internal MockTlsServer() + : base(new BcTlsCrypto(new SecureRandom())) + { + } + + protected override IList GetProtocolNames() + { + IList protocolNames = new ArrayList(); + protocolNames.Add(ProtocolName.Http_2_Tls); + protocolNames.Add(ProtocolName.Http_1_1); + return protocolNames; + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS server raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS server received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + + public override ProtocolVersion GetServerVersion() + { + ProtocolVersion serverVersion = base.GetServerVersion(); + + Console.WriteLine("TLS server negotiated " + serverVersion); + + return serverVersion; + } + + public override CertificateRequest GetCertificateRequest() + { + short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign, + ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign }; + + IList serverSigAlgs = null; + if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion)) + { + serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context); + } + + IList certificateAuthorities = new ArrayList(); + //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-dsa.pem").Subject); + //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-ecdsa.pem").Subject); + //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-rsa.pem").Subject); + + // All the CA certificates are currently configured with this subject + certificateAuthorities.Add(new X509Name("CN=BouncyCastle TLS Test CA")); + + return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities); + } + + public override void NotifyClientCertificate(Certificate clientCertificate) + { + TlsCertificate[] chain = clientCertificate.GetCertificateList(); + + Console.WriteLine("TLS server received client certificate chain of length " + chain.Length); + for (int i = 0; i < chain.Length; ++i) + { + X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " (" + + entry.Subject + ")"); + } + + bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty); + + if (isEmpty) + return; + + string[] trustedCertResources = new string[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem", + "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem", + "x509-client-rsa_pss_256.pem", "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem", + "x509-client-rsa.pem" }; + + TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0], + trustedCertResources); + + if (null == certPath) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + TlsUtilities.CheckPeerSigAlgs(m_context, certPath); + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol; + if (protocolName != null) + { + Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding()); + } + + byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint)); + + byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique)); + } + + protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials() + { + return TlsTestUtilities.LoadEncryptionCredentials(m_context, + new string[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem"); + } + + protected override TlsCredentialedSigner GetRsaSignerCredentials() + { + IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs; + return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.rsa); + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + } +} diff --git a/crypto/test/src/tls/test/NetworkStream.cs b/crypto/test/src/tls/test/NetworkStream.cs new file mode 100644 index 000000000..c5f1dfd59 --- /dev/null +++ b/crypto/test/src/tls/test/NetworkStream.cs @@ -0,0 +1,101 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class NetworkStream + : Stream + { + private readonly Stream m_inner; + private bool m_closed = false; + + internal NetworkStream(Stream inner) + { + this.m_inner = inner; + } + + internal virtual bool IsClosed + { + get { lock (this) return m_closed; } + } + + public override bool CanRead + { + get { return m_inner.CanRead; } + } + + public override bool CanSeek + { + get { return m_inner.CanSeek; } + } + + public override bool CanWrite + { + get { return m_inner.CanWrite; } + } + + public override void Close() + { + lock (this) m_closed = true; + } + + public override void Flush() + { + m_inner.Flush(); + } + + public override long Length + { + get { return m_inner.Length; } + } + + public override long Position + { + get { return m_inner.Position; } + set { m_inner.Position = value; } + } + + public override long Seek(long offset, SeekOrigin origin) + { + return m_inner.Seek(offset, origin); + } + + public override void SetLength(long value) + { + m_inner.SetLength(value); + } + + public override int Read(byte[] buffer, int offset, int count) + { + CheckNotClosed(); + return m_inner.Read(buffer, offset, count); + } + + public override int ReadByte() + { + CheckNotClosed(); + return m_inner.ReadByte(); + } + + public override void Write(byte[] buf, int off, int len) + { + CheckNotClosed(); + m_inner.Write(buf, off, len); + } + + public override void WriteByte(byte value) + { + CheckNotClosed(); + m_inner.WriteByte(value); + } + + private void CheckNotClosed() + { + lock (this) + { + if (m_closed) + throw new ObjectDisposedException(this.GetType().Name); + } + } + } +} diff --git a/crypto/test/src/tls/test/PipedStream.cs b/crypto/test/src/tls/test/PipedStream.cs new file mode 100644 index 000000000..6de5703e1 --- /dev/null +++ b/crypto/test/src/tls/test/PipedStream.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Threading; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class PipedStream + : Stream + { + private readonly MemoryStream m_buf = new MemoryStream(); + private bool m_closed = false; + + private PipedStream m_other = null; + private long m_readPos = 0; + + internal PipedStream() + { + } + + internal PipedStream(PipedStream other) + { + lock (other) + { + this.m_other = other; + other.m_other = this; + } + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return false; } + } + + public override bool CanWrite + { + get { return true; } + } + + public override void Close() + { + lock (this) + { + m_closed = true; + Monitor.PulseAll(this); + } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotImplementedException(); } + } + + public override long Position + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + lock (m_other) + { + WaitForData(); + int len = (int)System.Math.Min(count, m_other.m_buf.Position - m_readPos); + Array.Copy(m_other.m_buf.GetBuffer(), m_readPos, buffer, offset, len); + m_readPos += len; + return len; + } + } + + public override int ReadByte() + { + lock (m_other) + { + WaitForData(); + bool eof = (m_readPos >= m_other.m_buf.Position); + return eof ? -1 : m_other.m_buf.GetBuffer()[m_readPos++]; + } + } + + public override void Write(byte[] buf, int off, int len) + { + lock (this) + { + CheckOpen(); + m_buf.Write(buf, off, len); + Monitor.PulseAll(this); + } + } + + public override void WriteByte(byte value) + { + lock (this) + { + CheckOpen(); + m_buf.WriteByte(value); + Monitor.PulseAll(m_buf); + } + } + + private void CheckOpen() + { + if (m_closed) + throw new ObjectDisposedException(this.GetType().Name); + } + + private void WaitForData() + { + while (m_readPos >= m_other.m_buf.Position && !m_other.m_closed) + { + Monitor.Wait(m_other); + } + } + } +} diff --git a/crypto/test/src/tls/test/PrfTest.cs b/crypto/test/src/tls/test/PrfTest.cs new file mode 100644 index 000000000..de00166ed --- /dev/null +++ b/crypto/test/src/tls/test/PrfTest.cs @@ -0,0 +1,97 @@ +using System; + +using NUnit.Framework; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class PrfTest + { + [Test] + public void TestLwTls11() + { + byte[] pre_master_secret = Hex.Decode("86051948e4d9a0cd273b6cd3a76557fc695e2ad9517cda97081ed009588a20ab48d0b128de8f917da74e711879460b60"); + byte[] serverHello_random = Hex.Decode("55f1f273d4cdd4abb97f6856ed10f83a799dc42403c3f60c4e504419db4fd727"); + byte[] clientHello_random = Hex.Decode("0b71e1f7232e675112510cf654a5e6280b3bd8ff078b67ec55276bfaddb92075"); + byte[] server_random = Hex.Decode("a62615ee7fee41993588b2542735f90910c5a0f9c5dcb64898fdf3e90dc72a5f"); + byte[] client_random = Hex.Decode("7798a130b732d7789e59a5fc14ad331ae91199f7d122e7fd4a594036b0694873"); + byte[] master_secret = Hex.Decode("37841ef801f8cbdb49b6a164025de3e0ea8169604ffe80bd98b45cdd34105251cedac7223045ff4c7b67c8a12bf3141c"); + byte[] key_block = Hex.Decode("c520e2409fa54facd3da01910f50a28f2f50986beb56b0c7b4cee9122e8f7428b7f7b8277bda931c71d35fdc2ea92127a5a143f63fe145275af5bcdab26113deffbb87a67f965b3964ea1ca29df1841c1708e6f42aacd87c12c4471913f61bb994fe3790b735dd11"); + + byte[] msSeed = Arrays.Concatenate(clientHello_random, serverHello_random); + + BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom()); + TlsSecret masterSecret = new BcTlsSecret(crypto, pre_master_secret) + .DeriveUsingPrf(PrfAlgorithm.tls_prf_legacy, ExporterLabel.master_secret, msSeed, master_secret.Length); + + Assert.IsTrue(Arrays.AreEqual(master_secret, masterSecret.Extract()), "master secret wrong"); + + byte[] keSeed = Arrays.Concatenate(server_random, client_random); + + TlsSecret keyExpansion = new BcTlsSecret(crypto, master_secret) + .DeriveUsingPrf(PrfAlgorithm.tls_prf_legacy, ExporterLabel.key_expansion, keSeed, key_block.Length); + + Assert.IsTrue(Arrays.AreEqual(key_block, keyExpansion.Extract()), "key expansion error"); + } + + [Test] + public void TestLwTls12_Sha256Prf() + { + byte[] pre_master_secret = Hex.Decode("f8938ecc9edebc5030c0c6a441e213cd24e6f770a50dda07876f8d55da062bcadb386b411fd4fe4313a604fce6c17fbc"); + byte[] serverHello_random = Hex.Decode("f6c9575ed7ddd73e1f7d16eca115415812a43c2b747daaaae043abfb50053fce"); + byte[] clientHello_random = Hex.Decode("36c129d01a3200894b9179faac589d9835d58775f9b5ea3587cb8fd0364cae8c"); + byte[] server_random = Hex.Decode("ae6c806f8ad4d80784549dff28a4b58fd837681a51d928c3e30ee5ff14f39868"); + byte[] client_random = Hex.Decode("62e1fd91f23f558a605f28478c58cf72637b89784d959df7e946d3f07bd1b616"); + byte[] master_secret = Hex.Decode("202c88c00f84a17a20027079604787461176455539e705be730890602c289a5001e34eeb3a043e5d52a65e66125188bf"); + byte[] key_block = Hex.Decode("d06139889fffac1e3a71865f504aa5d0d2a2e89506c6f2279b670c3e1b74f531016a2530c51a3a0f7e1d6590d0f0566b2f387f8d11fd4f731cdd572d2eae927f6f2f81410b25e6960be68985add6c38445ad9f8c64bf8068bf9a6679485d966f1ad6f68b43495b10a683755ea2b858d70ccac7ec8b053c6bd41ca299d4e51928"); + + byte[] msSeed = Arrays.Concatenate(clientHello_random, serverHello_random); + + BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom()); + TlsSecret masterSecret = new BcTlsSecret(crypto, pre_master_secret) + .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha256, ExporterLabel.master_secret, msSeed, master_secret.Length); + + Assert.IsTrue(Arrays.AreEqual(master_secret, masterSecret.Extract()), "master secret wrong"); + + byte[] keSeed = Arrays.Concatenate(server_random, client_random); + + TlsSecret keyExpansion = new BcTlsSecret(crypto, master_secret) + .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha256, ExporterLabel.key_expansion, keSeed, key_block.Length); + + Assert.IsTrue(Arrays.AreEqual(key_block, keyExpansion.Extract()), "key expansion error"); + } + + [Test] + public void TestLwTls12_Sha384Prf() + { + byte[] pre_master_secret = Hex.Decode("a5e2642633f5b8c81ad3fe0c2fe3a8e5ef806b06121dd10df4bb0fe857bfdcf522558e05d2682c9a80c741a3aab1716f"); + byte[] serverHello_random = Hex.Decode("cb6e0b3eb02976b6466dfa9651c2919414f1648fd3a7838d02153e5bd39535b6"); + byte[] clientHello_random = Hex.Decode("abe4bf5527429ac8eb13574d2709e8012bd1a113c6d3b1d3aa2c3840518778ac"); + byte[] server_random = Hex.Decode("1b1c8568344a65c30828e7483c0e353e2c68641c9551efae6927d9cd627a107c"); + byte[] client_random = Hex.Decode("954b5fe1849c2ede177438261f099a2fcd884d001b9fe1de754364b1f6a6dd8e"); + byte[] master_secret = Hex.Decode("b4d49bfa87747fe815457bc3da15073d6ac73389e703079a3503c09e14bd559a5b3c7c601c7365f6ea8c68d3d9596827"); + byte[] key_block = Hex.Decode("10fd89ef689c7ef033387b8a8f3e5e8e7c11f680f6bdd71fbac3246a73e98d45d03185dde686e6b2369e4503e9dc5a6d2cee3e2bf2fa3f41d3de57dff3e197c8a9d5f74cc2d277119d894f8584b07a0a5822f0bd68b3433ec6adaf5c9406c5f3ddbb71bbe17ce98f3d4d5893d3179ef369f57aad908e2bf710639100c3ce7e0c"); + + byte[] msSeed = Arrays.Concatenate(clientHello_random, serverHello_random); + + BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom()); + TlsSecret masterSecret = new BcTlsSecret(crypto, pre_master_secret) + .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha384, ExporterLabel.master_secret, msSeed, master_secret.Length); + + Assert.IsTrue(Arrays.AreEqual(master_secret, masterSecret.Extract()), "master secret wrong"); + + byte[] keSeed = Arrays.Concatenate(server_random, client_random); + + TlsSecret keyExpansion = new BcTlsSecret(crypto, master_secret) + .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha384, ExporterLabel.key_expansion, keSeed, key_block.Length); + + Assert.IsTrue(Arrays.AreEqual(key_block, keyExpansion.Extract()), "key expansion error"); + } + } +} diff --git a/crypto/test/src/tls/test/PskTlsClientTest.cs b/crypto/test/src/tls/test/PskTlsClientTest.cs new file mode 100644 index 000000000..62cfc0dc2 --- /dev/null +++ b/crypto/test/src/tls/test/PskTlsClientTest.cs @@ -0,0 +1,113 @@ +using System; +using System.IO; +using System.Net.Sockets; +using System.Text; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Tls.Tests +{ + /// <summary>A simple test designed to conduct a TLS handshake with an external TLS server.</summary> + /// <remarks> + /// Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in this package + /// (under 'src/test/resources') for help configuring an external TLS server.<br/<br/ + /// In both cases, extra options are required to enable PSK ciphersuites and configure identities/keys. + /// </remarks> + [TestFixture] + public class PskTlsClientTest + { + [Test, Ignore] + public void TestConnection() + { + string host = "localhost"; + int port = 5556; + + long time1 = DateTimeUtilities.CurrentUnixMs(); + + /* + * Note: This is the default PSK identity for 'openssl s_server' testing, the server must be + * started with "-psk 6161616161" to make the keys match, and possibly the "-psk_hint" + * option should be present. + */ + //string psk_identity = "Client_identity"; + //byte[] psk = new byte[] { 0x61, 0x61, 0x61, 0x61, 0x61 }; + + // These correspond to the configuration of MockPskTlsServer + string psk_identity = "client"; + byte[] psk = Strings.ToUtf8ByteArray("TLS_TEST_PSK"); + + BasicTlsPskIdentity pskIdentity = new BasicTlsPskIdentity(psk_identity, psk); + + MockPskTlsClient client = new MockPskTlsClient(null, pskIdentity); + TlsClientProtocol protocol = OpenTlsClientConnection(host, port, client); + protocol.Close(); + + long time2 = DateTimeUtilities.CurrentUnixMs(); + Console.WriteLine("Elapsed 1: " + (time2 - time1) + "ms"); + + client = new MockPskTlsClient(client.GetSessionToResume(), pskIdentity); + protocol = OpenTlsClientConnection(host, port, client); + + long time3 = DateTimeUtilities.CurrentUnixMs(); + Console.WriteLine("Elapsed 2: " + (time3 - time2) + "ms"); + + Http11Get(host, port, protocol.Stream); + + protocol.Close(); + } + + private static void Http11Get(string host, int port, Stream s) + { + WriteUtf8Line(s, "GET / HTTP/1.1"); + //WriteUtf8Line(s, "Host: " + host + ":" + port); + WriteUtf8Line(s, ""); + s.Flush(); + + Console.WriteLine("---"); + + string[] ends = new string[] { "</HTML>", "HTTP/1.1 3", "HTTP/1.1 4" }; + + StreamReader reader = new StreamReader(s); + + bool finished = false; + string line; + while (!finished && (line = reader.ReadLine()) != null) + { + Console.WriteLine("<<< " + line); + + string upperLine = TlsTestUtilities.ToUpperInvariant(line); + + // TEST CODE ONLY. This is not a robust way of parsing the result! + foreach (string end in ends) + { + if (upperLine.Contains(end)) + { + finished = true; + break; + } + } + } + + Console.Out.Flush(); + } + + private static TlsClientProtocol OpenTlsClientConnection(string hostname, int port, TlsClient client) + { + TcpClient tcp = new TcpClient(hostname, port); + + TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream()); + protocol.Connect(client); + return protocol; + } + + private static void WriteUtf8Line(Stream output, string line) + { + byte[] buf = Encoding.UTF8.GetBytes(line + "\r\n"); + output.Write(buf, 0, buf.Length); + Console.WriteLine(">>> " + line); + } + } +} diff --git a/crypto/test/src/tls/test/PskTlsServerTest.cs b/crypto/test/src/tls/test/PskTlsServerTest.cs new file mode 100644 index 000000000..9d87a8d35 --- /dev/null +++ b/crypto/test/src/tls/test/PskTlsServerTest.cs @@ -0,0 +1,82 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Tls.Tests +{ + /// <summary>A simple test designed to conduct a TLS handshake with an external TLS client.</summary> + /// <remarks> + /// Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in this package + /// (under 'src/test/resources') for help configuring an external TLS client. + /// </remarks> + [TestFixture] + public class PskTlsServerTest + { + [Test, Ignore] + public void TestConnection() + { + int port = 5556; + + TcpListener ss = new TcpListener(IPAddress.Any, port); + ss.Start(); + Stream stdout = Console.OpenStandardOutput(); + try + { + while (true) + { + TcpClient s = ss.AcceptTcpClient(); + Console.WriteLine("--------------------------------------------------------------------------------"); + Console.WriteLine("Accepted " + s); + Server serverRun = new Server(s, stdout); + Thread t = new Thread(new ThreadStart(serverRun.Run)); + t.Start(); + } + } + finally + { + ss.Stop(); + } + } + + internal class Server + { + private readonly TcpClient s; + private readonly Stream stdout; + + internal Server(TcpClient s, Stream stdout) + { + this.s = s; + this.stdout = stdout; + } + + public void Run() + { + try + { + MockPskTlsServer server = new MockPskTlsServer(); + TlsServerProtocol serverProtocol = new TlsServerProtocol(s.GetStream()); + serverProtocol.Accept(server); + Stream log = new TeeOutputStream(serverProtocol.Stream, stdout); + Streams.PipeAll(serverProtocol.Stream, log); + serverProtocol.Close(); + } + finally + { + try + { + s.Close(); + } + catch (IOException) + { + } + } + } + } + } +} diff --git a/crypto/test/src/tls/test/TlsClientTest.cs b/crypto/test/src/tls/test/TlsClientTest.cs new file mode 100644 index 000000000..27e5f342b --- /dev/null +++ b/crypto/test/src/tls/test/TlsClientTest.cs @@ -0,0 +1,97 @@ +using System; +using System.IO; +using System.Net.Sockets; +using System.Text; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Tls.Tests +{ + /// <summary>A simple test designed to conduct a TLS handshake with an external TLS server.</summary> + /// <remarks> + /// Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in this package + /// (under 'src/test/resources') for help configuring an external TLS server. + /// </remarks> + [TestFixture] + public class TlsClientTest + { + [Test, Ignore] + public void TestConnection() + { + string host = "localhost"; + int port = 5556; + + long time1 = DateTimeUtilities.CurrentUnixMs(); + + MockTlsClient client = new MockTlsClient(null); + TlsClientProtocol protocol = OpenTlsClientConnection(host, port, client); + protocol.Close(); + + long time2 = DateTimeUtilities.CurrentUnixMs(); + Console.WriteLine("Elapsed 1: " + (time2 - time1) + "ms"); + + client = new MockTlsClient(client.GetSessionToResume()); + protocol = OpenTlsClientConnection(host, port, client); + + long time3 = DateTimeUtilities.CurrentUnixMs(); + Console.WriteLine("Elapsed 2: " + (time3 - time2) + "ms"); + + Http11Get(host, port, protocol.Stream); + + protocol.Close(); + } + + private static void Http11Get(string host, int port, Stream s) + { + WriteUtf8Line(s, "GET / HTTP/1.1"); + //WriteUtf8Line(s, "Host: " + host + ":" + port); + WriteUtf8Line(s, ""); + s.Flush(); + + Console.WriteLine("---"); + + string[] ends = new string[] { "</HTML>", "HTTP/1.1 3", "HTTP/1.1 4" }; + + StreamReader reader = new StreamReader(s); + + bool finished = false; + string line; + while (!finished && (line = reader.ReadLine()) != null) + { + Console.WriteLine("<<< " + line); + + string upperLine = TlsTestUtilities.ToUpperInvariant(line); + + // TEST CODE ONLY. This is not a robust way of parsing the result! + foreach (string end in ends) + { + if (upperLine.Contains(end)) + { + finished = true; + break; + } + } + } + + Console.Out.Flush(); + } + + private static TlsClientProtocol OpenTlsClientConnection(string hostname, int port, TlsClient client) + { + TcpClient tcp = new TcpClient(hostname, port); + + TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream()); + protocol.Connect(client); + return protocol; + } + + private static void WriteUtf8Line(Stream output, string line) + { + byte[] buf = Encoding.UTF8.GetBytes(line + "\r\n"); + output.Write(buf, 0, buf.Length); + Console.WriteLine(">>> " + line); + } + } +} diff --git a/crypto/test/src/tls/test/TlsProtocolNonBlockingTest.cs b/crypto/test/src/tls/test/TlsProtocolNonBlockingTest.cs new file mode 100644 index 000000000..56cd39e87 --- /dev/null +++ b/crypto/test/src/tls/test/TlsProtocolNonBlockingTest.cs @@ -0,0 +1,126 @@ +using System; +using System.IO; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class TlsProtocolNonBlockingTest + { + [Test] + public void TestClientServerFragmented() + { + // tests if it's really non-blocking when partial records arrive + ImplTestClientServer(true); + } + + [Test] + public void TestClientServerNonFragmented() + { + ImplTestClientServer(false); + } + + private static void ImplTestClientServer(bool fragment) + { + TlsClientProtocol clientProtocol = new TlsClientProtocol(); + TlsServerProtocol serverProtocol = new TlsServerProtocol(); + + MockTlsClient client = new MockTlsClient(null); + MockTlsServer server = new MockTlsServer(); + + clientProtocol.Connect(client); + serverProtocol.Accept(server); + + // pump handshake + bool hadDataFromServer = true; + bool hadDataFromClient = true; + while (hadDataFromServer || hadDataFromClient) + { + hadDataFromServer = PumpData(serverProtocol, clientProtocol, fragment); + hadDataFromClient = PumpData(clientProtocol, serverProtocol, fragment); + } + + // send data in both directions + byte[] data = new byte[1024]; + client.Crypto.SecureRandom.NextBytes(data); + + WriteAndRead(clientProtocol, serverProtocol, data, fragment); + WriteAndRead(serverProtocol, clientProtocol, data, fragment); + + // close the connection + clientProtocol.Close(); + PumpData(clientProtocol, serverProtocol, fragment); + serverProtocol.CloseInput(); + CheckClosed(serverProtocol); + CheckClosed(clientProtocol); + } + + private static void WriteAndRead(TlsProtocol writer, TlsProtocol reader, byte[] data, bool fragment) + { + int dataSize = data.Length; + writer.WriteApplicationData(data, 0, dataSize); + PumpData(writer, reader, fragment); + + Assert.AreEqual(dataSize, reader.GetAvailableInputBytes()); + byte[] readData = new byte[dataSize]; + reader.ReadInput(readData, 0, dataSize); + AssertArrayEquals(data, readData); + } + + private static bool PumpData(TlsProtocol from, TlsProtocol to, bool fragment) + { + int byteCount = from.GetAvailableOutputBytes(); + if (byteCount == 0) + return false; + + if (fragment) + { + byte[] buffer = new byte[1]; + while (from.GetAvailableOutputBytes() > 0) + { + from.ReadOutput(buffer, 0, 1); + to.OfferInput(buffer); + } + } + else + { + byte[] buffer = new byte[byteCount]; + from.ReadOutput(buffer, 0, buffer.Length); + to.OfferInput(buffer); + } + + return true; + } + + private static void CheckClosed(TlsProtocol protocol) + { + Assert.IsTrue(protocol.IsClosed); + + try + { + protocol.OfferInput(new byte[10]); + Assert.Fail("Input was accepted after close"); + } + catch (IOException e) + { + } + + try + { + protocol.WriteApplicationData(new byte[10], 0, 10); + Assert.Fail("Output was accepted after close"); + } + catch (IOException e) + { + } + } + + private static void AssertArrayEquals(byte[] a, byte[] b) + { + Assert.IsTrue(Arrays.AreEqual(a, b)); + } + } +} diff --git a/crypto/test/src/tls/test/TlsProtocolTest.cs b/crypto/test/src/tls/test/TlsProtocolTest.cs new file mode 100644 index 000000000..b4f79f9ba --- /dev/null +++ b/crypto/test/src/tls/test/TlsProtocolTest.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Threading; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class TlsProtocolTest + { + [Test] + public void TestClientServer() + { + PipedStream clientPipe = new PipedStream(); + PipedStream serverPipe = new PipedStream(clientPipe); + + TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe); + TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe); + + MockTlsClient client = new MockTlsClient(null); + MockTlsServer server = new MockTlsServer(); + + Server serverRun = new Server(serverProtocol, server); + Thread serverThread = new Thread(new ThreadStart(serverRun.Run)); + serverThread.Start(); + + clientProtocol.Connect(client); + + byte[] data = new byte[1000]; + client.Crypto.SecureRandom.NextBytes(data); + + Stream output = clientProtocol.Stream; + output.Write(data, 0, data.Length); + + byte[] echo = new byte[data.Length]; + int count = Streams.ReadFully(clientProtocol.Stream, echo); + + Assert.AreEqual(count, data.Length); + Assert.IsTrue(Arrays.AreEqual(data, echo)); + + output.Close(); + + serverThread.Join(); + } + + internal class Server + { + private readonly TlsServerProtocol m_serverProtocol; + private readonly TlsServer m_server; + + internal Server(TlsServerProtocol serverProtocol, TlsServer server) + { + this.m_serverProtocol = serverProtocol; + this.m_server = server; + } + + public void Run() + { + try + { + m_serverProtocol.Accept(m_server); + Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream); + m_serverProtocol.Close(); + } + catch (Exception e) + { + } + } + } + } +} diff --git a/crypto/test/src/tls/test/TlsPskProtocolTest.cs b/crypto/test/src/tls/test/TlsPskProtocolTest.cs new file mode 100644 index 000000000..97e6f133b --- /dev/null +++ b/crypto/test/src/tls/test/TlsPskProtocolTest.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Threading; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class TlsPskProtocolTest + { + [Test] + public void TestClientServer() + { + PipedStream clientPipe = new PipedStream(); + PipedStream serverPipe = new PipedStream(clientPipe); + + TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe); + TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe); + + MockPskTlsClient client = new MockPskTlsClient(null); + MockPskTlsServer server = new MockPskTlsServer(); + + Server serverRun = new Server(serverProtocol, server); + Thread serverThread = new Thread(new ThreadStart(serverRun.Run)); + serverThread.Start(); + + clientProtocol.Connect(client); + + byte[] data = new byte[1000]; + client.Crypto.SecureRandom.NextBytes(data); + + Stream output = clientProtocol.Stream; + output.Write(data, 0, data.Length); + + byte[] echo = new byte[data.Length]; + int count = Streams.ReadFully(clientProtocol.Stream, echo); + + Assert.AreEqual(count, data.Length); + Assert.IsTrue(Arrays.AreEqual(data, echo)); + + output.Close(); + + serverThread.Join(); + } + + internal class Server + { + private readonly TlsServerProtocol m_serverProtocol; + private readonly TlsServer m_server; + + internal Server(TlsServerProtocol serverProtocol, TlsServer server) + { + this.m_serverProtocol = serverProtocol; + this.m_server = server; + } + + public void Run() + { + try + { + m_serverProtocol.Accept(m_server); + Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream); + m_serverProtocol.Close(); + } + catch (Exception e) + { + } + } + } + } +} diff --git a/crypto/test/src/tls/test/TlsServerTest.cs b/crypto/test/src/tls/test/TlsServerTest.cs new file mode 100644 index 000000000..333ff5664 --- /dev/null +++ b/crypto/test/src/tls/test/TlsServerTest.cs @@ -0,0 +1,82 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Tls.Tests +{ + /// <summary>A simple test designed to conduct a TLS handshake with an external TLS client.</summary> + /// <remarks> + /// Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in this package + /// (under 'src/test/resources') for help configuring an external TLS client. + /// </remarks> + [TestFixture] + public class TlsServerTest + { + [Test, Ignore] + public void TestConnection() + { + int port = 5556; + + TcpListener ss = new TcpListener(IPAddress.Any, port); + ss.Start(); + Stream stdout = Console.OpenStandardOutput(); + try + { + while (true) + { + TcpClient s = ss.AcceptTcpClient(); + Console.WriteLine("--------------------------------------------------------------------------------"); + Console.WriteLine("Accepted " + s); + Server serverRun = new Server(s, stdout); + Thread t = new Thread(new ThreadStart(serverRun.Run)); + t.Start(); + } + } + finally + { + ss.Stop(); + } + } + + internal class Server + { + private readonly TcpClient s; + private readonly Stream stdout; + + internal Server(TcpClient s, Stream stdout) + { + this.s = s; + this.stdout = stdout; + } + + public void Run() + { + try + { + MockTlsServer server = new MockTlsServer(); + TlsServerProtocol serverProtocol = new TlsServerProtocol(s.GetStream()); + serverProtocol.Accept(server); + Stream log = new TeeOutputStream(serverProtocol.Stream, stdout); + Streams.PipeAll(serverProtocol.Stream, log); + serverProtocol.Close(); + } + finally + { + try + { + s.Close(); + } + catch (IOException) + { + } + } + } + } + } +} diff --git a/crypto/test/src/tls/test/TlsSrpProtocolTest.cs b/crypto/test/src/tls/test/TlsSrpProtocolTest.cs new file mode 100644 index 000000000..555b1f1f7 --- /dev/null +++ b/crypto/test/src/tls/test/TlsSrpProtocolTest.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Threading; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class TlsSrpProtocolTest + { + [Test] + public void TestClientServer() + { + PipedStream clientPipe = new PipedStream(); + PipedStream serverPipe = new PipedStream(clientPipe); + + TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe); + TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe); + + MockSrpTlsClient client = new MockSrpTlsClient(null, MockSrpTlsServer.TEST_SRP_IDENTITY); + MockSrpTlsServer server = new MockSrpTlsServer(); + + Server serverRun = new Server(serverProtocol, server); + Thread serverThread = new Thread(new ThreadStart(serverRun.Run)); + serverThread.Start(); + + clientProtocol.Connect(client); + + byte[] data = new byte[1000]; + client.Crypto.SecureRandom.NextBytes(data); + + Stream output = clientProtocol.Stream; + output.Write(data, 0, data.Length); + + byte[] echo = new byte[data.Length]; + int count = Streams.ReadFully(clientProtocol.Stream, echo); + + Assert.AreEqual(count, data.Length); + Assert.IsTrue(Arrays.AreEqual(data, echo)); + + output.Close(); + + serverThread.Join(); + } + + internal class Server + { + private readonly TlsServerProtocol m_serverProtocol; + private readonly TlsServer m_server; + + internal Server(TlsServerProtocol serverProtocol, TlsServer server) + { + this.m_serverProtocol = serverProtocol; + this.m_server = server; + } + + public void Run() + { + try + { + m_serverProtocol.Accept(m_server); + Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream); + m_serverProtocol.Close(); + } + catch (Exception e) + { + } + } + } + } +} diff --git a/crypto/test/src/tls/test/TlsTestCase.cs b/crypto/test/src/tls/test/TlsTestCase.cs new file mode 100644 index 000000000..0489d22c1 --- /dev/null +++ b/crypto/test/src/tls/test/TlsTestCase.cs @@ -0,0 +1,182 @@ +using System; +using System.IO; +using System.Threading; + +using NUnit.Framework; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class TlsTestCase + { + private static void CheckTlsVersions(ProtocolVersion[] versions) + { + if (versions != null) + { + for (int i = 0; i < versions.Length; ++i) + { + if (!versions[i].IsTls) + throw new InvalidOperationException("Non-TLS version"); + } + } + } + + [Test, TestCaseSource(typeof(TlsTestSuite), "Suite")] + public void RunTest(TlsTestConfig config) + { + // Disable the test if it is not being run via TlsTestSuite + if (config == null) + return; + + CheckTlsVersions(config.clientSupportedVersions); + CheckTlsVersions(config.serverSupportedVersions); + + PipedStream clientPipe = new PipedStream(); + PipedStream serverPipe = new PipedStream(clientPipe); + + NetworkStream clientNet = new NetworkStream(clientPipe); + NetworkStream serverNet = new NetworkStream(serverPipe); + + TlsTestClientProtocol clientProtocol = new TlsTestClientProtocol(clientNet, config); + TlsTestServerProtocol serverProtocol = new TlsTestServerProtocol(serverNet, config); + + clientProtocol.IsResumableHandshake = true; + serverProtocol.IsResumableHandshake = true; + + TlsTestClientImpl clientImpl = new TlsTestClientImpl(config); + TlsTestServerImpl serverImpl = new TlsTestServerImpl(config); + + Server serverRun = new Server(this, serverProtocol, serverImpl); + Thread serverThread = new Thread(new ThreadStart(serverRun.Run)); + serverThread.Start(); + + Exception caught = null; + try + { + clientProtocol.Connect(clientImpl); + + byte[] data = new byte[1000]; + clientImpl.Crypto.SecureRandom.NextBytes(data); + + Stream stream = clientProtocol.Stream; + stream.Write(data, 0, data.Length); + + byte[] echo = new byte[data.Length]; + int count = Streams.ReadFully(stream, echo, 0, echo.Length); + + Assert.AreEqual(count, data.Length); + Assert.IsTrue(Arrays.AreEqual(data, echo)); + + Assert.IsTrue(Arrays.AreEqual(clientImpl.m_tlsServerEndPoint, serverImpl.m_tlsServerEndPoint)); + + if (!TlsUtilities.IsTlsV13(clientImpl.m_negotiatedVersion)) + { + Assert.NotNull(clientImpl.m_tlsUnique); + Assert.NotNull(serverImpl.m_tlsUnique); + } + Assert.IsTrue(Arrays.AreEqual(clientImpl.m_tlsUnique, serverImpl.m_tlsUnique)); + + stream.Close(); + } + catch (Exception e) + { + caught = e; + LogException(caught); + } + + serverRun.AllowExit(); + serverThread.Join(); + + Assert.IsTrue(clientNet.IsClosed, "Client Stream not closed"); + Assert.IsTrue(serverNet.IsClosed, "Server Stream not closed"); + + Assert.AreEqual(config.expectFatalAlertConnectionEnd, clientImpl.FirstFatalAlertConnectionEnd, + "Client fatal alert connection end"); + Assert.AreEqual(config.expectFatalAlertConnectionEnd, serverImpl.FirstFatalAlertConnectionEnd, + "Server fatal alert connection end"); + + Assert.AreEqual(config.expectFatalAlertDescription, clientImpl.FirstFatalAlertDescription, + "Client fatal alert description"); + Assert.AreEqual(config.expectFatalAlertDescription, serverImpl.FirstFatalAlertDescription, + "Server fatal alert description"); + + if (config.expectFatalAlertConnectionEnd == -1) + { + Assert.IsNull(caught, "Unexpected client exception"); + Assert.IsNull(serverRun.m_caught, "Unexpected server exception"); + } + } + + protected virtual void LogException(Exception e) + { + if (TlsTestConfig.Debug) + { + Console.Error.WriteLine(e); + Console.Error.Flush(); + } + } + + internal class Server + { + protected readonly TlsTestCase m_outer; + protected readonly TlsServerProtocol m_serverProtocol; + protected readonly TlsServer m_server; + + internal bool m_canExit = false; + internal Exception m_caught = null; + + internal Server(TlsTestCase outer, TlsTestServerProtocol serverProtocol, TlsServer server) + { + this.m_outer = outer; + this.m_serverProtocol = serverProtocol; + this.m_server = server; + } + + internal void AllowExit() + { + lock (this) + { + m_canExit = true; + Monitor.PulseAll(this); + } + } + + public void Run() + { + try + { + m_serverProtocol.Accept(m_server); + Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream); + m_serverProtocol.Close(); + } + catch (Exception e) + { + m_caught = e; + m_outer.LogException(m_caught); + } + + WaitExit(); + } + + protected void WaitExit() + { + lock (this) + { + while (!m_canExit) + { + try + { + Monitor.Wait(this); + } + catch (ThreadInterruptedException) + { + } + } + } + } + } + } +} diff --git a/crypto/test/src/tls/test/TlsTestClientImpl.cs b/crypto/test/src/tls/test/TlsTestClientImpl.cs new file mode 100644 index 000000000..d436df3f7 --- /dev/null +++ b/crypto/test/src/tls/test/TlsTestClientImpl.cs @@ -0,0 +1,370 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class TlsTestClientImpl + : DefaultTlsClient + { + private static readonly int[] TestCipherSuites = new int[] + { + /* + * TLS 1.3 + */ + CipherSuite.TLS_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_AES_128_GCM_SHA256, + + /* + * pre-TLS 1.3 + */ + CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + }; + + protected readonly TlsTestConfig m_config; + + protected int m_firstFatalAlertConnectionEnd = -1; + protected short m_firstFatalAlertDescription = -1; + + internal ProtocolVersion m_negotiatedVersion = null; + internal byte[] m_tlsServerEndPoint = null; + internal byte[] m_tlsUnique = null; + + internal TlsTestClientImpl(TlsTestConfig config) + : base(TlsTestSuite.GetCrypto(config)) + { + this.m_config = config; + } + + internal int FirstFatalAlertConnectionEnd + { + get { return m_firstFatalAlertConnectionEnd; } + } + + internal short FirstFatalAlertDescription + { + get { return m_firstFatalAlertDescription; } + } + + public override IDictionary GetClientExtensions() + { + IDictionary clientExtensions = base.GetClientExtensions(); + if (clientExtensions != null) + { + if (!m_config.clientSendSignatureAlgorithms) + { + clientExtensions.Remove(ExtensionType.signature_algorithms); + this.m_supportedSignatureAlgorithms = null; + } + if (!m_config.clientSendSignatureAlgorithmsCert) + { + clientExtensions.Remove(ExtensionType.signature_algorithms_cert); + this.m_supportedSignatureAlgorithmsCert = null; + } + } + return clientExtensions; + } + + public override IList GetEarlyKeyShareGroups() + { + if (m_config.clientEmptyKeyShare) + return null; + + return base.GetEarlyKeyShareGroups(); + } + + public override bool IsFallback() + { + return m_config.clientFallback; + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1) + { + m_firstFatalAlertConnectionEnd = ConnectionEnd.client; + m_firstFatalAlertDescription = alertDescription; + } + + if (TlsTestConfig.Debug) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS client raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1) + { + m_firstFatalAlertConnectionEnd = ConnectionEnd.server; + m_firstFatalAlertDescription = alertDescription; + } + + if (TlsTestConfig.Debug) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS client received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + m_tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + m_tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + + if (TlsTestConfig.Debug) + { + Console.WriteLine("TLS client reports 'tls-server-end-point' = " + ToHexString(m_tlsServerEndPoint)); + Console.WriteLine("TLS client reports 'tls-unique' = " + ToHexString(m_tlsUnique)); + } + } + + public override void NotifyServerVersion(ProtocolVersion serverVersion) + { + base.NotifyServerVersion(serverVersion); + + this.m_negotiatedVersion = serverVersion; + + if (TlsTestConfig.Debug) + { + Console.WriteLine("TLS client negotiated " + serverVersion); + } + } + + public override TlsAuthentication GetAuthentication() + { + return new MyTlsAuthentication(this, m_context); + } + + protected virtual Certificate CorruptCertificate(Certificate cert) + { + CertificateEntry[] certEntryList = cert.GetCertificateEntryList(); + CertificateEntry ee = certEntryList[0]; + TlsCertificate corruptCert = CorruptCertificateSignature(ee.Certificate); + certEntryList[0] = new CertificateEntry(corruptCert, ee.Extensions); + return new Certificate(cert.GetCertificateRequestContext(), certEntryList); + } + + protected virtual TlsCertificate CorruptCertificateSignature(TlsCertificate tlsCertificate) + { + X509CertificateStructure cert = X509CertificateStructure.GetInstance(tlsCertificate.GetEncoded()); + + Asn1EncodableVector v = new Asn1EncodableVector(); + v.Add(cert.TbsCertificate); + v.Add(cert.SignatureAlgorithm); + v.Add(CorruptSignature(cert.Signature)); + + cert = X509CertificateStructure.GetInstance(new DerSequence(v)); + + return Crypto.CreateCertificate(cert.GetEncoded(Asn1Encodable.Der)); + } + + protected virtual DerBitString CorruptSignature(DerBitString bs) + { + return new DerBitString(CorruptBit(bs.GetOctets())); + } + + protected virtual byte[] CorruptBit(byte[] bs) + { + bs = Arrays.Clone(bs); + + // Flip a random bit + int bit = m_context.Crypto.SecureRandom.Next(bs.Length << 3); + bs[bit >> 3] ^= (byte)(1 << (bit & 7)); + + return bs; + } + + protected override int[] GetSupportedCipherSuites() + { + return TlsUtilities.GetSupportedCipherSuites(Crypto, TestCipherSuites); + } + + protected override ProtocolVersion[] GetSupportedVersions() + { + if (null != m_config.clientSupportedVersions) + { + return m_config.clientSupportedVersions; + } + + return base.GetSupportedVersions(); + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + internal class MyTlsAuthentication + : TlsAuthentication + { + private readonly TlsTestClientImpl m_outer; + private readonly TlsContext m_context; + + internal MyTlsAuthentication(TlsTestClientImpl outer, TlsContext context) + { + this.m_outer = outer; + this.m_context = context; + } + + public virtual void NotifyServerCertificate(TlsServerCertificate serverCertificate) + { + TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList(); + + if (TlsTestConfig.Debug) + { + Console.WriteLine("TLS client received server certificate chain of length " + chain.Length); + for (int i = 0; i < chain.Length; ++i) + { + X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " (" + + entry.Subject + ")"); + } + } + + bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null + || serverCertificate.Certificate.IsEmpty; + + if (isEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + string[] trustedCertResources = new string[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem", + "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem", + "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem", + "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" }; + + TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0], + trustedCertResources); + + if (null == certPath) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + if (m_outer.m_config.clientCheckSigAlgOfServerCerts) + { + TlsUtilities.CheckPeerSigAlgs(m_context, certPath); + } + } + + public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) + { + TlsTestConfig config = m_outer.m_config; + + if (config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE) + throw new InvalidOperationException(); + if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE) + return null; + + bool isTlsV13 = TlsUtilities.IsTlsV13(m_context); + + if (!isTlsV13) + { + short[] certificateTypes = certificateRequest.CertificateTypes; + if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign)) + return null; + } + + IList supportedSigAlgs = certificateRequest.SupportedSignatureAlgorithms; + if (supportedSigAlgs != null && config.clientAuthSigAlg != null) + { + supportedSigAlgs = new ArrayList(1); + supportedSigAlgs.Add(config.clientAuthSigAlg); + } + + // TODO[tls13] Check also supportedSigAlgsCert against the chain signature(s) + + TlsCredentialedSigner signerCredentials = TlsTestUtilities.LoadSignerCredentials(m_context, + supportedSigAlgs, SignatureAlgorithm.rsa, "x509-client-rsa.pem", "x509-client-key-rsa.pem"); + + if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_VALID) + return signerCredentials; + + return new MyTlsCredentialedSigner(m_outer, signerCredentials); + } + } + + internal class MyTlsCredentialedSigner + : TlsCredentialedSigner + { + private readonly TlsTestClientImpl m_outer; + private readonly TlsCredentialedSigner m_inner; + + internal MyTlsCredentialedSigner(TlsTestClientImpl outer, TlsCredentialedSigner inner) + { + this.m_outer = outer; + this.m_inner = inner; + } + + public virtual byte[] GenerateRawSignature(byte[] hash) + { + byte[] sig = m_inner.GenerateRawSignature(hash); + + if (m_outer.m_config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_VERIFY) + { + sig = m_outer.CorruptBit(sig); + } + + return sig; + } + + public virtual Certificate Certificate + { + get + { + Certificate cert = m_inner.Certificate; + + if (m_outer.m_config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_CERT) + { + cert = m_outer.CorruptCertificate(cert); + } + + return cert; + } + } + + public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm + { + get { return m_inner.SignatureAndHashAlgorithm; } + } + + public virtual TlsStreamSigner GetStreamSigner() + { + return null; + } + }; + } +} diff --git a/crypto/test/src/tls/test/TlsTestClientProtocol.cs b/crypto/test/src/tls/test/TlsTestClientProtocol.cs new file mode 100644 index 000000000..f7e94680a --- /dev/null +++ b/crypto/test/src/tls/test/TlsTestClientProtocol.cs @@ -0,0 +1,32 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class TlsTestClientProtocol + : TlsClientProtocol + { + protected readonly TlsTestConfig m_config; + + internal TlsTestClientProtocol(Stream stream, TlsTestConfig config) + : this(stream, stream, config) + { + } + + internal TlsTestClientProtocol(Stream input, Stream output, TlsTestConfig config) + : base(input, output) + { + this.m_config = config; + } + + protected override void SendCertificateVerifyMessage(DigitallySigned certificateVerify) + { + if (certificateVerify.Algorithm != null && m_config.clientAuthSigAlgClaimed != null) + { + certificateVerify = new DigitallySigned(m_config.clientAuthSigAlgClaimed, certificateVerify.Signature); + } + + base.SendCertificateVerifyMessage(certificateVerify); + } + } +} diff --git a/crypto/test/src/tls/test/TlsTestConfig.cs b/crypto/test/src/tls/test/TlsTestConfig.cs new file mode 100644 index 000000000..a15d4e535 --- /dev/null +++ b/crypto/test/src/tls/test/TlsTestConfig.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Tls.Tests +{ + public class TlsTestConfig + { + // TODO[tls-port] + public static readonly bool Debug = true; + + /// <summary>Client does not authenticate, ignores any certificate request.</summary> + public const int CLIENT_AUTH_NONE = 0; + + /// <summary>Client will authenticate if it receives a certificate request.</summary> + public const int CLIENT_AUTH_VALID = 1; + + /// <summary>Client will authenticate if it receives a certificate request, with an invalid certificate. + /// </summary> + public const int CLIENT_AUTH_INVALID_CERT = 2; + + /// <summary>Client will authenticate if it receives a certificate request, with an invalid CertificateVerify + /// signature.</summary> + public const int CLIENT_AUTH_INVALID_VERIFY = 3; + + public const int CRYPTO_BC = 0; + + /// <summary>Server will not request a client certificate.</summary> + public const int SERVER_CERT_REQ_NONE = 0; + + /// <summary>Server will request a client certificate but receiving one is optional.</summary> + public const int SERVER_CERT_REQ_OPTIONAL = 1; + + /// <summary>Server will request a client certificate and receiving one is mandatory.</summary> + public const int SERVER_CERT_REQ_MANDATORY = 2; + + /// <summary>Configures the client authentication behaviour of the test client. Use CLIENT_AUTH_* constants. + /// </summary> + public int clientAuth = CLIENT_AUTH_VALID; + + /// <summary>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).</summary> + public SignatureAndHashAlgorithm clientAuthSigAlg = null; + + /// <summary>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.</summary> + public SignatureAndHashAlgorithm clientAuthSigAlgClaimed = null; + + /// <summary>Control whether the client will call + /// <see cref="TlsUtilities.CheckPeerSigAlgs(TlsContext, Crypto.TlsCertificate[])"/> to check the server + /// certificate chain.</summary> + public bool clientCheckSigAlgOfServerCerts = true; + + public int clientCrypto = CRYPTO_BC; + + /// <summary>Configures whether the client will send an empty key_share extension in initial ClientHello. + /// </summary> + public bool clientEmptyKeyShare = false; + + /// <summary>Configures whether the client will indicate version fallback via TLS_FALLBACK_SCSV.</summary> + public bool clientFallback = false; + + /// <summary>Configures whether a (TLS 1.2+) client may send the signature_algorithms extension in ClientHello. + /// </summary> + public bool clientSendSignatureAlgorithms = true; + + /// <summary>Configures whether a (TLS 1.2+) client may send the signature_algorithms_cert extension in + /// ClientHello.</summary> + public bool clientSendSignatureAlgorithmsCert = true; + + /// <summary>Configures the supported protocol versions for the client. If null, uses the library's default. + /// </summary> + public ProtocolVersion[] clientSupportedVersions = null; + + /// <summary>If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/ hash algorithm to be + /// used for the ServerKeyExchange signature(if one is sent).</summary> + public SignatureAndHashAlgorithm serverAuthSigAlg = null; + + /// <summary>Configures whether the test server will send a certificate request.</summary> + public int serverCertReq = SERVER_CERT_REQ_OPTIONAL; + + /// <summary>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.</summary> + public IList serverCertReqSigAlgs = null; + + /// <summary>Control whether the server will call + /// <see cref="TlsUtilities.CheckPeerSigAlgs(TlsContext, Crypto.TlsCertificate[])"/> to check the client + /// certificate chain.</summary> + public bool serverCheckSigAlgOfClientCerts = true; + + public int serverCrypto = CRYPTO_BC; + + /// <summary>Configures a protocol version the server will unconditionally negotiate.</summary> + /// <remarks> + /// Ignored if null. + /// </remarks> + public ProtocolVersion serverNegotiateVersion = null; + + /// <summary>Configures the supported protocol versions for the server.</summary> + /// <remarks> + /// If null, uses the library's default. + /// </remarks> + public ProtocolVersion[] serverSupportedVersions = null; + + /// <summary>Configures the connection end at which a fatal alert is expected to be raised.</summary> + /// <remarks> + /// Use <see cref="ConnectionEnd"/> constants. + /// </remarks> + public int expectFatalAlertConnectionEnd = -1; + + /// <summary>Configures the type of fatal alert expected to be raised.</summary> + /// <remarks> + /// Use <see cref="AlertDescription"/> constants. + /// </remarks> + public short expectFatalAlertDescription = -1; + + public virtual void ExpectClientFatalAlert(short alertDescription) + { + this.expectFatalAlertConnectionEnd = ConnectionEnd.client; + this.expectFatalAlertDescription = alertDescription; + } + + public virtual void ExpectServerFatalAlert(short alertDescription) + { + this.expectFatalAlertConnectionEnd = ConnectionEnd.server; + this.expectFatalAlertDescription = alertDescription; + } + } +} diff --git a/crypto/test/src/tls/test/TlsTestServerImpl.cs b/crypto/test/src/tls/test/TlsTestServerImpl.cs new file mode 100644 index 000000000..6bc4d315d --- /dev/null +++ b/crypto/test/src/tls/test/TlsTestServerImpl.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class TlsTestServerImpl + : DefaultTlsServer + { + private static readonly int[] TestCipherSuites = new int[] + { + /* + * TLS 1.3 + */ + CipherSuite.TLS_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_AES_256_GCM_SHA384, + CipherSuite.TLS_AES_128_GCM_SHA256, + + /* + * pre-TLS 1.3 + */ + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + }; + + protected readonly TlsTestConfig m_config; + + protected int m_firstFatalAlertConnectionEnd = -1; + protected short m_firstFatalAlertDescription = -1; + + internal byte[] m_tlsServerEndPoint = null; + internal byte[] m_tlsUnique = null; + + internal TlsTestServerImpl(TlsTestConfig config) + : base(TlsTestSuite.GetCrypto(config)) + { + this.m_config = config; + } + + internal int FirstFatalAlertConnectionEnd + { + get { return m_firstFatalAlertConnectionEnd; } + } + + internal short FirstFatalAlertDescription + { + get { return m_firstFatalAlertDescription; } + } + + public override TlsCredentials GetCredentials() + { + /* + * TODO[tls13] Should really be finding the first client-supported signature scheme that the + * server also supports and has credentials for. + */ + if (TlsUtilities.IsTlsV13(m_context)) + { + return GetRsaSignerCredentials(); + } + + return base.GetCredentials(); + } + + public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message, + Exception cause) + { + if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1) + { + m_firstFatalAlertConnectionEnd = ConnectionEnd.server; + m_firstFatalAlertDescription = alertDescription; + } + + if (TlsTestConfig.Debug) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS server raised alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + if (message != null) + { + output.WriteLine("> " + message); + } + if (cause != null) + { + output.WriteLine(cause); + } + } + } + + public override void NotifyAlertReceived(short alertLevel, short alertDescription) + { + if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1) + { + m_firstFatalAlertConnectionEnd = ConnectionEnd.client; + m_firstFatalAlertDescription = alertDescription; + } + + if (TlsTestConfig.Debug) + { + TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out; + output.WriteLine("TLS server received alert: " + AlertLevel.GetText(alertLevel) + + ", " + AlertDescription.GetText(alertDescription)); + } + } + + public override void NotifyHandshakeComplete() + { + base.NotifyHandshakeComplete(); + + m_tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point); + m_tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique); + + if (TlsTestConfig.Debug) + { + Console.WriteLine("TLS server reports 'tls-server-end-point' = " + ToHexString(m_tlsServerEndPoint)); + Console.WriteLine("TLS server reports 'tls-unique' = " + ToHexString(m_tlsUnique)); + } + } + + public override ProtocolVersion GetServerVersion() + { + ProtocolVersion serverVersion = (null != m_config.serverNegotiateVersion) + ? m_config.serverNegotiateVersion + : base.GetServerVersion(); + + if (TlsTestConfig.Debug) + { + Console.WriteLine("TLS server negotiated " + serverVersion); + } + + return serverVersion; + } + + public override CertificateRequest GetCertificateRequest() + { + if (m_config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE) + return null; + + IList serverSigAlgs = null; + if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion)) + { + serverSigAlgs = m_config.serverCertReqSigAlgs; + if (serverSigAlgs == null) + { + serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context); + } + } + + IList certificateAuthorities = new ArrayList(); + //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-dsa.pem").Subject); + //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-ecdsa.pem").Subject); + //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-rsa.pem").Subject); + + // All the CA certificates are currently configured with this subject + certificateAuthorities.Add(new X509Name("CN=BouncyCastle TLS Test CA")); + + if (TlsUtilities.IsTlsV13(m_context)) + { + // TODO[tls13] Support for non-empty request context + byte[] certificateRequestContext = TlsUtilities.EmptyBytes; + + // TODO[tls13] Add TlsTestConfig.serverCertReqSigAlgsCert + IList serverSigAlgsCert = null; + + return new CertificateRequest(certificateRequestContext, serverSigAlgs, serverSigAlgsCert, + certificateAuthorities); + } + else + { + short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign, + ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign }; + + return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities); + } + } + + public override void NotifyClientCertificate(Certificate clientCertificate) + { + bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty); + + if (isEmpty != (m_config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE)) + throw new InvalidOperationException(); + + if (isEmpty && (m_config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_MANDATORY)) + { + short alertDescription = TlsUtilities.IsTlsV13(m_context) + ? AlertDescription.certificate_required + : AlertDescription.handshake_failure; + + throw new TlsFatalAlert(alertDescription); + } + + TlsCertificate[] chain = clientCertificate.GetCertificateList(); + + if (TlsTestConfig.Debug) + { + Console.WriteLine("TLS server received client certificate chain of length " + chain.Length); + for (int i = 0; i < chain.Length; ++i) + { + X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[0].GetEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " (" + + entry.Subject + ")"); + } + } + + if (isEmpty) + return; + + string[] trustedCertResources = new string[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem", + "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem", + "x509-client-rsa_pss_256.pem", "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem", + "x509-client-rsa.pem" }; + + TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0], + trustedCertResources); + + if (null == certPath) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + if (m_config.serverCheckSigAlgOfClientCerts) + { + TlsUtilities.CheckPeerSigAlgs(m_context, certPath); + } + } + + protected virtual IList GetSupportedSignatureAlgorithms() + { + if (TlsUtilities.IsTlsV12(m_context) && m_config.serverAuthSigAlg != null) + { + IList signatureAlgorithms = new ArrayList(1); + signatureAlgorithms.Add(m_config.serverAuthSigAlg); + return signatureAlgorithms; + } + + return m_context.SecurityParameters.ClientSigAlgs; + } + + protected override TlsCredentialedSigner GetDsaSignerCredentials() + { + return LoadSignerCredentials(SignatureAlgorithm.dsa); + } + + protected override TlsCredentialedSigner GetECDsaSignerCredentials() + { + // TODO[RFC 8422] Code should choose based on client's supported sig algs? + return LoadSignerCredentials(SignatureAlgorithm.ecdsa); + //return LoadSignerCredentials(SignatureAlgorithm.ed25519); + //return LoadSignerCredentials(SignatureAlgorithm.ed448); + } + + protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials() + { + return TlsTestUtilities.LoadEncryptionCredentials(m_context, + new string[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem"); + } + + protected override TlsCredentialedSigner GetRsaSignerCredentials() + { + return LoadSignerCredentials(SignatureAlgorithm.rsa); + } + + protected override int[] GetSupportedCipherSuites() + { + return TlsUtilities.GetSupportedCipherSuites(Crypto, TestCipherSuites); + } + + protected override ProtocolVersion[] GetSupportedVersions() + { + if (m_config.serverSupportedVersions != null) + { + return m_config.serverSupportedVersions; + } + + return base.GetSupportedVersions(); + } + + protected virtual string ToHexString(byte[] data) + { + return data == null ? "(null)" : Hex.ToHexString(data); + } + + private TlsCredentialedSigner LoadSignerCredentials(short signatureAlgorithm) + { + return TlsTestUtilities.LoadSignerCredentialsServer(m_context, GetSupportedSignatureAlgorithms(), + signatureAlgorithm); + } + } +} diff --git a/crypto/test/src/tls/test/TlsTestServerProtocol.cs b/crypto/test/src/tls/test/TlsTestServerProtocol.cs new file mode 100644 index 000000000..632f6b8bf --- /dev/null +++ b/crypto/test/src/tls/test/TlsTestServerProtocol.cs @@ -0,0 +1,22 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Tls.Tests +{ + internal class TlsTestServerProtocol + : TlsServerProtocol + { + protected readonly TlsTestConfig m_config; + + internal TlsTestServerProtocol(Stream stream, TlsTestConfig config) + : this(stream, stream, config) + { + } + + internal TlsTestServerProtocol(Stream input, Stream output, TlsTestConfig config) + : base(input, output) + { + this.m_config = config; + } + } +} diff --git a/crypto/test/src/tls/test/TlsTestSuite.cs b/crypto/test/src/tls/test/TlsTestSuite.cs new file mode 100644 index 000000000..adedd8249 --- /dev/null +++ b/crypto/test/src/tls/test/TlsTestSuite.cs @@ -0,0 +1,300 @@ +using System; +using System.Collections; + +using NUnit.Framework; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; + +namespace Org.BouncyCastle.Tls.Tests +{ + public class TlsTestSuite + { + internal static TlsCrypto BC_CRYPTO = new BcTlsCrypto(new SecureRandom()); + + internal static TlsCrypto GetCrypto(TlsTestConfig config) + { + switch (config.clientCrypto) + { + case TlsTestConfig.CRYPTO_BC: + default: + return BC_CRYPTO; + } + } + + // Make the access to constants less verbose + internal abstract class C : TlsTestConfig {} + + public TlsTestSuite() + { + } + + public static IEnumerable Suite() + { + IList testSuite = new ArrayList(); + AddAllTests(testSuite, TlsTestConfig.CRYPTO_BC, TlsTestConfig.CRYPTO_BC); + return testSuite; + } + + private static void AddAllTests(IList testSuite, int clientCrypto, int serverCrypto) + { + AddFallbackTests(testSuite, clientCrypto, serverCrypto); + AddVersionTests(testSuite, ProtocolVersion.SSLv3, clientCrypto, serverCrypto); + AddVersionTests(testSuite, ProtocolVersion.TLSv10, clientCrypto, serverCrypto); + AddVersionTests(testSuite, ProtocolVersion.TLSv11, clientCrypto, serverCrypto); + AddVersionTests(testSuite, ProtocolVersion.TLSv12, clientCrypto, serverCrypto); + AddVersionTests(testSuite, ProtocolVersion.TLSv13, clientCrypto, serverCrypto); + } + + private static void AddFallbackTests(IList testSuite, int clientCrypto, int serverCrypto) + { + string prefix = GetCryptoName(clientCrypto) + "_" + GetCryptoName(serverCrypto) + "_"; + + { + TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12, clientCrypto, serverCrypto); + c.clientFallback = true; + + AddTestCase(testSuite, c, prefix + "FallbackGood"); + } + + { + TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12, clientCrypto, serverCrypto); + c.clientFallback = true; + c.clientSupportedVersions = ProtocolVersion.TLSv11.DownTo(ProtocolVersion.TLSv10); + c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback); + + AddTestCase(testSuite, c, prefix + "FallbackBad"); + } + + { + TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12, clientCrypto, serverCrypto); + c.clientSupportedVersions = ProtocolVersion.TLSv11.DownTo(ProtocolVersion.TLSv10); + + AddTestCase(testSuite, c, prefix + "FallbackNone"); + } + } + + private static void AddVersionTests(IList testSuite, ProtocolVersion version, int clientCrypto, + int serverCrypto) + { + string prefix = GetCryptoName(clientCrypto) + "_" + GetCryptoName(serverCrypto) + "_" + + version.ToString().Replace(" ", "").Replace(".", "") + "_"; + + bool isTlsV12 = TlsUtilities.IsTlsV12(version); + bool isTlsV13 = TlsUtilities.IsTlsV13(version); + bool isTlsV12Exactly = isTlsV12 && !isTlsV13; + + short certReqDeclinedAlert = TlsUtilities.IsTlsV13(version) + ? AlertDescription.certificate_required + : AlertDescription.handshake_failure; + + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + + AddTestCase(testSuite, c, prefix + "GoodDefault"); + } + + if (isTlsV13) + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientEmptyKeyShare = true; + + AddTestCase(testSuite, c, prefix + "GoodEmptyKeyShare"); + } + + /* + * 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 (isTlsV12Exactly) + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientAuth = C.CLIENT_AUTH_VALID; + c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa); + c.serverCertReqSigAlgs = TlsUtilities.GetDefaultRsaSignatureAlgorithms(); + c.serverCheckSigAlgOfClientCerts = false; + 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 (isTlsV12) + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientAuth = C.CLIENT_AUTH_VALID; + c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa); + c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms(); + c.serverCheckSigAlgOfClientCerts = false; + 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 (isTlsV12) + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + 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.bad_certificate); + + AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch"); + } + + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY; + c.ExpectServerFatalAlert(AlertDescription.decrypt_error); + + AddTestCase(testSuite, c, prefix + "BadCertificateVerifySignature"); + } + + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientAuth = C.CLIENT_AUTH_INVALID_CERT; + c.ExpectServerFatalAlert(AlertDescription.bad_certificate); + + AddTestCase(testSuite, c, prefix + "BadClientCertificate"); + } + + if (isTlsV13) + { + /* + * For TLS 1.3 the supported_algorithms extension is required in ClientHello when the + * server authenticates via a certificate. + */ + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientSendSignatureAlgorithms = false; + c.clientSendSignatureAlgorithmsCert = false; + c.ExpectServerFatalAlert(AlertDescription.missing_extension); + + AddTestCase(testSuite, c, prefix + "BadClientSigAlgs"); + } + + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientAuth = C.CLIENT_AUTH_NONE; + c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY; + c.ExpectServerFatalAlert(certReqDeclinedAlert); + + AddTestCase(testSuite, c, prefix + "BadMandatoryCertReqDeclined"); + } + + /* + * Server sends SHA-256/RSA certificate, which is not the default {sha1,rsa} implied by the + * absent signature_algorithms extension. We expect fatal alert from the client when it + * verifies the certificate's 'signatureAlgorithm' against the implicit default signature_algorithms. + */ + if (isTlsV12Exactly) + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientSendSignatureAlgorithms = false; + c.clientSendSignatureAlgorithmsCert = false; + c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.rsa); + c.ExpectClientFatalAlert(AlertDescription.bad_certificate); + + AddTestCase(testSuite, c, prefix + "BadServerCertSigAlg"); + } + + /* + * Server selects MD5/RSA for ServerKeyExchange signature, which is not in the default + * supported signature algorithms that the client sent. We expect fatal alert from the + * client when it verifies the selected algorithm against the supported algorithms. + */ + if (TlsUtilities.IsTlsV12(version)) + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa); + c.ExpectClientFatalAlert(AlertDescription.illegal_parameter); + + AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg"); + } + + /* + * Server selects MD5/RSA for ServerKeyExchange signature, which is not the default {sha1,rsa} + * implied by the absent signature_algorithms extension. We expect fatal alert from the + * client when it verifies the selected algorithm against the implicit default. + */ + if (isTlsV12Exactly) + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientCheckSigAlgOfServerCerts = false; + c.clientSendSignatureAlgorithms = false; + c.clientSendSignatureAlgorithmsCert = false; + c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa); + c.ExpectClientFatalAlert(AlertDescription.illegal_parameter); + + AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg2"); + } + + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.serverCertReq = C.SERVER_CERT_REQ_NONE; + + AddTestCase(testSuite, c, prefix + "GoodNoCertReq"); + } + + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.clientAuth = C.CLIENT_AUTH_NONE; + + AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined"); + } + + /* + * Server generates downgraded (RFC 8446) 1.1 ServerHello. We expect fatal alert + * (illegal_parameter) from the client. + */ + if (!isTlsV13) + { + TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto); + c.serverNegotiateVersion = version; + c.serverSupportedVersions = ProtocolVersion.TLSv13.DownTo(version); + c.ExpectClientFatalAlert(AlertDescription.illegal_parameter); + + AddTestCase(testSuite, c, prefix + "BadDowngrade"); + } + } + + private static void AddTestCase(IList testSuite, TlsTestConfig config, string name) + { + testSuite.Add(new TestCaseData(config).SetName(name)); + } + + private static TlsTestConfig CreateTlsTestConfig(ProtocolVersion serverMaxVersion, int clientCrypto, + int serverCrypto) + { + TlsTestConfig c = new TlsTestConfig(); + c.clientCrypto = clientCrypto; + c.clientSupportedVersions = ProtocolVersion.TLSv13.DownTo(ProtocolVersion.SSLv3); + c.serverCrypto = serverCrypto; + c.serverSupportedVersions = serverMaxVersion.DownTo(ProtocolVersion.SSLv3); + return c; + } + + private static string GetCryptoName(int crypto) + { + switch (crypto) + { + case TlsTestConfig.CRYPTO_BC: + default: + return "BC"; + } + } + } +} diff --git a/crypto/test/src/tls/test/TlsTestUtilities.cs b/crypto/test/src/tls/test/TlsTestUtilities.cs new file mode 100644 index 000000000..3ecacb0f0 --- /dev/null +++ b/crypto/test/src/tls/test/TlsTestUtilities.cs @@ -0,0 +1,412 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO.Pem; +using Org.BouncyCastle.Utilities.Test; + +namespace Org.BouncyCastle.Tls.Tests +{ + public class TlsTestUtilities + { + internal static readonly byte[] RsaCertData = Base64.Decode( + "MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2" + + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq" + + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA2MDIwNVoXDTEzMDIyNT" + + "A2MDM0NVowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw" + + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG" + + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy" + + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCBSAwEgYDVR" + + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAHU55Ncz" + + "eglREcTg54YLUlGWu2WOYWhit/iM1eeq8Kivro7q98eW52jTuMI3CI5ulqd0hYzshQKQaZ5GDzErMyM="); + + internal static readonly byte[] DudRsaCertData = Base64.Decode( + "MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2" + + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq" + + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA1NDcyOFoXDTEzMDIyNT" + + "A1NDkwOFowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw" + + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG" + + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy" + + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCAAEwEgYDVR" + + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAJg55PBS" + + "weg6obRUKF4FF6fCrWFi6oCYSQ99LWcAeupc5BofW5MstFMhCOaEucuGVqunwT5G7/DweazzCIrSzB0="); + + internal static bool EqualsIgnoreCase(string a, string b) + { + return ToUpperInvariant(a) == ToUpperInvariant(b); + } + + internal static string ToUpperInvariant(string s) + { + return s.ToUpper(CultureInfo.InvariantCulture); + } + + internal static string Fingerprint(X509CertificateStructure c) + { + byte[] der = c.GetEncoded(); + byte[] hash = Sha256DigestOf(der); + byte[] hexBytes = Hex.Encode(hash); + string hex = ToUpperInvariant(Encoding.ASCII.GetString(hexBytes)); + + StringBuilder fp = new StringBuilder(); + int i = 0; + fp.Append(hex.Substring(i, 2)); + while ((i += 2) < hex.Length) + { + fp.Append(':'); + fp.Append(hex.Substring(i, 2)); + } + return fp.ToString(); + } + + internal static byte[] Sha256DigestOf(byte[] input) + { + return DigestUtilities.CalculateDigest("SHA256", input); + } + + internal static string GetCACertResource(short signatureAlgorithm) + { + return "x509-ca-" + GetResourceName(signatureAlgorithm) + ".pem"; + } + + internal static string GetCACertResource(string eeCertResource) + { + if (eeCertResource.StartsWith("x509-client-")) + { + eeCertResource = eeCertResource.Substring("x509-client-".Length); + } + if (eeCertResource.StartsWith("x509-server-")) + { + eeCertResource = eeCertResource.Substring("x509-server-".Length); + } + if (eeCertResource.EndsWith(".pem")) + { + eeCertResource = eeCertResource.Substring(0, eeCertResource.Length - ".pem".Length); + } + + if (EqualsIgnoreCase("dsa", eeCertResource)) + { + return GetCACertResource(SignatureAlgorithm.dsa); + } + + if (EqualsIgnoreCase("ecdh", eeCertResource) + || EqualsIgnoreCase("ecdsa", eeCertResource)) + { + return GetCACertResource(SignatureAlgorithm.ecdsa); + } + + if (EqualsIgnoreCase("ed25519", eeCertResource)) + { + return GetCACertResource(SignatureAlgorithm.ed25519); + } + + if (EqualsIgnoreCase("ed448", eeCertResource)) + { + return GetCACertResource(SignatureAlgorithm.ed448); + } + + if (EqualsIgnoreCase("rsa", eeCertResource) + || EqualsIgnoreCase("rsa-enc", eeCertResource) + || EqualsIgnoreCase("rsa-sign", eeCertResource)) + { + return GetCACertResource(SignatureAlgorithm.rsa); + } + + if (EqualsIgnoreCase("rsa_pss_256", eeCertResource)) + { + return GetCACertResource(SignatureAlgorithm.rsa_pss_pss_sha256); + } + if (EqualsIgnoreCase("rsa_pss_384", eeCertResource)) + { + return GetCACertResource(SignatureAlgorithm.rsa_pss_pss_sha384); + } + if (EqualsIgnoreCase("rsa_pss_512", eeCertResource)) + { + return GetCACertResource(SignatureAlgorithm.rsa_pss_pss_sha512); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + internal static string GetResourceName(short signatureAlgorithm) + { + switch (signatureAlgorithm) + { + case SignatureAlgorithm.rsa: + case SignatureAlgorithm.rsa_pss_rsae_sha256: + case SignatureAlgorithm.rsa_pss_rsae_sha384: + case SignatureAlgorithm.rsa_pss_rsae_sha512: + return "rsa"; + case SignatureAlgorithm.dsa: + return "dsa"; + case SignatureAlgorithm.ecdsa: + return "ecdsa"; + case SignatureAlgorithm.ed25519: + return "ed25519"; + case SignatureAlgorithm.ed448: + return "ed448"; + case SignatureAlgorithm.rsa_pss_pss_sha256: + return "rsa_pss_256"; + case SignatureAlgorithm.rsa_pss_pss_sha384: + return "rsa_pss_384"; + case SignatureAlgorithm.rsa_pss_pss_sha512: + return "rsa_pss_512"; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + internal static TlsCredentialedAgreement LoadAgreementCredentials(TlsContext context, string[] certResources, + string keyResource) + { + TlsCrypto crypto = context.Crypto; + Certificate certificate = LoadCertificateChain(context, certResources); + + // TODO[tls-ops] Need to have TlsCrypto construct the credentials from the certs/key (as raw data) + if (crypto is BcTlsCrypto) + { + AsymmetricKeyParameter privateKey = LoadBcPrivateKeyResource(keyResource); + + return new BcDefaultTlsCredentialedAgreement((BcTlsCrypto)crypto, certificate, privateKey); + } + else + { + throw new NotSupportedException(); + } + } + + internal static TlsCredentialedDecryptor LoadEncryptionCredentials(TlsContext context, string[] certResources, + string keyResource) + { + TlsCrypto crypto = context.Crypto; + Certificate certificate = LoadCertificateChain(context, certResources); + + // TODO[tls-ops] Need to have TlsCrypto construct the credentials from the certs/key (as raw data) + if (crypto is BcTlsCrypto) + { + AsymmetricKeyParameter privateKey = LoadBcPrivateKeyResource(keyResource); + + return new BcDefaultTlsCredentialedDecryptor((BcTlsCrypto)crypto, certificate, privateKey); + } + else + { + throw new NotSupportedException(); + } + } + + public static TlsCredentialedSigner LoadSignerCredentials(TlsCryptoParameters cryptoParams, TlsCrypto crypto, + string[] certResources, string keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + Certificate certificate = LoadCertificateChain(cryptoParams.ServerVersion, crypto, certResources); + + // TODO[tls-ops] Need to have TlsCrypto construct the credentials from the certs/key (as raw data) + if (crypto is BcTlsCrypto) + { + AsymmetricKeyParameter privateKey = LoadBcPrivateKeyResource(keyResource); + + return new BcDefaultTlsCredentialedSigner(cryptoParams, (BcTlsCrypto)crypto, privateKey, certificate, signatureAndHashAlgorithm); + } + else + { + throw new NotSupportedException(); + } + } + + internal static TlsCredentialedSigner LoadSignerCredentials(TlsContext context, string[] certResources, + string keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + TlsCrypto crypto = context.Crypto; + TlsCryptoParameters cryptoParams = new TlsCryptoParameters(context); + + return LoadSignerCredentials(cryptoParams, crypto, certResources, keyResource, signatureAndHashAlgorithm); + } + + internal static TlsCredentialedSigner LoadSignerCredentials(TlsContext context, + IList supportedSignatureAlgorithms, short signatureAlgorithm, string certResource, string keyResource) + { + if (supportedSignatureAlgorithms == null) + { + supportedSignatureAlgorithms = TlsUtilities.GetDefaultSignatureAlgorithms(signatureAlgorithm); + } + + SignatureAndHashAlgorithm signatureAndHashAlgorithm = null; + + foreach (SignatureAndHashAlgorithm alg in supportedSignatureAlgorithms) + { + if (alg.Signature == signatureAlgorithm) + { + // Just grab the first one we find + signatureAndHashAlgorithm = alg; + break; + } + } + + if (signatureAndHashAlgorithm == null) + return null; + + return LoadSignerCredentials(context, new string[]{ certResource }, keyResource, + signatureAndHashAlgorithm); + } + + internal static TlsCredentialedSigner LoadSignerCredentialsServer(TlsContext context, + IList supportedSignatureAlgorithms, short signatureAlgorithm) + { + string sigName = GetResourceName(signatureAlgorithm); + + switch (signatureAlgorithm) + { + case SignatureAlgorithm.rsa: + case SignatureAlgorithm.rsa_pss_rsae_sha256: + case SignatureAlgorithm.rsa_pss_rsae_sha384: + case SignatureAlgorithm.rsa_pss_rsae_sha512: + sigName += "-sign"; + break; + } + + string certResource = "x509-server-" + sigName + ".pem"; + string keyResource = "x509-server-key-" + sigName + ".pem"; + + return LoadSignerCredentials(context, supportedSignatureAlgorithms, signatureAlgorithm, certResource, + keyResource); + } + + internal static Certificate LoadCertificateChain(ProtocolVersion protocolVersion, TlsCrypto crypto, + string[] resources) + { + if (TlsUtilities.IsTlsV13(protocolVersion)) + { + CertificateEntry[] certificateEntryList = new CertificateEntry[resources.Length]; + for (int i = 0; i < resources.Length; ++i) + { + TlsCertificate certificate = LoadCertificateResource(crypto, resources[i]); + + // TODO[tls13] Add possibility of specifying e.g. CertificateStatus + IDictionary extensions = null; + + certificateEntryList[i] = new CertificateEntry(certificate, extensions); + } + + // TODO[tls13] Support for non-empty request context + byte[] certificateRequestContext = TlsUtilities.EmptyBytes; + + return new Certificate(certificateRequestContext, certificateEntryList); + } + else + { + TlsCertificate[] chain = new TlsCertificate[resources.Length]; + for (int i = 0; i < resources.Length; ++i) + { + chain[i] = LoadCertificateResource(crypto, resources[i]); + } + return new Certificate(chain); + } + } + + internal static Certificate LoadCertificateChain(TlsContext context, string[] resources) + { + return LoadCertificateChain(context.ServerVersion, context.Crypto, resources); + } + + internal static X509CertificateStructure LoadBcCertificateResource(string resource) + { + PemObject pem = LoadPemResource(resource); + if (pem.Type.EndsWith("CERTIFICATE")) + { + return X509CertificateStructure.GetInstance(pem.Content); + } + throw new ArgumentException("doesn't specify a valid certificate", "resource"); + } + + internal static TlsCertificate LoadCertificateResource(TlsCrypto crypto, string resource) + { + PemObject pem = LoadPemResource(resource); + if (pem.Type.EndsWith("CERTIFICATE")) + { + return crypto.CreateCertificate(pem.Content); + } + throw new ArgumentException("doesn't specify a valid certificate", "resource"); + } + + internal static AsymmetricKeyParameter LoadBcPrivateKeyResource(string resource) + { + PemObject pem = LoadPemResource(resource); + if (pem.Type.Equals("PRIVATE KEY")) + { + return PrivateKeyFactory.CreateKey(pem.Content); + } + if (pem.Type.Equals("ENCRYPTED PRIVATE KEY")) + { + throw new NotSupportedException("Encrypted PKCS#8 keys not supported"); + } + if (pem.Type.Equals("RSA PRIVATE KEY")) + { + RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(pem.Content); + return new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, + rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, + rsa.Exponent2, rsa.Coefficient); + } + if (pem.Type.Equals("EC PRIVATE KEY")) + { + ECPrivateKeyStructure pKey = ECPrivateKeyStructure.GetInstance(pem.Content); + AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey, + pKey.GetParameters()); + PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey); + return PrivateKeyFactory.CreateKey(privInfo); + } + throw new ArgumentException("doesn't specify a valid private key", "resource"); + } + + internal static PemObject LoadPemResource(string resource) + + { + Stream s = SimpleTest.GetTestDataAsStream("tls." + resource); + PemReader p = new PemReader(new StreamReader(s)); + PemObject o = p.ReadPemObject(); + p.Reader.Close(); + return o; + } + + internal static bool AreSameCertificate(TlsCrypto crypto, TlsCertificate cert, string resource) + { + // TODO Cache test resources? + return AreSameCertificate(cert, LoadCertificateResource(crypto, resource)); + } + + internal static bool AreSameCertificate(TlsCertificate a, TlsCertificate b) + { + // TODO[tls-ops] Support equals on TlsCertificate? + return Arrays.AreEqual(a.GetEncoded(), b.GetEncoded()); + } + + internal static TlsCertificate[] GetTrustedCertPath(TlsCrypto crypto, TlsCertificate cert, string[] resources) + { + foreach (string eeCertResource in resources) + { + TlsCertificate eeCert = LoadCertificateResource(crypto, eeCertResource); + if (AreSameCertificate(cert, eeCert)) + { + string caCertResource = GetCACertResource(eeCertResource); + TlsCertificate caCert = LoadCertificateResource(crypto, caCertResource); + if (null != caCert) + { + return new TlsCertificate[]{ eeCert, caCert }; + } + } + } + return null; + } + } +} diff --git a/crypto/test/src/tls/test/TlsUtilitiesTest.cs b/crypto/test/src/tls/test/TlsUtilitiesTest.cs new file mode 100644 index 000000000..702c40082 --- /dev/null +++ b/crypto/test/src/tls/test/TlsUtilitiesTest.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections; + +using NUnit.Framework; + +namespace Org.BouncyCastle.Tls.Tests +{ + [TestFixture] + public class TlsUtilitiesTest + { + [Test] + public void TestChooseSignatureAndHash() + { + int keyExchangeAlgorithm = KeyExchangeAlgorithm.ECDHE_RSA; + short signatureAlgorithm = TlsUtilities.GetLegacySignatureAlgorithmServer(keyExchangeAlgorithm); + + IList supportedSignatureAlgorithms = GetSignatureAlgorithms(false); + SignatureAndHashAlgorithm sigAlg = TlsUtilities.ChooseSignatureAndHashAlgorithm(ProtocolVersion.TLSv12, + supportedSignatureAlgorithms, signatureAlgorithm); + Assert.AreEqual(HashAlgorithm.sha256, sigAlg.Hash); + + for (int count = 0; count < 10; ++count) + { + supportedSignatureAlgorithms = GetSignatureAlgorithms(true); + sigAlg = TlsUtilities.ChooseSignatureAndHashAlgorithm(ProtocolVersion.TLSv12, + supportedSignatureAlgorithms, signatureAlgorithm); + Assert.AreEqual(HashAlgorithm.sha256, sigAlg.Hash); + } + } + + private static IList GetSignatureAlgorithms(bool randomise) + { + short[] hashAlgorithms = new short[]{ HashAlgorithm.sha1, HashAlgorithm.sha224, HashAlgorithm.sha256, + HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.md5 }; + short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa, SignatureAlgorithm.dsa, + SignatureAlgorithm.ecdsa }; + + IList result = new ArrayList(); + for (int i = 0; i < signatureAlgorithms.Length; ++i) + { + for (int j = 0; j < hashAlgorithms.Length; ++j) + { + result.Add(SignatureAndHashAlgorithm.GetInstance(hashAlgorithms[j], signatureAlgorithms[i])); + } + } + + if (randomise) + { + Random r = new Random(); + int count = result.Count; + for (int src = 0; src < count; ++src) + { + int dst = r.Next(count); + if (src != dst) + { + object a = result[src], b = result[dst]; + result[dst] = a; + result[src] = b; + } + } + } + + return result; + } + } +} diff --git a/crypto/test/src/tls/test/UnreliableDatagramTransport.cs b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs new file mode 100644 index 000000000..bdbfd6e67 --- /dev/null +++ b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs @@ -0,0 +1,79 @@ +using System; + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Tls.Tests +{ + public class UnreliableDatagramTransport + : DatagramTransport + { + private readonly DatagramTransport m_transport; + private readonly Random m_random; + private readonly int m_percentPacketLossReceiving, m_percentPacketLossSending; + + public UnreliableDatagramTransport(DatagramTransport transport, Random random, + int percentPacketLossReceiving, int percentPacketLossSending) + { + if (percentPacketLossReceiving < 0 || percentPacketLossReceiving > 100) + throw new ArgumentException("out of range", "percentPacketLossReceiving"); + if (percentPacketLossSending < 0 || percentPacketLossSending > 100) + throw new ArgumentException("out of range", "percentPacketLossSending"); + + this.m_transport = transport; + this.m_random = random; + this.m_percentPacketLossReceiving = percentPacketLossReceiving; + this.m_percentPacketLossSending = percentPacketLossSending; + } + + public virtual int GetReceiveLimit() + { + return m_transport.GetReceiveLimit(); + } + + public virtual int GetSendLimit() + { + return m_transport.GetSendLimit(); + } + + public virtual int Receive(byte[] buf, int off, int len, int waitMillis) + { + long endMillis = DateTimeUtilities.CurrentUnixMs() + waitMillis; + for (;;) + { + int length = m_transport.Receive(buf, off, len, waitMillis); + if (length < 0 || !LostPacket(m_percentPacketLossReceiving)) + return length; + + Console.WriteLine("PACKET LOSS (" + length + " byte packet not received)"); + + long now = DateTimeUtilities.CurrentUnixMs(); + if (now >= endMillis) + return -1; + + waitMillis = (int)(endMillis - now); + } + } + + public virtual void Send(byte[] buf, int off, int len) + { + if (LostPacket(m_percentPacketLossSending)) + { + Console.WriteLine("PACKET LOSS (" + len + " byte packet not sent)"); + } + else + { + m_transport.Send(buf, off, len); + } + } + + public virtual void Close() + { + m_transport.Close(); + } + + private bool LostPacket(int percentPacketLoss) + { + return percentPacketLoss > 0 && m_random.Next(100) < percentPacketLoss; + } + } +} |