summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/tls/tls_session.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/src/lib/tls/tls_session.cpp')
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_session.cpp299
1 files changed, 299 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/tls/tls_session.cpp b/comm/third_party/botan/src/lib/tls/tls_session.cpp
new file mode 100644
index 0000000000..0fd73c9fc5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_session.cpp
@@ -0,0 +1,299 @@
+/*
+* TLS Session State
+* (C) 2011-2012,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_session.h>
+#include <botan/loadstor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/asn1_obj.h>
+#include <botan/pem.h>
+#include <botan/aead.h>
+#include <botan/mac.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Session::Session(const std::vector<uint8_t>& session_identifier,
+ const secure_vector<uint8_t>& master_secret,
+ Protocol_Version version,
+ uint16_t ciphersuite,
+ Connection_Side side,
+ bool extended_master_secret,
+ bool encrypt_then_mac,
+ const std::vector<X509_Certificate>& certs,
+ const std::vector<uint8_t>& ticket,
+ const Server_Information& server_info,
+ const std::string& srp_identifier,
+ uint16_t srtp_profile) :
+ m_start_time(std::chrono::system_clock::now()),
+ m_identifier(session_identifier),
+ m_session_ticket(ticket),
+ m_master_secret(master_secret),
+ m_version(version),
+ m_ciphersuite(ciphersuite),
+ m_connection_side(side),
+ m_srtp_profile(srtp_profile),
+ m_extended_master_secret(extended_master_secret),
+ m_encrypt_then_mac(encrypt_then_mac),
+ m_peer_certs(certs),
+ m_server_info(server_info),
+ m_srp_identifier(srp_identifier)
+ {
+ }
+
+Session::Session(const std::string& pem)
+ {
+ secure_vector<uint8_t> der = PEM_Code::decode_check_label(pem, "TLS SESSION");
+
+ *this = Session(der.data(), der.size());
+ }
+
+Session::Session(const uint8_t ber[], size_t ber_len)
+ {
+ uint8_t side_code = 0;
+
+ ASN1_String server_hostname;
+ ASN1_String server_service;
+ size_t server_port;
+
+ ASN1_String srp_identifier_str;
+
+ uint8_t major_version = 0, minor_version = 0;
+ std::vector<uint8_t> peer_cert_bits;
+
+ size_t start_time = 0;
+ size_t srtp_profile = 0;
+ size_t fragment_size = 0;
+ size_t compression_method = 0;
+
+ BER_Decoder(ber, ber_len)
+ .start_cons(SEQUENCE)
+ .decode_and_check(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION),
+ "Unknown version in serialized TLS session")
+ .decode_integer_type(start_time)
+ .decode_integer_type(major_version)
+ .decode_integer_type(minor_version)
+ .decode(m_identifier, OCTET_STRING)
+ .decode(m_session_ticket, OCTET_STRING)
+ .decode_integer_type(m_ciphersuite)
+ .decode_integer_type(compression_method)
+ .decode_integer_type(side_code)
+ .decode_integer_type(fragment_size)
+ .decode(m_extended_master_secret)
+ .decode(m_encrypt_then_mac)
+ .decode(m_master_secret, OCTET_STRING)
+ .decode(peer_cert_bits, OCTET_STRING)
+ .decode(server_hostname)
+ .decode(server_service)
+ .decode(server_port)
+ .decode(srp_identifier_str)
+ .decode(srtp_profile)
+ .end_cons()
+ .verify_end();
+
+ /*
+ * Compression is not supported and must be zero
+ */
+ if(compression_method != 0)
+ {
+ throw Decoding_Error("Serialized TLS session contains non-null compression method");
+ }
+
+ /*
+ Fragment size is not supported anymore, but the field is still
+ set in the session object.
+ */
+ if(fragment_size != 0)
+ {
+ throw Decoding_Error("Serialized TLS session used maximum fragment length which is "
+ " no longer supported");
+ }
+
+ m_version = Protocol_Version(major_version, minor_version);
+ m_start_time = std::chrono::system_clock::from_time_t(start_time);
+ m_connection_side = static_cast<Connection_Side>(side_code);
+ m_srtp_profile = static_cast<uint16_t>(srtp_profile);
+
+ m_server_info = Server_Information(server_hostname.value(),
+ server_service.value(),
+ static_cast<uint16_t>(server_port));
+
+ m_srp_identifier = srp_identifier_str.value();
+
+ if(!peer_cert_bits.empty())
+ {
+ DataSource_Memory certs(peer_cert_bits.data(), peer_cert_bits.size());
+
+ while(!certs.end_of_data())
+ m_peer_certs.push_back(X509_Certificate(certs));
+ }
+ }
+
+secure_vector<uint8_t> Session::DER_encode() const
+ {
+ std::vector<uint8_t> peer_cert_bits;
+ for(size_t i = 0; i != m_peer_certs.size(); ++i)
+ peer_cert_bits += m_peer_certs[i].BER_encode();
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION))
+ .encode(static_cast<size_t>(std::chrono::system_clock::to_time_t(m_start_time)))
+ .encode(static_cast<size_t>(m_version.major_version()))
+ .encode(static_cast<size_t>(m_version.minor_version()))
+ .encode(m_identifier, OCTET_STRING)
+ .encode(m_session_ticket, OCTET_STRING)
+ .encode(static_cast<size_t>(m_ciphersuite))
+ .encode(static_cast<size_t>(/*old compression method*/0))
+ .encode(static_cast<size_t>(m_connection_side))
+ .encode(static_cast<size_t>(/*old fragment size*/0))
+ .encode(m_extended_master_secret)
+ .encode(m_encrypt_then_mac)
+ .encode(m_master_secret, OCTET_STRING)
+ .encode(peer_cert_bits, OCTET_STRING)
+ .encode(ASN1_String(m_server_info.hostname(), UTF8_STRING))
+ .encode(ASN1_String(m_server_info.service(), UTF8_STRING))
+ .encode(static_cast<size_t>(m_server_info.port()))
+ .encode(ASN1_String(m_srp_identifier, UTF8_STRING))
+ .encode(static_cast<size_t>(m_srtp_profile))
+ .end_cons()
+ .get_contents();
+ }
+
+std::string Session::PEM_encode() const
+ {
+ return PEM_Code::encode(this->DER_encode(), "TLS SESSION");
+ }
+
+std::chrono::seconds Session::session_age() const
+ {
+ return std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now() - m_start_time);
+ }
+
+namespace {
+
+// The output length of the HMAC must be a valid keylength for the AEAD
+const char* TLS_SESSION_CRYPT_HMAC = "HMAC(SHA-512-256)";
+// SIV would be better, but we can't assume it is available
+const char* TLS_SESSION_CRYPT_AEAD = "AES-256/GCM";
+const char* TLS_SESSION_CRYPT_KEY_NAME = "BOTAN TLS SESSION KEY NAME";
+const uint64_t TLS_SESSION_CRYPT_MAGIC = 0x068B5A9D396C0000;
+const size_t TLS_SESSION_CRYPT_MAGIC_LEN = 8;
+const size_t TLS_SESSION_CRYPT_KEY_NAME_LEN = 4;
+const size_t TLS_SESSION_CRYPT_AEAD_NONCE_LEN = 12;
+const size_t TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN = 16;
+const size_t TLS_SESSION_CRYPT_AEAD_TAG_SIZE = 16;
+
+const size_t TLS_SESSION_CRYPT_HDR_LEN =
+ TLS_SESSION_CRYPT_MAGIC_LEN +
+ TLS_SESSION_CRYPT_KEY_NAME_LEN +
+ TLS_SESSION_CRYPT_AEAD_NONCE_LEN +
+ TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN;
+
+const size_t TLS_SESSION_CRYPT_OVERHEAD =
+ TLS_SESSION_CRYPT_HDR_LEN + TLS_SESSION_CRYPT_AEAD_TAG_SIZE;
+
+}
+
+std::vector<uint8_t>
+Session::encrypt(const SymmetricKey& key, RandomNumberGenerator& rng) const
+ {
+ auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
+ hmac->set_key(key);
+
+ // First derive the "key name"
+ std::vector<uint8_t> key_name(hmac->output_length());
+ hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
+ hmac->final(key_name.data());
+ key_name.resize(TLS_SESSION_CRYPT_KEY_NAME_LEN);
+
+ std::vector<uint8_t> aead_nonce;
+ std::vector<uint8_t> key_seed;
+
+ rng.random_vec(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
+ rng.random_vec(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
+
+ hmac->update(key_seed);
+ const secure_vector<uint8_t> aead_key = hmac->final();
+
+ secure_vector<uint8_t> bits = this->DER_encode();
+
+ // create the header
+ std::vector<uint8_t> buf;
+ buf.reserve(TLS_SESSION_CRYPT_OVERHEAD + bits.size());
+ buf.resize(TLS_SESSION_CRYPT_MAGIC_LEN);
+ store_be(TLS_SESSION_CRYPT_MAGIC, &buf[0]);
+ buf += key_name;
+ buf += key_seed;
+ buf += aead_nonce;
+
+ std::unique_ptr<AEAD_Mode> aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, ENCRYPTION);
+ BOTAN_ASSERT_NOMSG(aead->valid_nonce_length(TLS_SESSION_CRYPT_AEAD_NONCE_LEN));
+ BOTAN_ASSERT_NOMSG(aead->tag_size() == TLS_SESSION_CRYPT_AEAD_TAG_SIZE);
+ aead->set_key(aead_key);
+ aead->set_associated_data_vec(buf);
+ aead->start(aead_nonce);
+ aead->finish(bits, 0);
+
+ // append the ciphertext
+ buf += bits;
+ return buf;
+ }
+
+Session Session::decrypt(const uint8_t in[], size_t in_len, const SymmetricKey& key)
+ {
+ try
+ {
+ const size_t min_session_size = 48 + 4; // serious under-estimate
+ if(in_len < TLS_SESSION_CRYPT_OVERHEAD + min_session_size)
+ throw Decoding_Error("Encrypted session too short to be valid");
+
+ const uint8_t* magic = &in[0];
+ const uint8_t* key_name = magic + TLS_SESSION_CRYPT_MAGIC_LEN;
+ const uint8_t* key_seed = key_name + TLS_SESSION_CRYPT_KEY_NAME_LEN;
+ const uint8_t* aead_nonce = key_seed + TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN;
+ const uint8_t* ctext = aead_nonce + TLS_SESSION_CRYPT_AEAD_NONCE_LEN;
+ const size_t ctext_len = in_len - TLS_SESSION_CRYPT_HDR_LEN; // includes the tag
+
+ if(load_be<uint64_t>(magic, 0) != TLS_SESSION_CRYPT_MAGIC)
+ throw Decoding_Error("Missing expected magic numbers");
+
+ auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
+ hmac->set_key(key);
+
+ // First derive and check the "key name"
+ std::vector<uint8_t> cmp_key_name(hmac->output_length());
+ hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
+ hmac->final(cmp_key_name.data());
+
+ if(same_mem(cmp_key_name.data(), key_name, TLS_SESSION_CRYPT_KEY_NAME_LEN) == false)
+ throw Decoding_Error("Wrong key name for encrypted session");
+
+ hmac->update(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
+ const secure_vector<uint8_t> aead_key = hmac->final();
+
+ auto aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, DECRYPTION);
+ aead->set_key(aead_key);
+ aead->set_associated_data(in, TLS_SESSION_CRYPT_HDR_LEN);
+ aead->start(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
+ secure_vector<uint8_t> buf(ctext, ctext + ctext_len);
+ aead->finish(buf, 0);
+ return Session(buf.data(), buf.size());
+ }
+ catch(std::exception& e)
+ {
+ throw Decoding_Error("Failed to decrypt serialized TLS session: " +
+ std::string(e.what()));
+ }
+ }
+
+}
+
+}