diff options
-rw-r--r-- | src/lib/filters/threaded_fork.cpp | 5 | ||||
-rw-r--r-- | src/lib/hash/hash.cpp | 2 | ||||
-rw-r--r-- | src/lib/rng/hmac_drbg/hmac_drbg.cpp | 54 | ||||
-rw-r--r-- | src/lib/rng/hmac_drbg/hmac_drbg.h | 52 | ||||
-rw-r--r-- | src/lib/tls/msg_client_hello.cpp | 18 | ||||
-rw-r--r-- | src/lib/tls/tls_client.cpp | 29 | ||||
-rw-r--r-- | src/tests/test_block.cpp | 2 | ||||
-rw-r--r-- | src/tests/test_filters.cpp | 183 | ||||
-rw-r--r-- | src/tests/test_rng.cpp | 61 | ||||
-rw-r--r-- | src/tests/test_stream.cpp | 15 | ||||
-rw-r--r-- | src/tests/unit_tls.cpp | 261 |
11 files changed, 523 insertions, 159 deletions
diff --git a/src/lib/filters/threaded_fork.cpp b/src/lib/filters/threaded_fork.cpp index f558ac9c2..ff54bcbc6 100644 --- a/src/lib/filters/threaded_fork.cpp +++ b/src/lib/filters/threaded_fork.cpp @@ -136,6 +136,11 @@ void Threaded_Fork::thread_entry(Filter* filter) { while(true) { + /* + * This is plain wrong: a single thread can get the semaphore + * more than one time, meaning it will process the input twice + * and some other thread/filter will not see this input. + */ m_thread_data->m_input_ready_semaphore.acquire(); if(!m_thread_data->m_input) diff --git a/src/lib/hash/hash.cpp b/src/lib/hash/hash.cpp index 7a32ccede..ede2f8c99 100644 --- a/src/lib/hash/hash.cpp +++ b/src/lib/hash/hash.cpp @@ -269,7 +269,7 @@ std::unique_ptr<HashFunction> HashFunction::create(const std::string& algo_spec, #endif #if defined(BOTAN_HAS_COMB4P) - if(req.algo_name() == "Comb4p" && req.arg_count() == 2) + if(req.algo_name() == "Comb4P" && req.arg_count() == 2) { std::unique_ptr<HashFunction> h1(HashFunction::create(req.arg(0))); std::unique_ptr<HashFunction> h2(HashFunction::create(req.arg(1))); diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/src/lib/rng/hmac_drbg/hmac_drbg.cpp index 6ea66aa2e..2e056e726 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.cpp +++ b/src/lib/rng/hmac_drbg/hmac_drbg.cpp @@ -12,38 +12,63 @@ namespace Botan { HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, RandomNumberGenerator& underlying_rng, - size_t reseed_interval) : + size_t reseed_interval, + size_t max_number_of_bytes_per_request) : Stateful_RNG(underlying_rng, reseed_interval), - m_mac(std::move(prf)) + m_mac(std::move(prf)), + m_max_number_of_bytes_per_request(max_number_of_bytes_per_request) { BOTAN_ASSERT_NONNULL(m_mac); + + if(m_max_number_of_bytes_per_request == 0 || m_max_number_of_bytes_per_request > 64 * 1024) + { + throw Invalid_Argument("Invalid value for max_number_of_bytes_per_request"); + } + clear(); } HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, RandomNumberGenerator& underlying_rng, Entropy_Sources& entropy_sources, - size_t reseed_interval) : + size_t reseed_interval, + size_t max_number_of_bytes_per_request ) : Stateful_RNG(underlying_rng, entropy_sources, reseed_interval), - m_mac(std::move(prf)) + m_mac(std::move(prf)), + m_max_number_of_bytes_per_request(max_number_of_bytes_per_request) { BOTAN_ASSERT_NONNULL(m_mac); + + if(m_max_number_of_bytes_per_request == 0 || m_max_number_of_bytes_per_request > 64 * 1024) + { + throw Invalid_Argument("Invalid value for max_number_of_bytes_per_request"); + } + clear(); } HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, Entropy_Sources& entropy_sources, - size_t reseed_interval) : + size_t reseed_interval, + size_t max_number_of_bytes_per_request) : Stateful_RNG(entropy_sources, reseed_interval), - m_mac(std::move(prf)) + m_mac(std::move(prf)), + m_max_number_of_bytes_per_request(max_number_of_bytes_per_request) { BOTAN_ASSERT_NONNULL(m_mac); + + if(m_max_number_of_bytes_per_request == 0 || m_max_number_of_bytes_per_request > 64 * 1024) + { + throw Invalid_Argument("Invalid value for max_number_of_bytes_per_request"); + } + clear(); } HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf) : Stateful_RNG(), - m_mac(std::move(prf)) + m_mac(std::move(prf)), + m_max_number_of_bytes_per_request(64*1024) { BOTAN_ASSERT_NONNULL(m_mac); clear(); @@ -76,22 +101,9 @@ void HMAC_DRBG::randomize(byte output[], size_t output_len) void HMAC_DRBG::randomize_with_input(byte output[], size_t output_len, const byte input[], size_t input_len) { - /** - * SP 800-90A requires we reject any request for a DRBG output - * longer than max_number_of_bits_per_request. This is an - * implementation-dependent value, but NIST requires for HMAC_DRBG - * that every implementation set a value no more than 2**19 bits - * (or 64 KiB). - * - * To avoid inconveniencing the caller who wants a large output for - * whatever reason, instead treat very long output requests as - * if multiple maximum-length requests had been made. - */ - const size_t max_number_of_bytes_per_request = 64*1024; - while(output_len > 0) { - size_t this_req = std::min(max_number_of_bytes_per_request, output_len); + size_t this_req = std::min(m_max_number_of_bytes_per_request, output_len); output_len -= this_req; reseed_check(); diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.h b/src/lib/rng/hmac_drbg/hmac_drbg.h index 11d355d70..9210cbca2 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.h +++ b/src/lib/rng/hmac_drbg/hmac_drbg.h @@ -24,7 +24,7 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG /** * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) * - * Automatic reseeding is disabled completely, as it as no access to + * Automatic reseeding is disabled completely, as it has no access to * any source for seed material. * * If a fork is detected, the RNG will be unable to reseed itself @@ -44,10 +44,22 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG * to perform the periodic reseeding * @param reseed_interval specifies a limit of how many times * the RNG will be called before automatic reseeding is performed + * @param max_number_of_bytes_per_request requests that are in size higher + * than max_number_of_bytes_per_request are treated as if multiple single + * requests of max_number_of_bytes_per_request size had been made. + * In theory SP 800-90A requires that we reject any request for a DRBG + * output longer than max_number_of_bytes_per_request. To avoid inconveniencing + * the caller who wants an output larger than max_number_of_bytes_per_request, + * instead treat these requests as if multiple requests of + * max_number_of_bytes_per_request size had been made. NIST requires for + * HMAC_DRBG that every implementation set a value no more than 2**19 bits + * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for + * example every 512 bit automatic reseeding occurs. */ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, RandomNumberGenerator& underlying_rng, - size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, + size_t max_number_of_bytes_per_request = 64 * 1024); /** * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) @@ -59,10 +71,22 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG * @param entropy_sources will be polled to perform reseeding periodically * @param reseed_interval specifies a limit of how many times * the RNG will be called before automatic reseeding is performed. + * @param max_number_of_bytes_per_request requests that are in size higher + * than max_number_of_bytes_per_request are treated as if multiple single + * requests of max_number_of_bytes_per_request size had been made. + * In theory SP 800-90A requires that we reject any request for a DRBG + * output longer than max_number_of_bytes_per_request. To avoid inconveniencing + * the caller who wants an output larger than max_number_of_bytes_per_request, + * instead treat these requests as if multiple requests of + * max_number_of_bytes_per_request size had been made. NIST requires for + * HMAC_DRBG that every implementation set a value no more than 2**19 bits + * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for + * example every 512 bit automatic reseeding occurs. */ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, Entropy_Sources& entropy_sources, - size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, + size_t max_number_of_bytes_per_request = 64 * 1024); /** * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) @@ -77,20 +101,35 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG * @param entropy_sources will be polled to perform reseeding periodically * @param reseed_interval specifies a limit of how many times * the RNG will be called before automatic reseeding is performed. + * @param max_number_of_bytes_per_request requests that are in size higher + * than max_number_of_bytes_per_request are treated as if multiple single + * requests of max_number_of_bytes_per_request size had been made. + * In theory SP 800-90A requires that we reject any request for a DRBG + * output longer than max_number_of_bytes_per_request. To avoid inconveniencing + * the caller who wants an output larger than max_number_of_bytes_per_request, + * instead treat these requests as if multiple requests of + * max_number_of_bytes_per_request size had been made. NIST requires for + * HMAC_DRBG that every implementation set a value no more than 2**19 bits + * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for + * example every 512 bit automatic reseeding occurs. */ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, RandomNumberGenerator& underlying_rng, Entropy_Sources& entropy_sources, - size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, + size_t max_number_of_bytes_per_request = 64 * 1024); /** * Constructor taking a string for the hash */ - HMAC_DRBG(const std::string& hmac_hash) : Stateful_RNG() + HMAC_DRBG(const std::string& hmac_hash) : + Stateful_RNG(), m_max_number_of_bytes_per_request(64 * 1024), + m_mac(MessageAuthenticationCode::create("HMAC(" + hmac_hash + ")")) { - m_mac = MessageAuthenticationCode::create("HMAC(" + hmac_hash + ")"); if(!m_mac) + { throw Algorithm_Not_Found(hmac_hash); + } clear(); } @@ -112,6 +151,7 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG std::unique_ptr<MessageAuthenticationCode> m_mac; secure_vector<byte> m_V; + const size_t m_max_number_of_bytes_per_request; }; } diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index 36335e7ce..50c83c10c 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -84,7 +84,7 @@ Client_Hello::Client_Hello(Handshake_IO& io, "Our policy accepts the version we are offering"); /* - * Place all empty extensions in front to avoid a bug in some sytems + * Place all empty extensions in front to avoid a bug in some systems * which reject hellos when the last extension in the list is empty. */ m_extensions.add(new Extended_Master_Secret); @@ -170,14 +170,7 @@ Client_Hello::Client_Hello(Handshake_IO& io, m_extensions.add(new Supported_Point_Formats()); } - if(m_version.supports_negotiable_signature_algorithms()) - m_extensions.add(new Signature_Algorithms(policy.allowed_signature_hashes(), - policy.allowed_signature_methods())); - - if(reneg_info.empty() && !next_protocols.empty()) - m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); - - if(policy.negotiate_encrypt_then_mac()) + if(session.supports_encrypt_then_mac()) m_extensions.add(new Encrypt_then_MAC); #if defined(BOTAN_HAS_SRP6) @@ -189,6 +182,13 @@ Client_Hello::Client_Hello(Handshake_IO& io, } #endif + if(m_version.supports_negotiable_signature_algorithms()) + m_extensions.add(new Signature_Algorithms(policy.allowed_signature_hashes(), + policy.allowed_signature_methods())); + + if(reneg_info.empty() && !next_protocols.empty()) + m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); + hash.update(io.send(*this)); } diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp index 0e72b9a28..183886c66 100644 --- a/src/lib/tls/tls_client.cpp +++ b/src/lib/tls/tls_client.cpp @@ -149,18 +149,25 @@ void Client::send_client_hello(Handshake_State& state_base, Session session_info; if(session_manager().load_from_server_info(m_info, session_info)) { - if(srp_identifier == "" || session_info.srp_identifier() == srp_identifier) + /* + Ensure that the session protocol type matches what we want to use + If not skip the resume and establish a new session + */ + if(version == session_info.version()) { - state.client_hello(new Client_Hello( - state.handshake_io(), - state.hash(), - policy(), - rng(), - secure_renegotiation_data_for_client_hello(), - session_info, - next_protocols)); - - state.resume_master_secret = session_info.master_secret(); + if(srp_identifier == "" || session_info.srp_identifier() == srp_identifier) + { + state.client_hello( + new Client_Hello(state.handshake_io(), + state.hash(), + policy(), + rng(), + secure_renegotiation_data_for_client_hello(), + session_info, + next_protocols)); + + state.resume_master_secret = session_info.master_secret(); + } } } } diff --git a/src/tests/test_block.cpp b/src/tests/test_block.cpp index acbe6702b..82ab0618d 100644 --- a/src/tests/test_block.cpp +++ b/src/tests/test_block.cpp @@ -64,6 +64,8 @@ class Block_Cipher_Tests : public Text_Based_Test buf = expected; cipher->decrypt(buf); + cipher->clear(); + result.test_eq(provider, "decrypt", buf, input); } diff --git a/src/tests/test_filters.cpp b/src/tests/test_filters.cpp index b6bbf05c3..392fd98ef 100644 --- a/src/tests/test_filters.cpp +++ b/src/tests/test_filters.cpp @@ -1,5 +1,6 @@ /* * (C) 2016 Daniel Neus +* 2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -7,7 +8,9 @@ #include "tests.h" #if defined(BOTAN_HAS_FILTERS) - #include <botan/secqueue.h> + #include <botan/secqueue.h> + #include <botan/pipe.h> + #include <botan/filters.h> #endif namespace Botan_Tests { @@ -20,43 +23,191 @@ class Filter_Tests : public Test std::vector<Test::Result> run() override { std::vector<Test::Result> results; - Test::Result secqueue_result("SecureQueue"); + + results.push_back(test_secqueue()); + results.push_back(test_pipe_hash()); + results.push_back(test_pipe_stream()); + results.push_back(test_pipe_codec()); + results.push_back(test_fork()); + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) && 0 + // Threaded_Fork is broken + results.push_back(test_threaded_fork()); +#endif + + return results; + } + + private: + Test::Result test_secqueue() + { + Test::Result result("SecureQueue"); try { - using Botan::SecureQueue; - SecureQueue queue_a; + Botan::SecureQueue queue_a; std::vector<uint8_t> test_data = {0x24, 0xB2, 0xBF, 0xC2, 0xE6, 0xD4, 0x7E, 0x04, 0x67, 0xB3}; queue_a.write(test_data.data(), test_data.size()); - secqueue_result.test_eq("size of SecureQueue is correct", queue_a.size(), test_data.size()); - secqueue_result.test_eq("0 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 0); + result.test_eq("size of SecureQueue is correct", queue_a.size(), test_data.size()); + result.test_eq("0 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 0); uint8_t b; size_t bytes_read = queue_a.read_byte(b); - secqueue_result.test_eq("1 byte read", bytes_read, 1); + result.test_eq("1 byte read", bytes_read, 1); Botan::secure_vector<uint8_t> produced(b); Botan::secure_vector<uint8_t> expected(test_data.at(0)); - secqueue_result.test_eq("byte read is correct", produced, expected); + result.test_eq("byte read is correct", produced, expected); - secqueue_result.test_eq("1 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 1); + result.test_eq("1 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 1); - SecureQueue queue_b; + Botan::SecureQueue queue_b; queue_a = queue_b; - secqueue_result.test_eq("bytes_read is set correctly", queue_a.get_bytes_read(), 0); + result.test_eq("bytes_read is set correctly", queue_a.get_bytes_read(), 0); } catch (std::exception& e) { - secqueue_result.test_failure("SecureQueue", e.what()); + result.test_failure("SecureQueue", e.what()); } - results.push_back(secqueue_result); - return results; - } + return result; + } + + Test::Result test_pipe_hash() + { + Test::Result result("Pipe"); + Botan::Pipe pipe(new Botan::Hash_Filter("SHA-224")); + pipe.pop(); + pipe.append(new Botan::Hash_Filter("SHA-256")); + + result.test_eq("Message count", pipe.message_count(), 0); + + pipe.start_msg(); + uint8_t inb = 0x41; + pipe.write(&inb, 1); + pipe.write(std::vector<uint8_t>(6, 0x41)); + pipe.write(inb); + pipe.end_msg(); + + result.test_eq("Message count", pipe.message_count(), 1); + result.test_eq("Message size", pipe.remaining(), 32); + + std::vector<uint8_t> out(32); + result.test_eq("Expected read count", pipe.read(&out[0], 5), 5); + result.test_eq("Expected read count", pipe.read(&out[5], 17), 17); + result.test_eq("Expected read count", pipe.read(&out[22], 12), 10); + result.test_eq("Expected read count", pipe.read(&out[0], 1), 0); // no more output + + result.test_eq("Expected output", out, "C34AB6ABB7B2BB595BC25C3B388C872FD1D575819A8F55CC689510285E212385"); + + return result; + } + + Test::Result test_pipe_codec() + { + Test::Result result("Pipe"); + + Botan::Pipe pipe(new Botan::Base64_Encoder); + + result.test_eq("Message count", pipe.message_count(), 0); + + pipe.process_msg("ABCDX"); + + result.test_eq("Message count", pipe.message_count(), 1); + result.test_eq("Message size", pipe.remaining(), 8); + + std::string output = pipe.read_all_as_string(0); + result.test_eq("Message size", pipe.remaining(0), 0); + result.test_eq("Output round tripped", output, "QUJDRFg="); + + pipe.append(new Botan::Base64_Decoder); + pipe.process_msg("FOOBAZ"); + + result.test_eq("base64 roundtrip", pipe.read_all_as_string(1), "FOOBAZ"); + + pipe.pop(); + pipe.pop(); + + // Pipe is empty of filters, should still pass through + pipe.process_msg("surprise plaintext"); + + pipe.set_default_msg(2); + result.test_eq("Message 2", pipe.read_all_as_string(), "surprise plaintext"); + + pipe.append(new Botan::Hex_Decoder); + + pipe.process_msg("F331F00D"); + Botan::secure_vector<uint8_t> bin = pipe.read_all(3); + result.test_eq("hex decoded", bin, "F331F00D"); + + pipe.append(new Botan::Hex_Encoder); + pipe.process_msg("F331F00D"); + result.test_eq("hex roundtrip", pipe.read_all_as_string(4), "F331F00D"); + + return result; + } + + Test::Result test_pipe_stream() + { + Test::Result result("Pipe"); + + Botan::Keyed_Filter* aes = nullptr; + const Botan::SymmetricKey key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + const Botan::InitializationVector iv("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + Botan::Pipe pipe(aes = new Botan::StreamCipher_Filter("CTR-BE(AES-128)", key)); + + aes->set_iv(iv); + + pipe.process_msg("ABCDEF"); + + result.test_eq("Message count", pipe.message_count(), 1); + result.test_eq("Ciphertext", pipe.read_all(), "FDFD6238F7C6"); + return result; + } + + Test::Result test_fork() + { + Test::Result result("Fork"); + + Botan::Pipe pipe(new Botan::Fork(new Botan::Hash_Filter("SHA-256"), + new Botan::Hash_Filter("SHA-512-256"))); + + result.test_eq("Message count", pipe.message_count(), 0); + pipe.process_msg("OMG"); + result.test_eq("Message count", pipe.message_count(), 2); + + // Test reading out of order + result.test_eq("Hash 2", pipe.read_all(1), "610480FFA82F24F6926544B976FE387878E3D973C03DFD591C2E9896EFB903E0"); + result.test_eq("Hash 1", pipe.read_all(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF"); + + return result; + + } + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) + Test::Result test_threaded_fork() + { + Test::Result result("Threaded_Fork"); + + Botan::Pipe pipe(new Botan::Threaded_Fork(new Botan::Hex_Encoder, + new Botan::Base64_Encoder)); + + result.test_eq("Message count", pipe.message_count(), 0); + pipe.process_msg("woo"); + result.test_eq("Message count", pipe.message_count(), 2); + + // Test reading out of order + result.test_eq("Hash 2", pipe.read_all_as_string(1), "d29v"); + result.test_eq("Hash 1", pipe.read_all_as_string(0), "776F6F"); + + return result; + } +#endif + }; - BOTAN_REGISTER_TEST("filter", Filter_Tests); +BOTAN_REGISTER_TEST("filter", Filter_Tests); #endif diff --git a/src/tests/test_rng.cpp b/src/tests/test_rng.cpp index ff5d0b62e..7f318426c 100644 --- a/src/tests/test_rng.cpp +++ b/src/tests/test_rng.cpp @@ -144,7 +144,10 @@ class HMAC_DRBG_Unit_Tests : public Test bool is_seeded() const override { return true; } - void clear() override {} + void clear() override + { + m_randomize_count = 0; + } void randomize(byte[], size_t) override { @@ -239,6 +242,61 @@ class HMAC_DRBG_Unit_Tests : public Test return result; } + Test::Result test_max_number_of_bytes_per_request() + { + Test::Result result("HMAC_DRBG max_number_of_bytes_per_request"); + + std::string mac_string = "HMAC(SHA-256)"; + auto mac = Botan::MessageAuthenticationCode::create(mac_string); + if(!mac) + { + result.note_missing(mac_string); + return result; + } + + Request_Counting_RNG counting_rng; + + result.test_throws("HMAC_DRBG does not accept 0 for max_number_of_bytes_per_request", [&mac_string, &counting_rng ]() + { + Botan::HMAC_DRBG rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 0); + }); + + result.test_throws("HMAC_DRBG does not accept values higher than 64KB for max_number_of_bytes_per_request", [ &mac_string, + &counting_rng ]() + { + Botan::HMAC_DRBG rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 64 * 1024 + 1); + }); + + // set reseed_interval to 1 so we can test that a long request is split + // into multiple, max_number_of_bytes_per_request long requests + // for each smaller request, reseed_check() calls counting_rng::randomize(), + // which we can compare with + Botan::HMAC_DRBG rng(std::move(mac), counting_rng, 1, 64); + + rng.random_vec(63); + result.test_eq("one request", counting_rng.randomize_count(), 1); + + rng.clear(); + counting_rng.clear(); + + rng.random_vec(64); + result.test_eq("one request", counting_rng.randomize_count(), 1); + + rng.clear(); + counting_rng.clear(); + + rng.random_vec(65); + result.test_eq("two requests", counting_rng.randomize_count(), 2); + + rng.clear(); + counting_rng.clear(); + + rng.random_vec(1025); + result.test_eq("17 requests", counting_rng.randomize_count(), 17); + + return result; + } + Test::Result test_broken_entropy_input() { Test::Result result("HMAC_DRBG Broken Entropy Input"); @@ -507,6 +565,7 @@ class HMAC_DRBG_Unit_Tests : public Test std::vector<Test::Result> results; results.push_back(test_reseed_kat()); results.push_back(test_reseed()); + results.push_back(test_max_number_of_bytes_per_request()); results.push_back(test_broken_entropy_input()); results.push_back(test_check_nonce()); results.push_back(test_prediction_resistance()); diff --git a/src/tests/test_stream.cpp b/src/tests/test_stream.cpp index 6097fd3e8..d53777593 100644 --- a/src/tests/test_stream.cpp +++ b/src/tests/test_stream.cpp @@ -1,5 +1,5 @@ /* -* (C) 2014,2015 Jack Lloyd +* (C) 2014,2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -57,7 +57,18 @@ class Stream_Cipher_Tests : public Text_Based_Test cipher->set_key(key); if(nonce.size()) + { cipher->set_iv(nonce.data(), nonce.size()); + } + else + { + /* + * If no nonce was set then implicitly the cipher is using a + * null/empty nonce. Call set_iv with such a nonce to make sure + * set_iv accepts it. + */ + cipher->set_iv(nullptr, 0); + } if (seek != 0) cipher->seek(seek); @@ -65,6 +76,8 @@ class Stream_Cipher_Tests : public Text_Based_Test std::vector<uint8_t> buf = input; cipher->encrypt(buf); + cipher->clear(); + result.test_eq(provider, "encrypt", buf, expected); } diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index ec082027b..8a074db0c 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -12,16 +12,23 @@ #if defined(BOTAN_HAS_TLS) -#include <botan/tls_server.h> #include <botan/tls_client.h> -#include <botan/tls_handshake_msg.h> +#include <botan/tls_server.h> + +#include <botan/ec_group.h> +#include <botan/hex.h> #include <botan/pkcs10.h> -#include <botan/x509self.h> #include <botan/rsa.h> +#include <botan/ecdsa.h> +#include <botan/tls_handshake_msg.h> #include <botan/x509_ca.h> -#include <botan/hex.h> +#include <botan/x509self.h> + +#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) + #include <botan/tls_session_manager_sqlite.h> #endif +#endif namespace Botan_Tests { @@ -31,14 +38,22 @@ namespace { class Credentials_Manager_Test : public Botan::Credentials_Manager { public: - Credentials_Manager_Test(const Botan::X509_Certificate& server_cert, - const Botan::X509_Certificate& ca_cert, - Botan::Private_Key* server_key) : - m_server_cert(server_cert), - m_ca_cert(ca_cert), - m_key(server_key) + Credentials_Manager_Test(const Botan::X509_Certificate& rsa_cert, + const Botan::X509_Certificate& rsa_ca, + const Botan::X509_Certificate& ecdsa_cert, + const Botan::X509_Certificate& ecdsa_ca, + Botan::Private_Key* rsa_key, + Botan::Private_Key* ecdsa_key) : + m_rsa_cert(rsa_cert), + m_rsa_ca(rsa_ca), + m_ecdsa_cert(ecdsa_cert), + m_ecdsa_ca(ecdsa_ca), + m_rsa_key(rsa_key), + m_ecdsa_key(ecdsa_key) { - std::unique_ptr<Botan::Certificate_Store> store(new Botan::Certificate_Store_In_Memory(m_ca_cert)); + std::unique_ptr<Botan::Certificate_Store_In_Memory> store(new Botan::Certificate_Store_In_Memory); + store->add_certificate(m_rsa_ca); + store->add_certificate(m_ecdsa_ca); m_stores.push_back(std::move(store)); m_provides_client_certs = false; } @@ -62,15 +77,20 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager 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) - if(cert_key_types[i] == m_key->algo_name()) - have_match = true; - - if(have_match) + for(auto&& key_type : cert_key_types) { - chain.push_back(m_server_cert); - chain.push_back(m_ca_cert); + if(key_type == "RSA") + { + chain.push_back(m_rsa_cert); + chain.push_back(m_rsa_ca); + break; + } + else if(key_type == "ECDSA") + { + chain.push_back(m_ecdsa_cert); + chain.push_back(m_ecdsa_ca); + break; + } } } @@ -87,11 +107,15 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager cert_chain); } - Botan::Private_Key* private_key_for(const Botan::X509_Certificate&, + Botan::Private_Key* private_key_for(const Botan::X509_Certificate& crt, const std::string&, const std::string&) override { - return m_key.get(); + if(crt == m_rsa_cert) + return m_rsa_key.get(); + if(crt == m_ecdsa_cert) + return m_ecdsa_key.get(); + return nullptr; } Botan::SymmetricKey psk(const std::string& type, @@ -111,52 +135,59 @@ class Credentials_Manager_Test : public Botan::Credentials_Manager } public: - Botan::X509_Certificate m_server_cert, m_ca_cert; - std::unique_ptr<Botan::Private_Key> m_key; + Botan::X509_Certificate m_rsa_cert, m_rsa_ca, m_ecdsa_cert, m_ecdsa_ca; + std::unique_ptr<Botan::Private_Key> m_rsa_key, m_ecdsa_key; std::vector<std::unique_ptr<Botan::Certificate_Store>> m_stores; bool m_provides_client_certs; }; -Botan::Credentials_Manager* create_creds(Botan::RandomNumberGenerator& rng, - bool with_client_certs = false) +Botan::Credentials_Manager* +create_creds(Botan::RandomNumberGenerator& rng, + bool with_client_certs = false) { - std::unique_ptr<Botan::Private_Key> ca_key(new Botan::RSA_PrivateKey(rng, 1024)); + const Botan::EC_Group ecdsa_params("secp256r1"); + const size_t rsa_params = 1024; - Botan::X509_Cert_Options ca_opts; - ca_opts.common_name = "Test CA"; - ca_opts.country = "US"; - ca_opts.CA_key(1); + std::unique_ptr<Botan::Private_Key> rsa_ca_key(new Botan::RSA_PrivateKey(rng, rsa_params)); + std::unique_ptr<Botan::Private_Key> rsa_srv_key(new Botan::RSA_PrivateKey(rng, rsa_params)); - Botan::X509_Certificate ca_cert = - Botan::X509::create_self_signed_cert(ca_opts, - *ca_key, - "SHA-256", - rng); + std::unique_ptr<Botan::Private_Key> ecdsa_ca_key(new Botan::ECDSA_PrivateKey(rng, ecdsa_params)); + std::unique_ptr<Botan::Private_Key> ecdsa_srv_key(new Botan::ECDSA_PrivateKey(rng, ecdsa_params)); - Botan::Private_Key* server_key = new Botan::RSA_PrivateKey(rng, 1024); + Botan::X509_Cert_Options ca_opts("Test CA/VT"); + ca_opts.CA_key(1); - Botan::X509_Cert_Options server_opts; - server_opts.common_name = "server.example.com"; - server_opts.country = "US"; + const Botan::X509_Certificate rsa_ca_cert = + Botan::X509::create_self_signed_cert(ca_opts, *rsa_ca_key, "SHA-256", rng); + const Botan::X509_Certificate ecdsa_ca_cert = + Botan::X509::create_self_signed_cert(ca_opts, *ecdsa_ca_key, "SHA-256", rng); - Botan::PKCS10_Request req = Botan::X509::create_cert_req(server_opts, - *server_key, - "SHA-256", - rng); + const Botan::X509_Cert_Options server_opts("server.example.com"); - Botan::X509_CA ca(ca_cert, *ca_key, "SHA-256", Test::rng()); + const Botan::PKCS10_Request rsa_req = + Botan::X509::create_cert_req(server_opts, *rsa_srv_key, "SHA-256", rng); + const Botan::PKCS10_Request ecdsa_req = + Botan::X509::create_cert_req(server_opts, *ecdsa_srv_key, "SHA-256", rng); + + Botan::X509_CA rsa_ca(rsa_ca_cert, *rsa_ca_key, "SHA-256", rng); + Botan::X509_CA ecdsa_ca(ecdsa_ca_cert, *ecdsa_ca_key, "SHA-256", rng); - auto now = std::chrono::system_clock::now(); - Botan::X509_Time start_time(now); typedef std::chrono::duration<int, std::ratio<31556926>> years; - Botan::X509_Time end_time(now + years(1)); + auto now = std::chrono::system_clock::now(); + + const Botan::X509_Time start_time(now); + const Botan::X509_Time end_time(now + years(1)); + + const Botan::X509_Certificate rsa_srv_cert = + rsa_ca.sign_request(rsa_req, rng, start_time, end_time); + const Botan::X509_Certificate ecdsa_srv_cert = + ecdsa_ca.sign_request(ecdsa_req, rng, start_time, end_time); - Botan::X509_Certificate server_cert = ca.sign_request(req, - rng, - start_time, - end_time); + Credentials_Manager_Test* cmt = new Credentials_Manager_Test( + rsa_srv_cert, rsa_ca_cert, + ecdsa_srv_cert, ecdsa_ca_cert, + rsa_srv_key.release(), ecdsa_srv_key.release()); - Credentials_Manager_Test* cmt (new Credentials_Manager_Test(server_cert, ca_cert, server_key)); cmt->m_provides_client_certs = with_client_certs; return cmt; } @@ -178,11 +209,10 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, Botan::Credentials_Manager& creds, const Botan::TLS::Policy& client_policy, const Botan::TLS::Policy& server_policy, - Botan::RandomNumberGenerator& rng) + Botan::RandomNumberGenerator& rng, + Botan::TLS::Session_Manager& client_sessions, + Botan::TLS::Session_Manager& server_sessions) { - Botan::TLS::Session_Manager_In_Memory server_sessions(rng); - Botan::TLS::Session_Manager_In_Memory client_sessions(rng); - Test::Result result(offer_version.to_string()); result.start_timer(); @@ -338,6 +368,7 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, client->send(&client_sent[sent_so_far], sending); sent_so_far += sending; } + client->send_warning_alert(Botan::TLS::Alert::NO_RENEGOTIATION); } if(server->is_active() && server_sent.empty()) @@ -357,6 +388,8 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, server->send(&server_sent[sent_so_far], sending); sent_so_far += sending; } + + server->send_warning_alert(Botan::TLS::Alert::NO_RENEGOTIATION); } const bool corrupt_client_data = (r == 3); @@ -484,22 +517,24 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, Botan::Credentials_Manager& creds, const Botan::TLS::Policy& policy, - Botan::RandomNumberGenerator& rng) + Botan::RandomNumberGenerator& rng, + Botan::TLS::Session_Manager& client_sessions, + Botan::TLS::Session_Manager& server_sessions) { - return test_tls_handshake(offer_version, creds, policy, policy, rng); + return test_tls_handshake(offer_version, creds, policy, policy, rng, + client_sessions, server_sessions); } Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, Botan::Credentials_Manager& creds, const Botan::TLS::Policy& client_policy, const Botan::TLS::Policy& server_policy, - Botan::RandomNumberGenerator& rng) + Botan::RandomNumberGenerator& rng, + Botan::TLS::Session_Manager& client_sessions, + Botan::TLS::Session_Manager& server_sessions) { BOTAN_ASSERT(offer_version.is_datagram_protocol(), "Test is for datagram version"); - Botan::TLS::Session_Manager_In_Memory server_sessions(rng); - Botan::TLS::Session_Manager_In_Memory client_sessions(rng); - Test::Result result(offer_version.to_string()); result.start_timer(); @@ -785,9 +820,11 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, Botan::Credentials_Manager& creds, const Botan::TLS::Policy& policy, - Botan::RandomNumberGenerator& rng) + Botan::RandomNumberGenerator& rng, + Botan::TLS::Session_Manager& client_ses, + Botan::TLS::Session_Manager& server_ses) { - return test_dtls_handshake(offer_version, creds, policy, policy, rng); + return test_dtls_handshake(offer_version, creds, policy, policy, rng, client_ses, server_ses); } class Test_Policy : public Botan::TLS::Text_Policy @@ -809,8 +846,10 @@ class TLS_Unit_Tests : public Test { private: void test_with_policy(std::vector<Test::Result>& results, - const std::vector<Botan::TLS::Protocol_Version>& versions, + Botan::TLS::Session_Manager& client_ses, + Botan::TLS::Session_Manager& server_ses, Botan::Credentials_Manager& creds, + const std::vector<Botan::TLS::Protocol_Version>& versions, const Botan::TLS::Policy& policy) { Botan::RandomNumberGenerator& rng = Test::rng(); @@ -818,13 +857,15 @@ class TLS_Unit_Tests : public Test for(auto&& version : versions) { if(version.is_datagram_protocol()) - results.push_back(test_dtls_handshake(version, creds, policy, rng)); + results.push_back(test_dtls_handshake(version, creds, policy, rng, client_ses, server_ses)); else - results.push_back(test_tls_handshake(version, creds, policy, rng)); + results.push_back(test_tls_handshake(version, creds, policy, rng, client_ses, server_ses)); } } void test_all_versions(std::vector<Test::Result>& results, + Botan::TLS::Session_Manager& client_ses, + Botan::TLS::Session_Manager& server_ses, Botan::Credentials_Manager& creds, const std::string& kex_policy, const std::string& cipher_policy, @@ -845,10 +886,12 @@ class TLS_Unit_Tests : public Test Botan::TLS::Protocol_Version::DTLS_V12 }; - return test_with_policy(results, versions, creds, policy); + return test_with_policy(results, client_ses, server_ses, creds, versions, policy); } void test_modern_versions(std::vector<Test::Result>& results, + Botan::TLS::Session_Manager& client_ses, + Botan::TLS::Session_Manager& server_ses, Botan::Credentials_Manager& creds, const std::string& kex_policy, const std::string& cipher_policy, @@ -868,7 +911,7 @@ class TLS_Unit_Tests : public Test Botan::TLS::Protocol_Version::DTLS_V12 }; - return test_with_policy(results, versions, creds, policy); + return test_with_policy(results, client_ses, server_ses, creds, versions, policy); } public: @@ -876,70 +919,102 @@ class TLS_Unit_Tests : public Test { Botan::RandomNumberGenerator& rng = Test::rng(); + std::unique_ptr<Botan::TLS::Session_Manager> client_ses; + std::unique_ptr<Botan::TLS::Session_Manager> server_ses; + +#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) + client_ses.reset( + new Botan::TLS::Session_Manager_SQLite("pass", rng, ":memory:", 5, + std::chrono::seconds(2))); + server_ses.reset( + new Botan::TLS::Session_Manager_SQLite("pass", rng, ":memory:", 10, + std::chrono::seconds(4))); +#else + client_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng)); + server_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng)); +#endif + std::unique_ptr<Botan::Credentials_Manager> creds(create_creds(rng)); std::vector<Test::Result> results; #if defined(BOTAN_HAS_TLS_CBC) - for(std::string etm_setting : { "true", "false" }) + for(std::string etm_setting : { "false", "true" }) { - test_all_versions(results, *creds, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting); - test_all_versions(results, *creds, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting); + test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting); + test_all_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting); - test_all_versions(results, *creds, "RSA", "AES-256", "SHA-1", etm_setting); - test_all_versions(results, *creds, "ECDH", "AES-256", "SHA-1", etm_setting); + test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "AES-256", "SHA-1", etm_setting); + test_all_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-256", "SHA-1", etm_setting); #if defined(BOTAN_HAS_CAMELLIA) - test_all_versions(results, *creds, "RSA", "Camellia-128", "SHA-256", etm_setting); - test_all_versions(results, *creds, "ECDH", "Camellia-256", "SHA-256 SHA-384", etm_setting); + test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "Camellia-128", "SHA-256", etm_setting); + test_all_versions(results, *client_ses, *server_ses, *creds, "ECDH", "Camellia-256", "SHA-256 SHA-384", etm_setting); #endif #if defined(BOTAN_HAS_DES) - test_all_versions(results, *creds, "RSA", "3DES", "SHA-1", etm_setting); - test_all_versions(results, *creds, "ECDH", "3DES", "SHA-1", etm_setting); + test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "3DES", "SHA-1", etm_setting); + test_all_versions(results, *client_ses, *server_ses, *creds, "ECDH", "3DES", "SHA-1", etm_setting); #endif #if defined(BOTAN_HAS_SEED) - test_all_versions(results, *creds, "RSA", "SEED", "SHA-1", etm_setting); + test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "SEED", "SHA-1", etm_setting); #endif + + server_ses->remove_all(); } - test_modern_versions(results, *creds, "DH", "AES-128", "SHA-256"); + test_modern_versions(results, *client_ses, *server_ses, *creds, "DH", "AES-128", "SHA-256"); #endif - test_modern_versions(results, *creds, "RSA", "AES-128/GCM"); - test_modern_versions(results, *creds, "ECDH", "AES-128/GCM"); - test_modern_versions(results, *creds, "ECDH", "AES-128/GCM", "AEAD", + Botan::TLS::Strict_Policy strict_policy; + test_with_policy(results, *client_ses, *server_ses, *creds, + {Botan::TLS::Protocol_Version::TLS_V12}, strict_policy); + + test_modern_versions(results, *client_ses, *server_ses, *creds, "RSA", "AES-128/GCM"); + test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM"); + + client_ses->remove_all(); + + test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", + { { "signature_methods", "RSA" } }); + + test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", { { "use_ecc_point_compression", "true" } }); - test_modern_versions(results, *creds, "ECDH", "AES-128/GCM", "AEAD", + test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", { { "ecc_curves", "secp384r1" } }); #if defined(BOTAN_HAS_CURVE_25519) - test_modern_versions(results, *creds, "ECDH", "AES-128/GCM", "AEAD", + test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", { { "ecc_curves", "x25519" } }); #endif std::unique_ptr<Botan::Credentials_Manager> creds_with_client_cert(create_creds(rng, true)); - test_modern_versions(results, *creds_with_client_cert, "ECDH", "AES-256/GCM"); + test_modern_versions(results, *client_ses, *server_ses, *creds_with_client_cert, "ECDH", "AES-256/GCM"); + +#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) + client_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng)); + server_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng)); +#endif #if defined(BOTAN_HAS_AEAD_OCB) - test_modern_versions(results, *creds, "ECDH", "AES-128/OCB(12)"); + test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/OCB(12)"); #endif #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) - test_modern_versions(results, *creds, "ECDH", "ChaCha20Poly1305"); + test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "ChaCha20Poly1305"); #endif - test_modern_versions(results, *creds, "PSK", "AES-128/GCM"); + test_modern_versions(results, *client_ses, *server_ses, *creds, "PSK", "AES-128/GCM"); #if defined(BOTAN_HAS_CCM) - test_modern_versions(results, *creds, "PSK", "AES-128/CCM"); - test_modern_versions(results, *creds, "PSK", "AES-128/CCM(8)"); + test_modern_versions(results, *client_ses, *server_ses, *creds, "PSK", "AES-128/CCM"); + test_modern_versions(results, *client_ses, *server_ses, *creds, "PSK", "AES-128/CCM(8)"); #endif #if defined(BOTAN_HAS_TLS_CBC) // For whatever reason no (EC)DHE_PSK GCM ciphersuites are defined - test_modern_versions(results, *creds, "ECDHE_PSK", "AES-128", "SHA-256"); - test_modern_versions(results, *creds, "DHE_PSK", "AES-128", "SHA-1"); + test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDHE_PSK", "AES-128", "SHA-256"); + test_modern_versions(results, *client_ses, *server_ses, *creds, "DHE_PSK", "AES-128", "SHA-1"); #endif return results; |