Z-Botan-0.1.1.0: third_party/botan/src/tests/test_tls.cpp
/*
* (C) 2014,2015,2017,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
#include "tests.h"
#include <fstream>
#include <memory>
#if defined(BOTAN_HAS_TLS)
#include "test_rng.h"
#include <botan/tls_alert.h>
#include <botan/tls_policy.h>
#include <botan/tls_session.h>
#include <botan/tls_version.h>
#if defined(BOTAN_HAS_TLS_CBC)
#include <botan/internal/tls_cbc.h>
#endif
#endif
namespace Botan_Tests {
#if defined(BOTAN_HAS_TLS)
class TLS_Session_Tests final : public Test
{
public:
std::vector<Test::Result> run() override
{
Test::Result result("TLS::Session");
Botan::TLS::Session default_session;
Botan::secure_vector<uint8_t> default_der = default_session.DER_encode();
result.test_gte("Encoded default session has size", default_der.size(), 0);
Botan::TLS::Session decoded_default(default_der.data(), default_der.size());
Botan::TLS::Session session(std::vector<uint8_t>{0xAA, 0xBB},
Botan::secure_vector<uint8_t>{0xCC, 0xDD},
Botan::TLS::Protocol_Version::TLS_V12,
0xFE0F,
Botan::TLS::CLIENT,
true,
false,
std::vector<Botan::X509_Certificate>(),
std::vector<uint8_t>(),
Botan::TLS::Server_Information("server"),
"SRP username",
0x0000);
const std::string pem = session.PEM_encode();
Botan::TLS::Session session_from_pem(pem);
result.test_eq("Roundtrip from pem", session.DER_encode(), session_from_pem.DER_encode());
const Botan::SymmetricKey key("ABCDEF");
const std::vector<uint8_t> ctext1 = session.encrypt(key, Test::rng());
const std::vector<uint8_t> ctext2 = session.encrypt(key, Test::rng());
result.test_ne("TLS session encryption is non-determinsitic",
ctext1.data(), ctext1.size(),
ctext2.data(), ctext2.size());
const std::vector<uint8_t> expected_hdr = Botan::hex_decode("068B5A9D396C0000F2322CAE");
result.test_eq("tls", "TLS session encryption same header",
ctext1.data(), 12, expected_hdr.data(), 12);
result.test_eq("tls", "TLS session encryption same header",
ctext2.data(), 12, expected_hdr.data(), 12);
Botan::TLS::Session dsession = Botan::TLS::Session::decrypt(ctext1.data(), ctext1.size(), key);
result.test_eq("Decrypted session access works", dsession.srp_identifier(), "SRP username");
Fixed_Output_RNG frng1("00112233445566778899AABBCCDDEEFF802802802802802802802802");
const std::vector<uint8_t> ctextf1 = session.encrypt(key, frng1);
Fixed_Output_RNG frng2("00112233445566778899AABBCCDDEEFF802802802802802802802802");
const std::vector<uint8_t> ctextf2 = session.encrypt(key, frng2);
result.test_eq("Only randomness comes from RNG", ctextf1, ctextf2);
return {result};
}
};
BOTAN_REGISTER_TEST("tls_session", TLS_Session_Tests);
#if defined(BOTAN_HAS_TLS_CBC)
class TLS_CBC_Padding_Tests final : public Text_Based_Test
{
public:
TLS_CBC_Padding_Tests() : Text_Based_Test("tls_cbc_padding.vec", "Record,Output") {}
Test::Result run_one_test(const std::string&, const VarMap& vars) override
{
const std::vector<uint8_t> record = vars.get_req_bin("Record");
const size_t output = vars.get_req_sz("Output");
uint16_t res = Botan::TLS::check_tls_cbc_padding(record.data(), record.size());
Test::Result result("TLS CBC padding check");
result.test_eq("Expected", res, output);
return result;
}
};
BOTAN_REGISTER_TEST("tls_cbc_padding", TLS_CBC_Padding_Tests);
class TLS_CBC_Tests final : public Text_Based_Test
{
public:
class ZeroMac : public Botan::MessageAuthenticationCode
{
public:
ZeroMac(size_t mac_len) : m_mac_len(mac_len) {}
void clear() override {}
std::string name() const override { return "ZeroMac"; }
size_t output_length() const override { return m_mac_len; }
void add_data(const uint8_t[], size_t) override {}
void final_result(uint8_t out[]) override
{
for(size_t i = 0; i != m_mac_len; ++i)
out[i] = 0;
}
Botan::Key_Length_Specification key_spec() const override
{
return Botan::Key_Length_Specification(0, 0, 1);
}
virtual MessageAuthenticationCode* clone() const override { return new ZeroMac(m_mac_len); }
private:
void key_schedule(const uint8_t[], size_t) override {}
size_t m_mac_len;
};
class Noop_Block_Cipher : public Botan::BlockCipher
{
public:
Noop_Block_Cipher(size_t bs) : m_bs(bs) {}
void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
{
Botan::copy_mem(out, in, blocks * m_bs);
}
void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
{
Botan::copy_mem(out, in, blocks * m_bs);
}
size_t block_size() const override { return m_bs; }
void clear() override { }
std::string name() const override { return "noop"; }
Botan::Key_Length_Specification key_spec() const override
{
return Botan::Key_Length_Specification(0, 0, 1);
}
virtual BlockCipher* clone() const override { return new Noop_Block_Cipher(m_bs); }
private:
void key_schedule(const uint8_t[], size_t) override {}
size_t m_bs;
};
TLS_CBC_Tests() : Text_Based_Test("tls_cbc.vec", "Blocksize,MACsize,Record,Valid") {}
Test::Result run_one_test(const std::string&, const VarMap& vars) override
{
Test::Result result("TLS CBC");
const size_t block_size = vars.get_req_sz("Blocksize");
const size_t mac_len = vars.get_req_sz("MACsize");
const std::vector<uint8_t> record = vars.get_req_bin("Record");
const bool is_valid = vars.get_req_sz("Valid") == 1;
// todo test permutations
bool encrypt_then_mac = false;
Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption tls_cbc(
std::unique_ptr<Botan::BlockCipher>(new Noop_Block_Cipher(block_size)),
std::unique_ptr<Botan::MessageAuthenticationCode>(new ZeroMac(mac_len)),
0, 0, Botan::TLS::Protocol_Version::TLS_V11, encrypt_then_mac);
tls_cbc.set_key(std::vector<uint8_t>(0));
std::vector<uint8_t> ad(13);
tls_cbc.set_associated_data(ad.data(), ad.size());
Botan::secure_vector<uint8_t> vec(record.begin(), record.end());
try
{
tls_cbc.finish(vec, 0);
if(is_valid)
result.test_success("Accepted valid TLS-CBC ciphertext");
else
result.test_failure("Accepted invalid TLS-CBC ciphertext");
}
catch(std::exception&)
{
if(is_valid)
result.test_failure("Rejected valid TLS-CBC ciphertext");
else
result.test_success("Accepted invalid TLS-CBC ciphertext");
}
return result;
}
};
BOTAN_REGISTER_TEST("tls_cbc", TLS_CBC_Tests);
#endif
class Test_TLS_Alert_Strings : public Test
{
public:
std::vector<Test::Result> run() override
{
Test::Result result("TLS::Alert::type_string");
const std::vector<Botan::TLS::Alert::Type> alert_types =
{
Botan::TLS::Alert::CLOSE_NOTIFY,
Botan::TLS::Alert::UNEXPECTED_MESSAGE,
Botan::TLS::Alert::BAD_RECORD_MAC,
Botan::TLS::Alert::DECRYPTION_FAILED,
Botan::TLS::Alert::RECORD_OVERFLOW,
Botan::TLS::Alert::DECOMPRESSION_FAILURE,
Botan::TLS::Alert::HANDSHAKE_FAILURE,
Botan::TLS::Alert::NO_CERTIFICATE,
Botan::TLS::Alert::BAD_CERTIFICATE,
Botan::TLS::Alert::UNSUPPORTED_CERTIFICATE,
Botan::TLS::Alert::CERTIFICATE_REVOKED,
Botan::TLS::Alert::CERTIFICATE_EXPIRED,
Botan::TLS::Alert::CERTIFICATE_UNKNOWN,
Botan::TLS::Alert::ILLEGAL_PARAMETER,
Botan::TLS::Alert::UNKNOWN_CA,
Botan::TLS::Alert::ACCESS_DENIED,
Botan::TLS::Alert::DECODE_ERROR,
Botan::TLS::Alert::DECRYPT_ERROR,
Botan::TLS::Alert::EXPORT_RESTRICTION,
Botan::TLS::Alert::PROTOCOL_VERSION,
Botan::TLS::Alert::INSUFFICIENT_SECURITY,
Botan::TLS::Alert::INTERNAL_ERROR,
Botan::TLS::Alert::INAPPROPRIATE_FALLBACK,
Botan::TLS::Alert::USER_CANCELED,
Botan::TLS::Alert::NO_RENEGOTIATION,
Botan::TLS::Alert::UNSUPPORTED_EXTENSION,
Botan::TLS::Alert::CERTIFICATE_UNOBTAINABLE,
Botan::TLS::Alert::UNRECOGNIZED_NAME,
Botan::TLS::Alert::BAD_CERTIFICATE_STATUS_RESPONSE,
Botan::TLS::Alert::BAD_CERTIFICATE_HASH_VALUE,
Botan::TLS::Alert::UNKNOWN_PSK_IDENTITY,
Botan::TLS::Alert:: NO_APPLICATION_PROTOCOL,
};
std::set<std::string> seen;
for(auto alert : alert_types)
{
const std::string str = Botan::TLS::Alert(alert).type_string();
result.test_eq("No duplicate strings", seen.count(str), 0);
seen.insert(str);
}
Botan::TLS::Alert unknown_alert = Botan::TLS::Alert({01, 66});
result.test_eq("Unknown alert str", unknown_alert.type_string(), "unrecognized_alert_66");
return {result};
}
};
BOTAN_REGISTER_TEST("tls_alert_strings", Test_TLS_Alert_Strings);
class Test_TLS_Policy_Text : public Test
{
public:
std::vector<Test::Result> run() override
{
Test::Result result("TLS Policy");
const std::vector<std::string> policies = { "default", "suiteb_128", "suiteb_192", "strict", "datagram", "bsi" };
for(std::string policy : policies)
{
const std::string from_policy_obj = tls_policy_string(policy);
std::string from_file = read_tls_policy(policy);
#if !defined(BOTAN_HAS_CURVE_25519)
auto pos = from_file.find("x25519 ");
if(pos != std::string::npos)
from_file = from_file.replace(pos, 7, "");
#endif
result.test_eq("Values for TLS " + policy + " policy", from_file, from_policy_obj);
}
return {result};
}
private:
std::string read_tls_policy(const std::string& policy_str)
{
const std::string fspath = Test::data_file("tls-policy/" + policy_str + ".txt");
std::ifstream is(fspath.c_str());
if(!is.good())
{
throw Test_Error("Missing policy file " + fspath);
}
Botan::TLS::Text_Policy policy(is);
return policy.to_string();
}
std::string tls_policy_string(const std::string& policy_str)
{
std::unique_ptr<Botan::TLS::Policy> policy;
if(policy_str == "default")
{
policy.reset(new Botan::TLS::Policy);
}
else if(policy_str == "suiteb_128")
{
policy.reset(new Botan::TLS::NSA_Suite_B_128);
}
else if(policy_str == "suiteb_192")
{
policy.reset(new Botan::TLS::NSA_Suite_B_192);
}
else if(policy_str == "bsi")
{
policy.reset(new Botan::TLS::BSI_TR_02102_2);
}
else if(policy_str == "strict")
{
policy.reset(new Botan::TLS::Strict_Policy);
}
else if(policy_str == "datagram")
{
policy.reset(new Botan::TLS::Datagram_Policy);
}
else
{
throw Test_Error("Unknown TLS policy type '" + policy_str + "'");
}
return policy->to_string();
}
};
BOTAN_REGISTER_TEST("tls_policy_text", Test_TLS_Policy_Text);
class Test_TLS_Ciphersuites : public Test
{
public:
std::vector<Test::Result> run() override
{
Test::Result result("TLS::Ciphersuite");
for(size_t csuite_id = 0; csuite_id <= 0xFFFF; ++csuite_id)
{
const uint16_t csuite_id16 = static_cast<uint16_t>(csuite_id);
Botan::TLS::Ciphersuite ciphersuite = Botan::TLS::Ciphersuite::by_id(csuite_id16);
if(ciphersuite.valid())
{
result.test_eq("Valid Ciphersuite is not SCSV", Botan::TLS::Ciphersuite::is_scsv(csuite_id16), false);
if(ciphersuite.cbc_ciphersuite() == false)
{
result.test_eq("Expected MAC name for AEAD ciphersuites", ciphersuite.mac_algo(), "AEAD");
}
else
{
result.test_eq("MAC algo and PRF algo same for CBC suites", ciphersuite.prf_algo(), ciphersuite.mac_algo());
}
// TODO more tests here
}
}
return {result};
}
};
BOTAN_REGISTER_TEST("tls_ciphersuites", Test_TLS_Ciphersuites);
class Test_TLS_Algo_Strings : public Test
{
public:
std::vector<Test::Result> run() override
{
std::vector<Test::Result> results;
results.push_back(test_auth_method_strings());
results.push_back(test_kex_algo_strings());
results.push_back(test_tls_sig_method_strings());
return results;
}
private:
Test::Result test_tls_sig_method_strings()
{
Test::Result result("TLS::Signature_Scheme");
std::vector<Botan::TLS::Signature_Scheme> schemes = Botan::TLS::all_signature_schemes();
std::set<std::string> scheme_strs;
for(auto scheme : schemes)
{
std::string scheme_str = Botan::TLS::sig_scheme_to_string(scheme);
result.test_eq("Scheme strings unique", scheme_strs.count(scheme_str), 0);
scheme_strs.insert(scheme_str);
}
return result;
}
Test::Result test_auth_method_strings()
{
Test::Result result("TLS::Auth_Method");
const std::vector<Botan::TLS::Auth_Method> auth_methods({
Botan::TLS::Auth_Method::RSA,
Botan::TLS::Auth_Method::DSA,
Botan::TLS::Auth_Method::ECDSA,
Botan::TLS::Auth_Method::IMPLICIT,
Botan::TLS::Auth_Method::ANONYMOUS
});
for(Botan::TLS::Auth_Method meth : auth_methods)
{
std::string meth_str = Botan::TLS::auth_method_to_string(meth);
result.test_ne("Method string is not empty", meth_str, "");
Botan::TLS::Auth_Method meth2 = Botan::TLS::auth_method_from_string(meth_str);
result.confirm("Decoded method matches", meth == meth2);
}
return result;
}
Test::Result test_kex_algo_strings()
{
Test::Result result("TLS::Kex_Algo");
const std::vector<Botan::TLS::Kex_Algo> kex_algos({
Botan::TLS::Kex_Algo::STATIC_RSA,
Botan::TLS::Kex_Algo::DH,
Botan::TLS::Kex_Algo::ECDH,
Botan::TLS::Kex_Algo::CECPQ1,
Botan::TLS::Kex_Algo::SRP_SHA,
Botan::TLS::Kex_Algo::PSK,
Botan::TLS::Kex_Algo::DHE_PSK,
Botan::TLS::Kex_Algo::ECDHE_PSK
});
for(Botan::TLS::Kex_Algo meth : kex_algos)
{
std::string meth_str = Botan::TLS::kex_method_to_string(meth);
result.test_ne("Method string is not empty", meth_str, "");
Botan::TLS::Kex_Algo meth2 = Botan::TLS::kex_method_from_string(meth_str);
result.confirm("Decoded method matches", meth == meth2);
}
return result;
}
};
BOTAN_REGISTER_TEST("tls_algo_strings", Test_TLS_Algo_Strings);
#endif
}