/* * TLS Cipher Suite * (C) 2004-2010,2012,2013 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include #include #include namespace Botan { namespace TLS { size_t Ciphersuite::nonce_bytes_from_handshake() const { switch(m_nonce_format) { case Nonce_Format::CBC_MODE: { if(cipher_algo() == "3DES") return 8; else return 16; } case Nonce_Format::AEAD_IMPLICIT_4: return 4; case Nonce_Format::AEAD_XOR_12: return 12; } throw Invalid_State("In Ciphersuite::nonce_bytes_from_handshake invalid enum value"); } size_t Ciphersuite::nonce_bytes_from_record(Protocol_Version version) const { switch(m_nonce_format) { case Nonce_Format::CBC_MODE: { if(version.supports_explicit_cbc_ivs()) { return cipher_algo() == "3DES" ? 8 : 16; } else { return 0; } } case Nonce_Format::AEAD_IMPLICIT_4: return 8; case Nonce_Format::AEAD_XOR_12: return 0; } throw Invalid_State("In Ciphersuite::nonce_bytes_from_handshake invalid enum value"); } bool Ciphersuite::is_scsv(uint16_t suite) { // TODO: derive from IANA file in script return (suite == 0x00FF || suite == 0x5600); } bool Ciphersuite::psk_ciphersuite() const { return kex_method() == Kex_Algo::PSK || kex_method() == Kex_Algo::DHE_PSK || kex_method() == Kex_Algo::ECDHE_PSK; } bool Ciphersuite::ecc_ciphersuite() const { return kex_method() == Kex_Algo::ECDH || kex_method() == Kex_Algo::ECDHE_PSK || auth_method() == Auth_Method::ECDSA; } bool Ciphersuite::usable_in_version(Protocol_Version version) const { if(!version.supports_aead_modes()) { // Old versions do not support AEAD, or any MAC but SHA-1 if(mac_algo() != "SHA-1") return false; } return true; } bool Ciphersuite::cbc_ciphersuite() const { return (mac_algo() != "AEAD"); } bool Ciphersuite::signature_used() const { return auth_method() != Auth_Method::ANONYMOUS && auth_method() != Auth_Method::IMPLICIT; } Ciphersuite Ciphersuite::by_id(uint16_t suite) { const std::vector& all_suites = all_known_ciphersuites(); auto s = std::lower_bound(all_suites.begin(), all_suites.end(), suite); if(s != all_suites.end() && s->ciphersuite_code() == suite) { return *s; } return Ciphersuite(); // some unknown ciphersuite } Ciphersuite Ciphersuite::from_name(const std::string& name) { const std::vector& all_suites = all_known_ciphersuites(); for(auto suite : all_suites) { if(suite.to_string() == name) return suite; } return Ciphersuite(); // some unknown ciphersuite } namespace { bool have_hash(const std::string& prf) { return (HashFunction::providers(prf).size() > 0); } bool have_cipher(const std::string& cipher) { return (BlockCipher::providers(cipher).size() > 0) || (StreamCipher::providers(cipher).size() > 0); } } bool Ciphersuite::is_usable() const { if(!m_cipher_keylen) // uninitialized object return false; if(!have_hash(prf_algo())) return false; #if !defined(BOTAN_HAS_TLS_CBC) if(cbc_ciphersuite()) return false; #endif if(mac_algo() == "AEAD") { if(cipher_algo() == "ChaCha20Poly1305") { #if !defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) return false; #endif } else { auto cipher_and_mode = split_on(cipher_algo(), '/'); BOTAN_ASSERT(cipher_and_mode.size() == 2, "Expected format for AEAD algo"); if(!have_cipher(cipher_and_mode[0])) return false; const auto mode = cipher_and_mode[1]; #if !defined(BOTAN_HAS_AEAD_CCM) if(mode == "CCM" || mode == "CCM-8") return false; #endif #if !defined(BOTAN_HAS_AEAD_GCM) if(mode == "GCM") return false; #endif #if !defined(BOTAN_HAS_AEAD_OCB) if(mode == "OCB(12)" || mode == "OCB") return false; #endif } } else { // Old non-AEAD schemes if(!have_cipher(cipher_algo())) return false; if(!have_hash(mac_algo())) // HMAC return false; } if(kex_method() == Kex_Algo::SRP_SHA) { #if !defined(BOTAN_HAS_SRP6) return false; #endif } else if(kex_method() == Kex_Algo::ECDH || kex_method() == Kex_Algo::ECDHE_PSK) { #if !defined(BOTAN_HAS_ECDH) return false; #endif } else if(kex_method() == Kex_Algo::DH || kex_method() == Kex_Algo::DHE_PSK) { #if !defined(BOTAN_HAS_DIFFIE_HELLMAN) return false; #endif } else if(kex_method() == Kex_Algo::CECPQ1) { #if !defined(BOTAN_HAS_CECPQ1) return false; #endif } if(auth_method() == Auth_Method::DSA) { #if !defined(BOTAN_HAS_DSA) return false; #endif } else if(auth_method() == Auth_Method::ECDSA) { #if !defined(BOTAN_HAS_ECDSA) return false; #endif } else if(auth_method() == Auth_Method::RSA) { #if !defined(BOTAN_HAS_RSA) return false; #endif } return true; } } }