diff options
author | Juraj Somorovsky <[email protected]> | 2016-09-17 14:44:40 +0200 |
---|---|---|
committer | Juraj Somorovsky <[email protected]> | 2016-09-30 00:57:29 +0200 |
commit | 863fc12c6ebcc96ed10a7c8896fea033a78fbb5d (patch) | |
tree | 129adf63325c4dd6b9ea58b5e397fe3aee28bb41 | |
parent | ebe2f21dde0bd26261af633a96867df2372779cb (diff) |
New TLS positive and negative tests.
TLS message parsing:
- CertificateVerify
- HelloVerify
- ClientHello (with extensions)
- ServerHello (with extensions)
- NewSessionTicket
- Alert
TLS message processing:
- HelloVerify
TLS Policy tests
Unit tests with TLS client authentication
Added test_throws method that checks the correct exception message.
-rw-r--r-- | src/lib/tls/tls_extensions.h | 2 | ||||
-rw-r--r-- | src/lib/tls/tls_messages.h | 12 | ||||
-rw-r--r-- | src/tests/data/tls/alert.vec | 24 | ||||
-rw-r--r-- | src/tests/data/tls/cert_verify.vec | 40 | ||||
-rw-r--r-- | src/tests/data/tls/client_hello.vec | 68 | ||||
-rw-r--r-- | src/tests/data/tls/hello_request.vec | 8 | ||||
-rw-r--r-- | src/tests/data/tls/hello_verify.vec | 26 | ||||
-rw-r--r-- | src/tests/data/tls/new_session_ticket.vec | 20 | ||||
-rw-r--r-- | src/tests/data/tls/server_hello.vec | 48 | ||||
-rw-r--r-- | src/tests/test_tls_messages.cpp | 203 | ||||
-rw-r--r-- | src/tests/tests.cpp | 23 | ||||
-rw-r--r-- | src/tests/tests.h | 3 | ||||
-rw-r--r-- | src/tests/unit_tls.cpp | 14 | ||||
-rw-r--r-- | src/tests/unit_tls_policy.cpp | 162 |
14 files changed, 642 insertions, 11 deletions
diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index 4bd564a85..c270bf23a 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -366,7 +366,7 @@ class Encrypt_then_MAC final : public Extension /** * Represents a block of extensions in a hello message */ -class Extensions +class BOTAN_DLL Extensions { public: std::set<Handshake_Extension_Type> extension_types() const; diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index cf35053f2..698dbc99f 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -40,7 +40,7 @@ std::vector<byte> make_hello_random(RandomNumberGenerator& rng, /** * DTLS Hello Verify Request */ -class Hello_Verify_Request final : public Handshake_Message +class BOTAN_DLL Hello_Verify_Request final : public Handshake_Message { public: std::vector<byte> serialize() const override; @@ -60,7 +60,7 @@ class Hello_Verify_Request final : public Handshake_Message /** * Client Hello Message */ -class Client_Hello final : public Handshake_Message +class BOTAN_DLL Client_Hello final : public Handshake_Message { public: class Settings @@ -234,7 +234,7 @@ class Client_Hello final : public Handshake_Message /** * Server Hello Message */ -class Server_Hello final : public Handshake_Message +class BOTAN_DLL Server_Hello final : public Handshake_Message { public: class Settings @@ -452,7 +452,7 @@ class Certificate_Req final : public Handshake_Message /** * Certificate Verify Message */ -class Certificate_Verify final : public Handshake_Message +class BOTAN_DLL Certificate_Verify final : public Handshake_Message { public: Handshake_Type type() const override { return CERTIFICATE_VERIFY; } @@ -510,7 +510,7 @@ class Finished final : public Handshake_Message /** * Hello Request Message */ -class Hello_Request final : public Handshake_Message +class BOTAN_DLL Hello_Request final : public Handshake_Message { public: Handshake_Type type() const override { return HELLO_REQUEST; } @@ -592,7 +592,7 @@ class Server_Hello_Done final : public Handshake_Message /** * New Session Ticket Message */ -class New_Session_Ticket final : public Handshake_Message +class BOTAN_DLL New_Session_Ticket final : public Handshake_Message { public: Handshake_Type type() const override { return NEW_SESSION_TICKET; } diff --git a/src/tests/data/tls/alert.vec b/src/tests/data/tls/alert.vec new file mode 100644 index 000000000..19ec8839b --- /dev/null +++ b/src/tests/data/tls/alert.vec @@ -0,0 +1,24 @@ +# Alert message contains the following fields: +# - Fatal (1 byte): 1=false, 2=true +# - Type (1 byte) + +[alert] +Buffer = 0130 +Exception = + +Buffer = 0230 +Exception = + +Buffer = 0231 +Exception = + +Buffer = 0030 +Protocol = 0303 +Exception = Invalid argument Decoding error: Alert: Bad code for alert level + +Buffer = 02 +Exception = Invalid argument Decoding error: Alert: Bad size 1 for alert message + +Buffer = 020101 +Exception = Invalid argument Decoding error: Alert: Bad size 3 for alert message + diff --git a/src/tests/data/tls/cert_verify.vec b/src/tests/data/tls/cert_verify.vec new file mode 100644 index 000000000..f812d1c6a --- /dev/null +++ b/src/tests/data/tls/cert_verify.vec @@ -0,0 +1,40 @@ +# Tests generated partially with openssl 1.0.2g +# CertificateVerify message contains the following fields: +# - SignatureAndHash Algorithm (2 bytes) [only in TLS 1.2] +# - Certificate length (2 bytes) +# - Certificate + +[cert_verify] +Buffer = 06010080266481066a8431582157a9a591150d418b63d46154c4cd85bffcfdba8c7f6396f0ceb0402c2142c526a19659d58cd4111bf45f57a56e97d16eeecd350f6e9dc93662e4361053666e5a53c74fe11bd6cf86a9cf7a2488704c5121915820973280ed6afa3e8b79dfb799bddffb52caa2d1a0a895a0e7505d841a882bdd92ec9141 +Protocol = 0303 +Exception = + +Buffer = 008080c920a228dc3f32927fd8026a97fb8474603191a89c49aeeddd1b1caf7f28d6af7b9b7c0bc6b954e909f3d054eb3964d626402b7c932c019111bc854007c90c134d6adce505e5cd60292331f7645fba909017565fc60ee76a5eb6b6a89ab2a3d69be6c0e283ae5a84b1fc367c1a865c35dd8a1c93ac3d538d91a2d5128d8d52 +Protocol = 0302 +Exception = + +Buffer = 0080bb6b1df8c744f961ee3f5334448fac4af0f372763149972b88bec525a3196f87cf0204a50fd516b6808530252d1c6b79414b8b9194b3c5e2958adab5524bc124e16d9f3b05f5bf63c0b184709ce6586a0a4b267280b47576893406c381a401b10bcc5f111b14cd8ce889b5d48fbe47f465cf70bf23b71109f81d4574bbf6f93f +Protocol = 0301 +Exception = + +Buffer = 0601000100 +Protocol = 0303 +Exception = + +#Incomplete algorithm +Buffer = 06 +Protocol = 0303 +Exception = Invalid argument Decoding error: Invalid CertificateVerify: Expected 1 bytes remaining, only 0 left + +#Incomplete certificate +Buffer = 0601000500 +Protocol = 0303 +Exception = Invalid argument Decoding error: Invalid CertificateVerify: Expected 5 bytes remaining, only 1 left + +Buffer = 000200 +Protocol = 0302 +Exception = Invalid argument Decoding error: Invalid CertificateVerify: Expected 2 bytes remaining, only 1 left + +Buffer = 000200 +Protocol = 0301 +Exception = Invalid argument Decoding error: Invalid CertificateVerify: Expected 2 bytes remaining, only 1 left
\ No newline at end of file diff --git a/src/tests/data/tls/client_hello.vec b/src/tests/data/tls/client_hello.vec new file mode 100644 index 000000000..d629e3f6e --- /dev/null +++ b/src/tests/data/tls/client_hello.vec @@ -0,0 +1,68 @@ +# Tests generated partially with openssl 1.0.2g/1.1.0a and TLS-Attacker +# ClientHello message contains many fields, the following fields are checked: +# - Protocol Version +# - Extensions + +[client_hello] +# no extension (empty renegotiation generated) +Buffer = 030320f3dc33f90be6509e6133a1819f2b80fe6ccc6268d9195ca4ead7504ffe7e2a0000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff01000000 +Protocol = 0303 +AdditionalData = FF01 +Exception = + +# with extensions: point formats, ec curves, session ticket, signature algorithms, heartbeat (point formats and heartbeat not supported, empty renegotiation generated) +Buffer = 0303871e18983024eaee1be8ae6607d5ecad941d33fd7fc1d8554a9e1fbfda8d30880000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff01000055000b000403000102000a001c001a00170019001c001b0018001a0016000e000d000b000c0009000a00230000000d0020001e060106020603050105020503040104020403030103020303020102020203000f000101 +Protocol = 0303 +AdditionalData = 000A000D0023FF01 +Exception = + +# with extensions: point formats, ec curves, session ticket, signature algorithms, heartbeat, Encrypt-then-MAC, Extended Master Secret (point formats and heartbeat not supported, empty renegotiation generated) +Buffer = 0303e00da23523058b5dc9c445d97b2bb6315b019e97838ac4f16c23b2cb031b6a490000e2c0afc0adc030c02cc028c024c014c00ac0a3c09f00a500a300a1009f006b006a006900680039003800370036cca9cca8c077c073ccaa00c400c300c200c10088008700860085c032c02ec02ac026c00fc005c079c075c0a1c09d009d003d003500c00084c0aec0acc02fc02bc027c023c013c009c0a2c09e00a400a200a0009e00670040003f003e0033003200310030c076c07200be00bd00bc00bb009a0099009800970045004400430042c031c02dc029c025c00ec004c078c074c0a0c09c009c003c002f00ba009600410007c012c008001600130010000dc00dc003000a00ff0100005f000b000403000102000a001c001a00170019001c001b0018001a0016000e000d000b000c0009000a00230000000d00220020060106020603050105020503040104020403030103020303020102020203eded000f0001010016000000170000 +Protocol = 0303 +AdditionalData = 000A000D001600170023FF01 +Exception = + +# empty +Buffer = +Protocol = 0303 +Exception = Invalid argument Decoding error: Client_Hello: Packet corrupted + +Buffer = 00 +Protocol = 0303 +Exception = Invalid argument Decoding error: Client_Hello: Packet corrupted + +# Invalid cipher suite length (0xf0e2 instead of 0x00e2) +Buffer = 0303e00da23523058b5dc9c445d97b2bb6315b019e97838ac4f16c23b2cb031b6a4900f0e2c0afc0adc030c02cc028c024c014c00ac0a3c09f00a500a300a1009f006b006a006900680039003800370036cca9cca8c077c073ccaa00c400c300c200c10088008700860085c032c02ec02ac026c00fc005c079c075c0a1c09d009d003d003500c00084c0aec0acc02fc02bc027c023c013c009c0a2c09e00a400a200a0009e00670040003f003e0033003200310030c076c07200be00bd00bc00bb009a0099009800970045004400430042c031c02dc029c025c00ec004c078c074c0a0c09c009c003c002f00ba009600410007c012c008001600130010000dc00dc003000a00ff01000000 +Protocol = 0303 +AdditionalData = +Exception = Invalid argument Decoding error: Invalid ClientHello: Expected 61666 bytes remaining, only 230 left + +#invalid extensions length +Buffer = 030320f3dc33f90be6509e6133a1819f2b80fe6ccc6268d9195ca4ead7504ffe7e2a0000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff01000001 +Protocol = 0303 +Exception = Invalid argument Decoding error: Bad extension size + +#invalid extensions length 2 +Buffer = 030320f3dc33f90be6509e6133a1819f2b80fe6ccc6268d9195ca4ead7504ffe7e2a0000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff010000010000 +Protocol = 0303 +Exception = Invalid argument Decoding error: Bad extension size + +#invalid length of the elliptic curve extension (0xf01c instead of 0x001c) +Buffer = 0303871e18983024eaee1be8ae6607d5ecad941d33fd7fc1d8554a9e1fbfda8d30880000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff01000055000b000403000102000af01c001a00170019001c001b0018001a0016000e000d000b000c0009000a00230000000d0020001e060106020603050105020503040104020403030103020303020102020203000f000101 +Protocol = 0303 +Exception = Invalid argument Decoding error: Inconsistent length field in elliptic curve list + +#invalid length of the elliptic curve extension (0xf01a instead of 0x001a) +Buffer = 0303871e18983024eaee1be8ae6607d5ecad941d33fd7fc1d8554a9e1fbfda8d30880000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff01000055000b000403000102000a001cf01a00170019001c001b0018001a0016000e000d000b000c0009000a00230000000d0020001e060106020603050105020503040104020403030103020303020102020203000f000101 +Protocol = 0303 +Exception = Invalid argument Decoding error: Inconsistent length field in elliptic curve list + +#invalid length of the session ticket extension +Buffer = 0303871e18983024eaee1be8ae6607d5ecad941d33fd7fc1d8554a9e1fbfda8d30880000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff01000055000b000403000102000a001c001a00170019001c001b0018001a0016000e000d000b000c0009000a002300ff000d0020001e060106020603050105020503040104020403030103020303020102020203000f000101 +Protocol = 0303 +Exception = Invalid argument Decoding error: Invalid ClientHello: Expected 255 bytes remaining, only 41 left + +#invalid length of the heartbeat extension +Buffer = 0303871e18983024eaee1be8ae6607d5ecad941d33fd7fc1d8554a9e1fbfda8d30880000aac030c02cc028c024c014c00a00a500a300a1009f006b006a0069006800390038003700360088008700860085c032c02ec02ac026c00fc005009d003d00350084c02fc02bc027c023c013c00900a400a200a0009e00670040003f003e0033003200310030009a0099009800970045004400430042c031c02dc029c025c00ec004009c003c002f00960041c011c007c00cc00200050004c012c008001600130010000dc00dc003000a00ff01000055000b000403000102000a001c001a00170019001c001b0018001a0016000e000d000b000c0009000a00230000000d0020001e060106020603050105020503040104020403030103020303020102020203000f000201 +Protocol = 0303 +Exception = Invalid argument Decoding error: Invalid ClientHello: Expected 2 bytes remaining, only 1 left
\ No newline at end of file diff --git a/src/tests/data/tls/hello_request.vec b/src/tests/data/tls/hello_request.vec new file mode 100644 index 000000000..3a7471ae4 --- /dev/null +++ b/src/tests/data/tls/hello_request.vec @@ -0,0 +1,8 @@ +# HelloRequest message does not contain any bytes + +[hello_request] +Buffer = +Exception = + +Buffer = 01 +Exception = Invalid argument Decoding error: Bad Hello_Request, has non-zero size diff --git a/src/tests/data/tls/hello_verify.vec b/src/tests/data/tls/hello_verify.vec new file mode 100644 index 000000000..f5db9e085 --- /dev/null +++ b/src/tests/data/tls/hello_verify.vec @@ -0,0 +1,26 @@ +# Tests generated partially with openssl 1.0.2g +# HelloVerify message contains the following fields: +# - Protocol version (2 bytes) +# - Cookie length (1 byte) +# - Cookie + +[hello_verify] +Buffer = feff14925523e7539a13d9782af6d771b97d0032c61800 +Exception = + +# HelloVerify request has to contain at least 3 bytes +Buffer = 0101 +Exception = Invalid argument Decoding error: Hello verify request too small + +# HelloVerify has to contain valid protocol version +Buffer = 010100 +Exception = Invalid argument Decoding error: Unknown version from server in hello verify request + +# HelloVerify has to contain valid number of bytes +Buffer = FEFD0000 +Exception = Invalid argument Decoding error: Bad length in hello verify request + +# HelloVerify has to contain valid number of bytes +Buffer = FEFD0500 +Exception = Invalid argument Decoding error: Bad length in hello verify request + diff --git a/src/tests/data/tls/new_session_ticket.vec b/src/tests/data/tls/new_session_ticket.vec new file mode 100644 index 000000000..22c03611e --- /dev/null +++ b/src/tests/data/tls/new_session_ticket.vec @@ -0,0 +1,20 @@ +# NewSessionTicket message contains the following fields: +# - lifetime (4 bytes) +# - length (2 bytes) +# - session ticket + +[new_session_ticket] +Buffer = 000000000000 +Exception = + +Buffer = 00000000000100 +Exception = + +Buffer = 0000000000051122334455 +Exception = + +Buffer = 0001 +Exception = Invalid argument Decoding error: Session ticket message too short to be valid + +Buffer = 00010203000500 +Exception = Invalid argument Decoding error: Invalid SessionTicket: Expected 5 bytes remaining, only 1 left
\ No newline at end of file diff --git a/src/tests/data/tls/server_hello.vec b/src/tests/data/tls/server_hello.vec new file mode 100644 index 000000000..64ec40b80 --- /dev/null +++ b/src/tests/data/tls/server_hello.vec @@ -0,0 +1,48 @@ +# Tests generated partially with openssl 1.0.2g +# ServerHello message contains many fields, the following fields are checked: +# - Protocol Version +# - Cipher suite +# - Extensions + +[server_hello] +# correct, with session ticket and renegotiation info +Buffer = 0303ffea0bcfba564a4ce177c6a444b0ebdff5629b277293c618c1125f231e8628dd00c030000016ff01000100000b00040300010200230000000f000101 +Protocol = 0303 +Ciphersuite = C030 +AdditionalData = 0023FF01 +Exception = + +# correct, with session ticket, extended master secret, and renegotiation info +Buffer = 03019f9cafa88664d9095f85dd64a39e5dd5c09f5a4a5362938af3718ee4e818af6a00c03000001aff01000100000b00040300010200230000000f00010100170000 +Protocol = 0301 +Ciphersuite = C030 +AdditionalData = 00170023FF01 +Exception = + +# incorrect, corrupted +Buffer = +Protocol = 0303 +Ciphersuite = C030 +AdditionalData = +Exception = Invalid argument Decoding error: Server_Hello: Packet corrupted + +# incorrect, corrupted +Buffer = 00 +Protocol = 0303 +Ciphersuite = C030 +AdditionalData = +Exception = Invalid argument Decoding error: Server_Hello: Packet corrupted + +# invalid extensions length +Buffer = 03039f9cafa88664d9095f85dd64a39e5dd5c09f5a4a5362938af3718ee4e818af6a00c03000001cff01000100000b00040300010200230000000f00010100170000 +Protocol = 0303 +Ciphersuite = C030 +AdditionalData = 00170023FF01 +Exception = Invalid argument Decoding error: Bad extension size + +# invalid extension length +Buffer = 03039f9cafa88664d9095f85dd64a39e5dd5c09f5a4a5362938af3718ee4e818af6a00c03000001aff01000100000b00040300010200230100000f00010100170000 +Protocol = 0303 +Ciphersuite = C030 +AdditionalData = 00170023FF01 +Exception = Invalid argument Decoding error: Invalid ServerHello: Expected 256 bytes remaining, only 9 left
\ No newline at end of file diff --git a/src/tests/test_tls_messages.cpp b/src/tests/test_tls_messages.cpp new file mode 100644 index 000000000..da6e72f79 --- /dev/null +++ b/src/tests/test_tls_messages.cpp @@ -0,0 +1,203 @@ +/* +* (C) 2016 Juraj Somorovsky +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS) + #include <exception> + #include <botan/hex.h> + #include <botan/mac.h> + #include <botan/tls_ciphersuite.h> + #include <botan/tls_handshake_msg.h> + #include <botan/internal/tls_messages.h> +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_TLS) +Test::Result test_hello_verify_request() + { + Test::Result result("hello_verify_request construction"); + + std::vector<byte> test_data; + std::vector<byte> key_data(32); + Botan::SymmetricKey sk(key_data); + + // Compute cookie over an empty string with an empty test data + Botan::TLS::Hello_Verify_Request hfr(test_data, "", sk); + + // Compute HMAC + std::unique_ptr<Botan::MessageAuthenticationCode> hmac(Botan::MessageAuthenticationCode::create("HMAC(SHA-256)")); + hmac->set_key(sk); + hmac->update_be(size_t(0)); + hmac->update_be(size_t(0)); + std::vector<byte> test = unlock(hmac->final()); + + result.test_eq("Cookie comparison", hfr.cookie(), test); + return result; + } + +class TLS_Message_Parsing_Test : public Text_Based_Test + { + public: + TLS_Message_Parsing_Test() : + Text_Based_Test("tls", {"Buffer", "Protocol", "Ciphersuite", "AdditionalData", "Exception"}) + {} + + Test::Result run_one_test(const std::string& algo, const VarMap& vars) override + { + const std::vector<uint8_t> buffer = get_req_bin(vars, "Buffer"); + const std::vector<uint8_t> protocol = get_opt_bin(vars, "Protocol"); + const std::vector<uint8_t> ciphersuite = get_opt_bin(vars, "Ciphersuite"); + const std::string exception = get_req_str(vars, "Exception"); + const bool is_positive_test = exception.empty(); + + Test::Result result(algo + " parsing"); + + if(is_positive_test) + { + try + { + if(algo == "cert_verify") + { + Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); + Botan::TLS::Certificate_Verify message(buffer, pv); + } + if(algo == "client_hello") + { + const std::string extensions = get_req_str(vars, "AdditionalData"); + Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); + Botan::TLS::Client_Hello message(buffer); + result.test_eq("Protocol version", message.version().to_string(), pv.to_string()); + std::vector<byte> buf; + for(Botan::TLS::Handshake_Extension_Type const& type : message.extension_types()) + { + Botan::u16bit u16type = type; + buf.push_back(Botan::get_byte(0, u16type)); + buf.push_back(Botan::get_byte(1, u16type)); + } + result.test_eq("Hello extensions", Botan::hex_encode(buf), extensions); + } + else if(algo == "hello_verify") + { + Botan::TLS::Hello_Verify_Request message(buffer); + } + else if(algo == "hello_request") + { + Botan::TLS::Hello_Request message(buffer); + } + else if(algo == "new_session_ticket") + { + Botan::TLS::New_Session_Ticket message(buffer); + } + if(algo == "server_hello") + { + const std::string extensions = get_req_str(vars, "AdditionalData"); + Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); + Botan::TLS::Ciphersuite cs = Botan::TLS::Ciphersuite::by_id(Botan::make_u16bit(ciphersuite[0], ciphersuite[1])); + Botan::TLS::Server_Hello message(buffer); + result.test_eq("Protocol version", message.version().to_string(), pv.to_string()); + result.confirm("Ciphersuite", (message.ciphersuite() == cs.ciphersuite_code())); + std::vector<byte> buf; + for(Botan::TLS::Handshake_Extension_Type const& type : message.extension_types()) + { + Botan::u16bit u16type = type; + buf.push_back(Botan::get_byte(0, u16type)); + buf.push_back(Botan::get_byte(1, u16type)); + } + result.test_eq("Hello extensions", Botan::hex_encode(buf), extensions); + } + else if(algo == "alert") + { + Botan::secure_vector<uint8_t> sb(buffer.begin(), buffer.end()); + Botan::TLS::Alert message(sb); + result.test_lt("Alert type vectors result to UNKNOWN_CA or ACCESS_DENIED, which is shorter than 15", + message.type_string().size(), 15); + } + result.test_success("Correct parsing"); + } + catch(std::exception& e) + { + result.test_failure(e.what()); + } + } + else + { + if(algo == "cert_verify") + { + Botan::TLS::Protocol_Version pv(protocol[0], protocol[1]); + result.test_throws("invalid cert_verify input", exception, [&buffer, &pv]() + { + Botan::TLS::Certificate_Verify message(buffer, pv); + }); + } + else if(algo == "client_hello") + { + result.test_throws("invalid client_hello input", exception, [&buffer]() + { + Botan::TLS::Client_Hello message(buffer); + }); + } + else if(algo == "hello_verify") + { + result.test_throws("invalid hello_verify input", exception, [&buffer]() + { + Botan::TLS::Hello_Verify_Request message(buffer); + }); + } + else if(algo == "hello_request") + { + result.test_throws("invalid hello_request input", exception, [&buffer]() + { + Botan::TLS::Hello_Request message(buffer); + }); + } + else if(algo == "new_session_ticket") + { + result.test_throws("invalid new_session_ticket input", exception, [&buffer]() + { + Botan::TLS::New_Session_Ticket message(buffer); + }); + } + else if(algo == "server_hello") + { + result.test_throws("invalid server_hello input", exception, [&buffer]() + { + Botan::TLS::Server_Hello message(buffer); + }); + } + else if(algo == "alert") + { + result.test_throws("invalid alert input", exception, [&buffer]() + { + Botan::secure_vector<uint8_t> sb(buffer.begin(), buffer.end()); + Botan::TLS::Alert message(sb); + }); + } + } + + return result; + } + + std::vector<Test::Result> run_final_tests() override + { + std::vector<Test::Result> results; + + results.push_back(test_hello_verify_request()); + + return results; + } + }; + +BOTAN_REGISTER_TEST("tls_messages", TLS_Message_Parsing_Test); + +#endif + +} + +} diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index b47f2a7ab..1bb8b7303 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -94,6 +94,29 @@ bool Test::Result::test_throws(const std::string& what, std::function<void ()> f } } +bool Test::Result::test_throws(const std::string& what, const std::string& expected, std::function<void ()> fn) + { + try { + fn(); + return test_failure(what + " failed to throw expected exception"); + } + catch(std::exception& e) + { + if(expected == e.what()) + { + return test_success(what + " threw exception " + e.what()); + } + else + { + return test_failure(what + " failed to throw an exception with the expected text:\n Expected: " + expected + "\n Got: " + e.what()); + } + } + catch(...) + { + return test_failure(what + " failed to throw an exception with the expected text:\n Expected: " + expected); + } + } + bool Test::Result::test_success(const std::string& note) { if(Test::log_success()) diff --git a/src/tests/tests.h b/src/tests/tests.h index 1ceb24f48..fb8d357d4 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -291,6 +291,9 @@ class Test } bool test_throws(const std::string& what, std::function<void ()> fn); + + bool test_throws(const std::string& what, const std::string& expected, + std::function<void ()> fn); void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; } diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index 081b1038d..de54c9747 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -41,6 +41,7 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager { std::unique_ptr<Botan::Certificate_Store> store(new Botan::Certificate_Store_In_Memory(m_ca_cert)); m_stores.push_back(std::move(store)); + m_provides_client_certs = false; } std::vector<Botan::Certificate_Store*> @@ -60,7 +61,7 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager { std::vector<Botan::X509_Certificate> chain; - if(type == "tls-server") + if(type == "tls-server" || (type == "tls-client" && m_provides_client_certs)) { bool have_match = false; for(size_t i = 0; i != cert_key_types.size(); ++i) @@ -114,9 +115,10 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager Botan::X509_Certificate m_server_cert, m_ca_cert; std::unique_ptr<Botan::Private_Key> m_key; std::vector<std::unique_ptr<Botan::Certificate_Store>> m_stores; + bool m_provides_client_certs; }; -Botan::Credentials_Manager* create_creds() +Botan::Credentials_Manager* create_creds(bool client_type) { std::unique_ptr<Botan::Private_Key> ca_key(new Botan::RSA_PrivateKey(Test::rng(), 1024)); @@ -154,7 +156,9 @@ Botan::Credentials_Manager* create_creds() start_time, end_time); - return new Credentials_Manager_Test(server_cert, ca_cert, server_key); + Credentials_Manager_Test* cmt (new Credentials_Manager_Test(server_cert, ca_cert, server_key)); + cmt->m_provides_client_certs = client_type; + return cmt; } std::function<void (const byte[], size_t)> queue_inserter(std::vector<byte>& q) @@ -782,7 +786,8 @@ class TLS_Unit_Tests : public Test public: std::vector<Test::Result> run() override { - std::unique_ptr<Botan::Credentials_Manager> basic_creds(create_creds()); + std::unique_ptr<Botan::Credentials_Manager> basic_creds(create_creds(false)); + std::unique_ptr<Botan::Credentials_Manager> basic_creds_with_client_cert(create_creds(true)); std::vector<Test::Result> results; Test_Policy policy; @@ -815,6 +820,7 @@ class TLS_Unit_Tests : public Test results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V12, *basic_creds, policy)); results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V10, *basic_creds, policy)); results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V12, *basic_creds, policy)); + results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V12, *basic_creds_with_client_cert, policy)); #if defined(BOTAN_HAS_AEAD_OCB) policy.set("ciphers", "AES-128/OCB(12)"); diff --git a/src/tests/unit_tls_policy.cpp b/src/tests/unit_tls_policy.cpp new file mode 100644 index 000000000..2e5b63f41 --- /dev/null +++ b/src/tests/unit_tls_policy.cpp @@ -0,0 +1,162 @@ +/* +* TLS Policy tests +* +* (C) 2016 Juraj Somorovsky +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_TLS) + #include <botan/pubkey.h> + #include <botan/auto_rng.h> + #include <botan/oids.h> + #include <botan/tls_policy.h> +#endif + +#if defined(BOTAN_HAS_RSA) + #include <botan/rsa.h> +#endif + +#if defined(BOTAN_HAS_ECDH) + #include <botan/ecdh.h> +#endif + +#if defined(BOTAN_HAS_ECDSA) + #include <botan/ecdsa.h> +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + #include <botan/dh.h> +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_TLS) +class TLS_Policy_Unit_Tests : public Test + { + public: + std::vector<Test::Result> run() override + { + std::vector<Test::Result> results; + + results.push_back(test_peer_key_acceptable_rsa()); + results.push_back(test_peer_key_acceptable_ecdh()); + results.push_back(test_peer_key_acceptable_ecdsa()); + results.push_back(test_peer_key_acceptable_dh()); + + return results; + } + private: + Test::Result test_peer_key_acceptable_rsa() + { + Test::Result result("TLS Policy RSA key verification"); +#if defined(BOTAN_HAS_RSA) + std::unique_ptr<Botan::Private_Key> rsa_key_1024 (new Botan::RSA_PrivateKey(Test::rng(), 1024)); + Botan::TLS::Policy policy; + + try + { + policy.check_peer_key_acceptable(*rsa_key_1024); + result.test_failure("Incorrectly accepting 1024 bit RSA keys"); + } + catch(std::exception& e) + { + result.test_success("Correctly rejecting 1024 bit RSA keys"); + } + + std::unique_ptr<Botan::Private_Key> rsa_key_2048 (new Botan::RSA_PrivateKey(Test::rng(), 2048)); + policy.check_peer_key_acceptable(*rsa_key_2048); + result.test_success("Correctly accepting 2048 bit RSA keys"); +#endif + return result; + } + + Test::Result test_peer_key_acceptable_ecdh() + { + Test::Result result("TLS Policy ECDH key verification"); +#if defined(BOTAN_HAS_ECDH) + Botan::EC_Group group_192("secp192r1"); + std::unique_ptr<Botan::Private_Key> ecdh_192 (new Botan::ECDH_PrivateKey(Test::rng(), group_192)); + + Botan::TLS::Policy policy; + try + { + policy.check_peer_key_acceptable(*ecdh_192); + result.test_failure("Incorrectly accepting 192 bit EC keys"); + } + catch(std::exception& e) + { + result.test_success("Correctly rejecting 192 bit EC keys"); + } + + Botan::EC_Group group_256("secp256r1"); + std::unique_ptr<Botan::Private_Key> ecdh_256 (new Botan::ECDH_PrivateKey(Test::rng(), group_256)); + policy.check_peer_key_acceptable(*ecdh_256); + result.test_success("Correctly accepting 256 bit EC keys"); +#endif + return result; + } + + Test::Result test_peer_key_acceptable_ecdsa() + { + Test::Result result("TLS Policy ECDSA key verification"); +#if defined(BOTAN_HAS_ECDSA) + Botan::EC_Group group_192("secp192r1"); + std::unique_ptr<Botan::Private_Key> ecdsa_192 (new Botan::ECDSA_PrivateKey(Test::rng(), group_192)); + + Botan::TLS::Policy policy; + try + { + policy.check_peer_key_acceptable(*ecdsa_192); + result.test_failure("Incorrectly accepting 192 bit EC keys"); + } + catch(std::exception& e) + { + result.test_success("Correctly rejecting 192 bit EC keys"); + } + + Botan::EC_Group group_256("secp256r1"); + std::unique_ptr<Botan::Private_Key> ecdsa_256 (new Botan::ECDSA_PrivateKey(Test::rng(), group_256)); + policy.check_peer_key_acceptable(*ecdsa_256); + result.test_success("Correctly accepting 256 bit EC keys"); +#endif + return result; + } + + Test::Result test_peer_key_acceptable_dh() + { + Test::Result result("TLS Policy DH key verification"); +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + const BigInt g("2"); + const BigInt p("58458002095536094658683755258523362961421200751439456159756164191494576279467"); + const Botan::DL_Group grp(p, g); + const Botan::BigInt x("46205663093589612668746163860870963912226379131190812163519349848291472898748"); + std::unique_ptr<Botan::Private_Key> dhkey (new Botan::DH_PrivateKey(Test::rng(), grp, x)); + + Botan::TLS::Policy policy; + try + { + policy.check_peer_key_acceptable(*dhkey); + result.test_failure("Incorrectly accepting short bit DH keys"); + } + catch(std::exception& e) + { + result.test_success("Correctly rejecting short bit DH keys"); + } +#endif + return result; + } + + }; + +BOTAN_REGISTER_TEST("tls_policy", TLS_Policy_Unit_Tests); + +#endif + +} + +} |