Botan  1.11.15
src/lib/tls/tls_ciphersuite.cpp
Go to the documentation of this file.
00001 /*
00002 * TLS Cipher Suite
00003 * (C) 2004-2010,2012,2013 Jack Lloyd
00004 *
00005 * Botan is released under the Simplified BSD License (see license.txt)
00006 */
00007 
00008 #include <botan/tls_ciphersuite.h>
00009 #include <botan/parsing.h>
00010 #include <botan/internal/algo_registry.h>
00011 #include <botan/block_cipher.h>
00012 #include <botan/stream_cipher.h>
00013 #include <botan/hash.h>
00014 #include <botan/mac.h>
00015 #include <sstream>
00016 #include <stdexcept>
00017 
00018 namespace Botan {
00019 
00020 namespace TLS {
00021 
00022 namespace {
00023 
00024 /*
00025 * This way all work happens at the constuctor call, and we can
00026 * rely on that happening only once in C++11.
00027 */
00028 std::vector<Ciphersuite> gather_known_ciphersuites()
00029    {
00030    std::vector<Ciphersuite> ciphersuites;
00031 
00032    for(size_t i = 0; i <= 0xFFFF; ++i)
00033       {
00034       Ciphersuite suite = Ciphersuite::by_id(i);
00035 
00036       if(suite.valid())
00037          ciphersuites.push_back(suite);
00038       }
00039 
00040    return ciphersuites;
00041    }
00042 
00043 }
00044 
00045 const std::vector<Ciphersuite>& Ciphersuite::all_known_ciphersuites()
00046    {
00047    static std::vector<Ciphersuite> all_ciphersuites(gather_known_ciphersuites());
00048    return all_ciphersuites;
00049    }
00050 
00051 Ciphersuite Ciphersuite::by_name(const std::string& name)
00052    {
00053    for(auto suite : all_known_ciphersuites())
00054       {
00055       if(suite.to_string() == name)
00056          return suite;
00057       }
00058 
00059    return Ciphersuite(); // some unknown ciphersuite
00060    }
00061 
00062 bool Ciphersuite::is_scsv(u16bit suite)
00063    {
00064    // TODO: derive from IANA file in script
00065    return (suite == 0x00FF || suite == 0x5600);
00066    }
00067 
00068 Ciphersuite::Ciphersuite(u16bit ciphersuite_code,
00069                          const char* sig_algo,
00070                          const char* kex_algo,
00071                          const char* cipher_algo,
00072                          size_t cipher_keylen,
00073                          size_t nonce_bytes_from_handshake,
00074                          size_t nonce_bytes_from_record,
00075                          const char* mac_algo,
00076                          size_t mac_keylen,
00077                          const char* prf_algo) :
00078    m_ciphersuite_code(ciphersuite_code),
00079    m_sig_algo(sig_algo),
00080    m_kex_algo(kex_algo),
00081    m_prf_algo(prf_algo),
00082    m_cipher_algo(cipher_algo),
00083    m_cipher_keylen(cipher_keylen),
00084    m_nonce_bytes_from_handshake(nonce_bytes_from_handshake),
00085    m_nonce_bytes_from_record(nonce_bytes_from_record),
00086    m_mac_algo(mac_algo),
00087    m_mac_keylen(mac_keylen)
00088    {
00089    }
00090 
00091 bool Ciphersuite::psk_ciphersuite() const
00092    {
00093    return (kex_algo() == "PSK" ||
00094            kex_algo() == "DHE_PSK" ||
00095            kex_algo() == "ECDHE_PSK");
00096    }
00097 
00098 bool Ciphersuite::ecc_ciphersuite() const
00099    {
00100    return (sig_algo() == "ECDSA" || kex_algo() == "ECDH" || kex_algo() == "ECDHE_PSK");
00101    }
00102 
00103 namespace {
00104 
00105 bool have_hash(const std::string& prf)
00106    {
00107    if(Algo_Registry<HashFunction>::global_registry().providers_of(prf).size() > 0)
00108       return true;
00109    return false;
00110    }
00111 
00112 bool have_cipher(const std::string& cipher)
00113    {
00114    if(Algo_Registry<BlockCipher>::global_registry().providers_of(cipher).size() > 0)
00115       return true;
00116    if(Algo_Registry<StreamCipher>::global_registry().providers_of(cipher).size() > 0)
00117       return true;
00118    return false;
00119    }
00120 
00121 }
00122 
00123 bool Ciphersuite::valid() const
00124    {
00125    if(!m_cipher_keylen) // uninitialized object
00126       return false;
00127 
00128    if(!have_hash(prf_algo()))
00129       return false;
00130 
00131    if(mac_algo() == "AEAD")
00132       {
00133       if(cipher_algo() == "ChaCha20Poly1305")
00134          {
00135 #if !defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
00136          return false;
00137 #endif
00138          }
00139       else
00140          {
00141          auto cipher_and_mode = split_on(cipher_algo(), '/');
00142          BOTAN_ASSERT(cipher_and_mode.size() == 2, "Expected format for AEAD algo");
00143          if(!have_cipher(cipher_and_mode[0]))
00144             return false;
00145 
00146          const auto mode = cipher_and_mode[1];
00147 
00148 #if !defined(BOTAN_HAS_AEAD_CCM)
00149          if(mode == "CCM" || mode == "CCM-8")
00150             return false;
00151 #endif
00152 
00153 #if !defined(BOTAN_HAS_AEAD_GCM)
00154          if(mode == "GCM")
00155             return false;
00156 #endif
00157 
00158 #if !defined(BOTAN_HAS_AEAD_OCB)
00159          if(mode == "OCB(12)" || mode == "OCB")
00160             return false;
00161 #endif
00162          }
00163       }
00164    else
00165       {
00166       // Old non-AEAD schemes
00167       if(!have_cipher(cipher_algo()))
00168          return false;
00169       if(!have_hash(mac_algo())) // HMAC
00170          return false;
00171       }
00172 
00173    if(kex_algo() == "SRP_SHA")
00174       {
00175 #if !defined(BOTAN_HAS_SRP6)
00176       return false;
00177 #endif
00178       }
00179    else if(kex_algo() == "ECDH" || kex_algo() == "ECDHE_PSK")
00180       {
00181 #if !defined(BOTAN_HAS_ECDH)
00182       return false;
00183 #endif
00184       }
00185    else if(kex_algo() == "DH" || kex_algo() == "DHE_PSK")
00186       {
00187 #if !defined(BOTAN_HAS_DIFFIE_HELLMAN)
00188       return false;
00189 #endif
00190       }
00191 
00192    if(sig_algo() == "DSA")
00193       {
00194 #if !defined(BOTAN_HAS_DSA)
00195       return false;
00196 #endif
00197       }
00198    else if(sig_algo() == "ECDSA")
00199       {
00200 #if !defined(BOTAN_HAS_ECDSA)
00201       return false;
00202 #endif
00203       }
00204    else if(sig_algo() == "RSA")
00205       {
00206 #if !defined(BOTAN_HAS_RSA)
00207       return false;
00208 #endif
00209       }
00210 
00211    return true;
00212    }
00213 
00214 std::string Ciphersuite::to_string() const
00215    {
00216    if(m_cipher_keylen == 0)
00217       throw std::runtime_error("Ciphersuite::to_string - no value set");
00218 
00219    std::ostringstream out;
00220 
00221    out << "TLS_";
00222 
00223    if(kex_algo() != "RSA")
00224       {
00225       if(kex_algo() == "DH")
00226          out << "DHE";
00227       else if(kex_algo() == "ECDH")
00228          out << "ECDHE";
00229       else
00230          out << kex_algo();
00231 
00232       out << '_';
00233       }
00234 
00235    if(sig_algo() == "DSA")
00236       out << "DSS_";
00237    else if(sig_algo() != "")
00238       out << sig_algo() << '_';
00239 
00240    out << "WITH_";
00241 
00242    if(cipher_algo() == "RC4")
00243       {
00244       out << "RC4_128_";
00245       }
00246    else if(cipher_algo() == "ChaCha20Poly1305")
00247       {
00248       out << "CHACHA20_POLY1305_";
00249       }
00250    else
00251       {
00252       if(cipher_algo() == "3DES")
00253          out << "3DES_EDE";
00254       else if(cipher_algo().find("Camellia") == 0)
00255          out << "CAMELLIA_" << std::to_string(8*cipher_keylen());
00256       else
00257          {
00258          if(cipher_algo().find("OCB(12)") != std::string::npos)
00259             out << replace_chars(cipher_algo().substr(0, cipher_algo().size() - 4),
00260                                  {'-', '/'}, '_');
00261          else
00262             out << replace_chars(cipher_algo(), {'-', '/'}, '_');
00263          }
00264 
00265       if(cipher_algo().find("/") != std::string::npos)
00266          out << "_"; // some explicit mode already included
00267       else
00268          out << "_CBC_";
00269       }
00270 
00271    if(mac_algo() == "SHA-1")
00272       out << "SHA";
00273    else if(mac_algo() == "AEAD")
00274       out << erase_chars(prf_algo(), {'-'});
00275    else
00276       out << erase_chars(mac_algo(), {'-'});
00277 
00278    return out.str();
00279    }
00280 
00281 }
00282 
00283 }
00284