• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 25139258422

29 Apr 2026 08:02PM UTC coverage: 89.37% (-0.02%) from 89.385%
25139258422

push

github

web-flow
Merge pull request #5550 from randombit/jack/tls-misc

TLS conformance, hardening, and performance fixes

107055 of 119789 relevant lines covered (89.37%)

11415549.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.55
/src/lib/tls/tls_ciphersuite.cpp
1
/*
2
* TLS Cipher Suite
3
* (C) 2004-2010,2012,2013 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/tls_ciphersuite.h>
9

10
#include <botan/assert.h>
11
#include <botan/block_cipher.h>
12
#include <botan/exceptn.h>
13
#include <botan/hash.h>
14
#include <botan/stream_cipher.h>
15
#include <botan/internal/parsing.h>
16
#include <algorithm>
17

18
namespace Botan::TLS {
19

20
size_t Ciphersuite::nonce_bytes_from_handshake() const {
5,950✔
21
   switch(m_nonce_format) {
5,950✔
22
      case Nonce_Format::CBC_MODE:
23
         return 0;
24
      case Nonce_Format::AEAD_IMPLICIT_4:
25
         return 4;
26
      case Nonce_Format::AEAD_XOR_12:
27
         return 12;
28
      case Nonce_Format::NULL_CIPHER:
29
         return 0;
30
   }
31

32
   throw Invalid_State("In Ciphersuite::nonce_bytes_from_handshake invalid enum value");
×
33
}
34

35
size_t Ciphersuite::nonce_bytes_from_record(Protocol_Version version) const {
3,899✔
36
   BOTAN_UNUSED(version);
3,899✔
37
   switch(m_nonce_format) {
3,899✔
38
      case Nonce_Format::CBC_MODE:
576✔
39
         return cipher_algo() == "3DES" ? 8 : 16;
1,061✔
40
      case Nonce_Format::AEAD_IMPLICIT_4:
41
         return 8;
42
      case Nonce_Format::AEAD_XOR_12:
2,427✔
43
      case Nonce_Format::NULL_CIPHER:
2,427✔
44
         return 0;
2,427✔
45
   }
46

47
   throw Invalid_State("In Ciphersuite::nonce_bytes_from_handshake invalid enum value");
×
48
}
49

50
bool Ciphersuite::is_scsv(uint16_t suite) {
2,998✔
51
   // Both signaling cipher suite values - skip them when iterating
52
   // negotiable ciphersuites. The two callers are:
53
   //
54
   // - 0x00FF: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (RFC 5746). Consumed by
55
   //   Client_Hello_12::Client_Hello_12 to set secure_renegotiation when
56
   //   the renegotiation_info extension is absent.
57
   //
58
   // - 0x5600: TLS_FALLBACK_SCSV (RFC 7507). Recognized so it is filtered
59
   //   out of negotiation, but the inappropriate_fallback enforcement is
60
   //   intentionally not implemented:
61
   //     * Botan does not support TLS 1.0 / 1.1, so the 1.2 -> 1.0/1.1
62
   //       fallback that SCSV was originally designed to detect cannot
63
   //       occur here.
64
   //     * The 1.3 -> 1.2 downgrade is already protected by the
65
   //       ServerHello.random sentinel (RFC 8446 4.1.3, DOWNGRADE_TLS12),
66
   //       which Botan's TLS 1.3 client enforces at
67
   //       tls_client_impl_13.cpp via random_signals_downgrade().
68
   //
69
   // TODO: derive from IANA file in script
70
   return (suite == 0x00FF || suite == 0x5600);
2,998✔
71
}
72

73
bool Ciphersuite::psk_ciphersuite() const {
1,093✔
74
   return kex_method() == Kex_Algo::PSK || kex_method() == Kex_Algo::ECDHE_PSK;
1,093✔
75
}
76

77
bool Ciphersuite::ecc_ciphersuite() const {
1,079✔
78
   return kex_method() == Kex_Algo::ECDH || kex_method() == Kex_Algo::ECDHE_PSK || auth_method() == Auth_Method::ECDSA;
1,079✔
79
}
80

81
bool Ciphersuite::usable_in_version(Protocol_Version version) const {
624,994✔
82
   // RFC 8446 B.4.:
83
   //   Although TLS 1.3 uses the same cipher suite space as previous
84
   //   versions of TLS, TLS 1.3 cipher suites are defined differently, only
85
   //   specifying the symmetric ciphers, and cannot be used for TLS 1.2.
86
   //   Similarly, cipher suites for TLS 1.2 and lower cannot be used with
87
   //   TLS 1.3.
88
   //
89
   // Currently cipher suite codes {0x13,0x01} through {0x13,0x05} are
90
   // allowed for TLS 1.3. This may change in the future.
91
   const auto is_legacy_suite = (ciphersuite_code() & 0xFF00) != 0x1300;
624,994✔
92
   return version.is_pre_tls_13() == is_legacy_suite;
624,994✔
93
}
94

95
bool Ciphersuite::cbc_ciphersuite() const {
880✔
96
   return (mac_algo() != "AEAD" && cipher_algo() != "NULL");
1,136✔
97
}
98

99
bool Ciphersuite::null_ciphersuite() const {
182✔
100
   return (cipher_algo() == "NULL");
182✔
101
}
102

103
bool Ciphersuite::aead_ciphersuite() const {
3,015✔
104
   return (mac_algo() == "AEAD");
3,015✔
105
}
106

107
bool Ciphersuite::signature_used() const {
8,255✔
108
   return auth_method() != Auth_Method::IMPLICIT;
8,255✔
109
}
110

111
std::optional<Ciphersuite> Ciphersuite::by_id(uint16_t suite) {
81,751✔
112
   const std::vector<Ciphersuite>& all_suites = all_known_ciphersuites();
81,751✔
113
   auto s = std::lower_bound(all_suites.begin(), all_suites.end(), suite);
81,751✔
114

115
   if(s != all_suites.end() && s->ciphersuite_code() == suite) {
81,751✔
116
      return *s;
16,271✔
117
   }
118

119
   return std::nullopt;  // some unknown ciphersuite
65,480✔
120
}
121

122
std::optional<Ciphersuite> Ciphersuite::from_name(std::string_view name) {
89✔
123
   const std::vector<Ciphersuite>& all_suites = all_known_ciphersuites();
89✔
124

125
   for(const auto& suite : all_suites) {
2,672✔
126
      if(suite.to_string() == name) {
8,016✔
127
         return suite;
89✔
128
      }
129
   }
130

131
   return std::nullopt;  // some unknown ciphersuite
×
132
}
133

134
namespace {
135

136
bool have_hash(std::string_view prf) {
314,300✔
137
   return (!HashFunction::providers(prf).empty());
314,300✔
138
}
139

140
bool have_cipher(std::string_view cipher) {
215,520✔
141
   return (!BlockCipher::providers(cipher).empty()) || (!StreamCipher::providers(cipher).empty());
233,480✔
142
}
143

144
}  // namespace
145

146
bool Ciphersuite::is_usable() const {
228,990✔
147
   // NOLINTBEGIN(bugprone-branch-clone) this function needs help
148

149
   if(((cipher_algo() != "NULL") && (m_cipher_keylen == 0)) ||
457,980✔
150
      ((cipher_algo() == "NULL") && (m_cipher_keylen != 0))) {  // invalid keylen state
457,980✔
151
      return false;
152
   }
153

154
   if(!have_hash(prf_algo())) {
228,990✔
155
      return false;
156
   }
157

158
#if !defined(BOTAN_HAS_TLS_CBC)
159
   if(cbc_ciphersuite()) {
160
      return false;
161
   }
162
#endif
163

164
#if !defined(BOTAN_HAS_TLS_NULL)
165
   if(null_ciphersuite()) {
166
      return false;
167
   }
168
#endif
169

170
   if(mac_algo() == "AEAD") {
228,990✔
171
      if(cipher_algo() == "ChaCha20Poly1305") {
143,680✔
172
#if !defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
173
         return false;
174
#endif
175
      } else {
176
         auto cipher_and_mode = split_on(cipher_algo(), '/');
130,210✔
177
         BOTAN_ASSERT(cipher_and_mode.size() == 2, "Expected format for AEAD algo");
130,210✔
178
         if(!have_cipher(cipher_and_mode[0])) {
130,210✔
179
            return false;
×
180
         }
181

182
         const auto& mode = cipher_and_mode[1];
130,210✔
183

184
#if !defined(BOTAN_HAS_AEAD_CCM)
185
         if(mode == "CCM" || mode == "CCM(8)") {
186
            return false;
187
         }
188
#endif
189

190
#if !defined(BOTAN_HAS_AEAD_GCM)
191
         if(mode == "GCM") {
192
            return false;
193
         }
194
#endif
195

196
#if !defined(BOTAN_HAS_AEAD_OCB)
197
         if(mode == "OCB(12)" || mode == "OCB") {
198
            return false;
199
         }
200
#endif
201

202
         // Potentially unused if all AEADs are available
203
         BOTAN_UNUSED(mode);
130,210✔
204
      }
130,210✔
205
   } else {
206
      // Old non-AEAD schemes
207
      if(!have_cipher(cipher_algo()) && (cipher_algo() != "NULL")) {
121,230✔
208
         return false;
209
      }
210
      if(!have_hash(mac_algo())) {  // HMAC
85,310✔
211
         return false;
212
      }
213
   }
214

215
   if(kex_method() == Kex_Algo::ECDH || kex_method() == Kex_Algo::ECDHE_PSK) {
228,990✔
216
#if !defined(BOTAN_HAS_ECDH)
217
      return false;
218
#endif
219
   } else if(kex_method() == Kex_Algo::DH) {
220
#if !defined(BOTAN_HAS_DIFFIE_HELLMAN)
221
      return false;
222
#endif
223
   }
224

225
   if(auth_method() == Auth_Method::ECDSA) {
228,990✔
226
#if !defined(BOTAN_HAS_ECDSA)
227
      return false;
228
#endif
229
   } else if(auth_method() == Auth_Method::RSA) {
230
#if !defined(BOTAN_HAS_RSA)
231
      return false;
232
#endif
233
   }
234

235
   // NOLINTEND(bugprone-branch-clone)
236

237
   return true;
238
}
239

240
}  // namespace Botan::TLS
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc