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

randombit / botan / 30974838434

04 Aug 2026 05:33PM UTC coverage: 89.682% (+0.02%) from 89.662%
30974838434

push

github

web-flow
Merge pull request #5793 from randombit/jack/dtls-bounded-retry

Bound DTLS handshake retry attempts and properly abandon timed out states

119137 of 132844 relevant lines covered (89.68%)

10371831.54 hits per line

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

80.37
/src/lib/tls/tls_callbacks.cpp
1
/*
2
* TLS Callbacks
3
* (C) 2016 Jack Lloyd
4
*     2017 Harry Reimann, Rohde & Schwarz Cybersecurity
5
*     2022 René Meusel, Hannes Rantzsch - neXenio GmbH
6
*     2023 René Meusel - Rohde & Schwarz Cybersecurity
7
*
8
* Botan is released under the Simplified BSD License (see license.txt)
9
*/
10

11
#include <botan/tls_callbacks.h>
12

13
#include <botan/dh.h>
14
#include <botan/dl_group.h>
15
#include <botan/ec_group.h>
16
#include <botan/ecdh.h>
17
#include <botan/ocsp.h>
18
#include <botan/pk_algs.h>
19
#include <botan/tls_algos.h>
20
#include <botan/tls_exceptn.h>
21
#include <botan/tls_policy.h>
22
#include <botan/tls_session.h>
23
#include <botan/x509path.h>
24
#include <botan/internal/fmt.h>
25
#include <botan/internal/stl_util.h>
26

27
#if defined(BOTAN_HAS_X25519)
28
   #include <botan/x25519.h>
29
#endif
30

31
#if defined(BOTAN_HAS_X448)
32
   #include <botan/x448.h>
33
#endif
34

35
#if defined(BOTAN_HAS_ML_KEM)
36
   #include <botan/ml_kem.h>
37
#endif
38

39
#if defined(BOTAN_HAS_FRODOKEM)
40
   #include <botan/frodokem.h>
41
#endif
42

43
#if defined(BOTAN_HAS_TLS_13_PQC)
44
   #include <botan/internal/hybrid_public_key.h>
45
#endif
46

47
namespace Botan {
48

49
void TLS::Callbacks::tls_inspect_handshake_msg(const Handshake_Message& /*unused*/) {
7,036✔
50
   // default is no op
51
}
7,036✔
52

53
std::string TLS::Callbacks::tls_server_choose_app_protocol(const std::vector<std::string>& /*unused*/) {
×
54
   return "";
×
55
}
56

57
std::string TLS::Callbacks::tls_peer_network_identity() {
×
58
   return "";
×
59
}
60

61
std::chrono::system_clock::time_point TLS::Callbacks::tls_current_timestamp() {
5,570✔
62
   return std::chrono::system_clock::now();
5,570✔
63
}
64

65
uint64_t TLS::Callbacks::tls_current_monotonic_clock_ms() {
409✔
66
   const auto now = std::chrono::steady_clock::now().time_since_epoch();
409✔
67
   return std::chrono::duration_cast<std::chrono::milliseconds>(now).count();
409✔
68
}
69

70
void TLS::Callbacks::tls_modify_extensions(Extensions& /*unused*/,
2,244✔
71
                                           Connection_Side /*unused*/,
72
                                           Handshake_Type /*unused*/) {}
2,244✔
73

74
void TLS::Callbacks::tls_examine_extensions(const Extensions& /*unused*/,
6,588✔
75
                                            Connection_Side /*unused*/,
76
                                            Handshake_Type /*unused*/) {}
6,588✔
77

78
bool TLS::Callbacks::tls_should_persist_resumption_information(const Session& session) {
3,068✔
79
   // RFC 5077 3.3
80
   //    The ticket_lifetime_hint field contains a hint from the server about
81
   //    how long the ticket should be stored. A value of zero is reserved to
82
   //    indicate that the lifetime of the ticket is unspecified.
83
   //
84
   // RFC 8446 4.6.1
85
   //    [A ticket_lifetime] of zero indicates that the ticket should be discarded
86
   //    immediately.
87
   //
88
   // By default we opt to keep all sessions, except for TLS 1.3 with a lifetime
89
   // hint of zero.
90
   return session.lifetime_hint().count() > 0 || session.version().is_pre_tls_13();
3,068✔
91
}
92

93
void TLS::Callbacks::tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain,
1,499✔
94
                                           const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
95
                                           const std::vector<Certificate_Store*>& trusted_roots,
96
                                           Usage_Type usage,
97
                                           std::string_view hostname,
98
                                           const TLS::Policy& policy) {
99
   if(cert_chain.empty()) {
1,499✔
100
      throw Invalid_Argument("Certificate chain was empty");
×
101
   }
102

103
   const Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(),
1,499✔
104
                                                   policy.minimum_signature_strength());
2,998✔
105

106
   /*
107
   Hostname is always provided in order to allow host-specific logic if required,
108
   but it should not be passed to x509_path_validate unless we are verifying
109
   the server.
110
   */
111
   const std::string_view name_to_match = (usage == Usage_Type::TLS_CLIENT_AUTH) ? std::string_view{} : hostname;
1,499✔
112

113
   const Path_Validation_Result result = x509_path_validate(cert_chain,
1,499✔
114
                                                            restrictions,
115
                                                            trusted_roots,
116
                                                            name_to_match,
117
                                                            usage,
118
                                                            tls_current_timestamp(),
1,499✔
119
                                                            tls_verify_cert_chain_ocsp_timeout(),
1,499✔
120
                                                            ocsp_responses);
1,499✔
121

122
   if(!result.successful_validation()) {
1,499✔
123
      throw TLS_Exception(Alert::BadCertificate, "Certificate validation failure: " + result.result_string());
458✔
124
   }
125
}
1,728✔
126

127
void TLS::Callbacks::tls_verify_raw_public_key(const Public_Key& raw_public_key,
×
128
                                               Usage_Type usage,
129
                                               std::string_view hostname,
130
                                               const TLS::Policy& policy) {
131
   BOTAN_UNUSED(raw_public_key, usage, hostname, policy);
×
132
   // There is no good default implementation for authenticating raw public key.
133
   // Applications that wish to use them for authentication, must override this.
134
   throw TLS_Exception(Alert::CertificateUnknown, "Application did not provide a means to validate the raw public key");
×
135
}
136

137
std::optional<OCSP::Response> TLS::Callbacks::tls_parse_ocsp_response(const std::vector<uint8_t>& raw_response) {
×
138
   try {
×
139
      return OCSP::Response(raw_response);
×
140
   } catch(const Decoding_Error&) {
×
141
      // ignore parsing errors and just ignore the broken OCSP response
142
      return std::nullopt;
×
143
   }
×
144
}
145

146
std::vector<std::vector<uint8_t>> TLS::Callbacks::tls_provide_cert_chain_status(
314✔
147
   const std::vector<X509_Certificate>& chain, const Certificate_Status_Request& csr) {
148
   std::vector<std::vector<uint8_t>> result(chain.size());
314✔
149
   if(!chain.empty()) {
314✔
150
      result[0] = tls_provide_cert_status(chain, csr);
314✔
151
   }
152
   return result;
306✔
153
}
8✔
154

155
std::vector<uint8_t> TLS::Callbacks::tls_sign_message(const Private_Key& key,
1,081✔
156
                                                      RandomNumberGenerator& rng,
157
                                                      std::string_view padding,
158
                                                      Signature_Format format,
159
                                                      const std::vector<uint8_t>& msg) {
160
   PK_Signer signer(key, rng, padding, format);
1,081✔
161

162
   return signer.sign_message(msg, rng);
2,161✔
163
}
1,081✔
164

165
bool TLS::Callbacks::tls_verify_message(const Public_Key& key,
1,466✔
166
                                        std::string_view padding,
167
                                        Signature_Format format,
168
                                        const std::vector<uint8_t>& msg,
169
                                        const std::vector<uint8_t>& sig) {
170
   PK_Verifier verifier(key, padding, format);
1,466✔
171

172
   return verifier.verify_message(msg, sig);
2,932✔
173
}
1,466✔
174

175
namespace {
176

177
bool is_dh_group(const std::variant<TLS::Group_Params, DL_Group>& group) {
5,401✔
178
   return std::holds_alternative<DL_Group>(group) || std::get<TLS::Group_Params>(group).is_dh_named_group();
10,794✔
179
}
180

181
DL_Group get_dl_group(const std::variant<TLS::Group_Params, DL_Group>& group) {
20✔
182
   BOTAN_ASSERT_NOMSG(is_dh_group(group));
20✔
183

184
   // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of
185
   // a standardized DH group identifier. TLS 1.3 just offers pre-defined
186
   // groups.
187
   return std::visit(overloaded{[](const DL_Group& dl_group) { return dl_group; },
4✔
188
                                [&](TLS::Group_Params group_param) {
16✔
189
                                   return DL_Group::from_name(group_param.to_algorithm_spec().value());
48✔
190
                                }},
191
                     group);
20✔
192
}
193

194
}  // namespace
195

196
std::unique_ptr<Public_Key> TLS::Callbacks::tls_deserialize_peer_public_key(
2,389✔
197
   const std::variant<TLS::Group_Params, DL_Group>& group, std::span<const uint8_t> key_bits) {
198
   if(is_dh_group(group)) {
2,389✔
199
      // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of
200
      // a standardized DH group identifier.
201
      const auto dl_group = get_dl_group(group);
8✔
202

203
      auto Y = BigInt::from_bytes(key_bits);
8✔
204

205
      /*
206
       * A basic check for key validity. As we do not know q here we
207
       * cannot check that Y is in the right subgroup. However since
208
       * our key is ephemeral there does not seem to be any
209
       * advantage to bogus keys anyway.
210
       */
211
      if(Y <= 1 || Y >= dl_group.get_p() - 1) {
16✔
212
         throw Decoding_Error("Server sent bad DH key for DHE exchange");
×
213
      }
214

215
      return std::make_unique<DH_PublicKey>(dl_group, Y);
16✔
216
   }
16✔
217

218
   // The special case for TLS 1.2 with an explicit DH group definition is
219
   // handled above. All other cases are based on the opaque group definition.
220
   BOTAN_ASSERT_NOMSG(std::holds_alternative<TLS::Group_Params>(group));
2,381✔
221
   const auto group_params = std::get<TLS::Group_Params>(group);
2,381✔
222

223
   if(group_params.is_ecdh_named_curve()) {
2,381✔
224
      const auto ec_group = EC_Group::from_name(group_params.to_algorithm_spec().value());
198✔
225
      // TLS 1.3 requires uncompressed points (checked when parsing the key
226
      // share); TLS 1.2 may negotiate the compressed format. The deprecated
227
      // hybrid encoding and the identity element are never accepted.
228

229
      auto point = [&]() -> EC_AffinePoint {
297✔
230
         if(auto pt_uncompressed = EC_AffinePoint::deserialize_uncompressed(ec_group, key_bits)) {
99✔
231
            return std::move(pt_uncompressed).value();
63✔
232
         } else if(auto pt_compressed = EC_AffinePoint::deserialize_compressed(ec_group, key_bits)) {
36✔
233
            return std::move(pt_compressed).value();
×
234
         } else {
235
            throw Decoding_Error("Invalid ECDH public key encoding");
36✔
236
         }
99✔
237
      }();
99✔
238
      return std::make_unique<ECDH_PublicKey>(ec_group, std::move(point));
126✔
239
   }
99✔
240

241
#if defined(BOTAN_HAS_X25519)
242
   if(group_params.is_x25519()) {
2,282✔
243
      return std::make_unique<X25519_PublicKey>(key_bits);
4,438✔
244
   }
245
#endif
246

247
#if defined(BOTAN_HAS_X448)
248
   if(group_params.is_x448()) {
59✔
249
      return std::make_unique<X448_PublicKey>(key_bits);
8✔
250
   }
251
#endif
252

253
#if defined(BOTAN_HAS_TLS_13_PQC)
254
   if(group_params.is_pqc_hybrid()) {
55✔
255
      return Hybrid_KEM_PublicKey::load_for_group(group_params, key_bits);
97✔
256
   }
257
#endif
258

259
#if defined(BOTAN_HAS_ML_KEM)
260
   if(group_params.is_pure_ml_kem()) {
5✔
261
      return std::make_unique<ML_KEM_PublicKey>(key_bits, ML_KEM_Mode(group_params.to_algorithm_spec().value()));
17✔
262
   }
263
#endif
264

265
#if defined(BOTAN_HAS_FRODOKEM)
266
   if(group_params.is_pure_frodokem()) {
×
267
      return std::make_unique<FrodoKEM_PublicKey>(key_bits, FrodoKEMMode(group_params.to_algorithm_spec().value()));
×
268
   }
269
#endif
270

271
   throw Decoding_Error("cannot create a key offering without a group definition");
×
272
}
273

274
std::unique_ptr<Private_Key> TLS::Callbacks::tls_kem_generate_key(TLS::Group_Params group, RandomNumberGenerator& rng) {
2,220✔
275
#if defined(BOTAN_HAS_ML_KEM)
276
   if(group.is_pure_ml_kem()) {
2,220✔
277
      return std::make_unique<ML_KEM_PrivateKey>(rng, ML_KEM_Mode(group.to_algorithm_spec().value()));
33✔
278
   }
279
#endif
280

281
#if defined(BOTAN_HAS_FRODOKEM)
282
   if(group.is_pure_frodokem()) {
2,209✔
283
      return std::make_unique<FrodoKEM_PrivateKey>(rng, FrodoKEMMode(group.to_algorithm_spec().value()));
×
284
   }
285
#endif
286

287
#if defined(BOTAN_HAS_TLS_13_PQC)
288
   if(group.is_pqc_hybrid()) {
2,209✔
289
      return Hybrid_KEM_PrivateKey::generate_from_group(group, rng);
2,076✔
290
   }
291
#endif
292

293
   return tls_generate_ephemeral_key(group, rng);
3,513✔
294
}
295

296
KEM_Encapsulation TLS::Callbacks::tls_kem_encapsulate(TLS::Group_Params group,
574✔
297
                                                      const std::vector<uint8_t>& encoded_public_key,
298
                                                      RandomNumberGenerator& rng,
299
                                                      const Policy& policy) {
300
   if(group.is_kem()) {
574✔
301
      auto kem_pub_key = [&] {
159✔
302
         try {
55✔
303
            return tls_deserialize_peer_public_key(group, encoded_public_key);
110✔
304
         } catch(const Decoding_Error& ex) {
6✔
305
            // This exception means that the public key was invalid. However,
306
            // TLS' DecodeError would imply that a protocol message was invalid.
307
            throw TLS_Exception(Alert::IllegalParameter, ex.what());
4✔
308
         } catch(const Invalid_Argument& ex) {
6✔
309
            throw TLS_Exception(Alert::IllegalParameter, ex.what());
2✔
310
         }
2✔
311
      }();
55✔
312

313
      BOTAN_ASSERT_NONNULL(kem_pub_key);
49✔
314
      policy.check_peer_key_acceptable(*kem_pub_key);
49✔
315

316
      try {
49✔
317
         return PK_KEM_Encryptor(*kem_pub_key, "Raw").encrypt(rng);
49✔
318
      } catch(const Decoding_Error& ex) {
1✔
319
         throw TLS_Exception(Alert::IllegalParameter, ex.what());
×
320
      } catch(const Invalid_Argument& ex) {
1✔
321
         throw TLS_Exception(Alert::IllegalParameter, ex.what());
1✔
322
      }
1✔
323
   } else {
49✔
324
      // TODO: We could use the KEX_to_KEM_Adapter to remove the case distinction
325
      //       of KEM and KEX. However, the workarounds in this adapter class
326
      //       should first be addressed.
327
      auto ephemeral_keypair = tls_generate_ephemeral_key(group, rng);
519✔
328
      BOTAN_ASSERT_NONNULL(ephemeral_keypair);
519✔
329
      return {ephemeral_keypair->public_value(),
1,038✔
330
              tls_ephemeral_key_agreement(group, *ephemeral_keypair, encoded_public_key, rng, policy)};
1,038✔
331
   }
507✔
332
}
333

334
secure_vector<uint8_t> TLS::Callbacks::tls_kem_decapsulate(TLS::Group_Params group,
588✔
335
                                                           const Private_Key& private_key,
336
                                                           const std::vector<uint8_t>& encapsulated_bytes,
337
                                                           RandomNumberGenerator& rng,
338
                                                           const Policy& policy) {
339
   if(group.is_kem()) {
588✔
340
      PK_KEM_Decryptor kemdec(private_key, rng, "Raw");
60✔
341
      if(encapsulated_bytes.size() != kemdec.encapsulated_key_length()) {
60✔
342
         throw TLS_Exception(Alert::IllegalParameter, "Invalid encapsulated key length");
4✔
343
      }
344
      try {
56✔
345
         return kemdec.decrypt(encapsulated_bytes, 0, {});
56✔
346
      } catch(const Decoding_Error& ex) {
1✔
347
         throw TLS_Exception(Alert::IllegalParameter, ex.what());
×
348
      } catch(const Invalid_Argument& ex) {
1✔
349
         throw TLS_Exception(Alert::IllegalParameter, ex.what());
1✔
350
      }
1✔
351
   }
60✔
352

353
   try {
528✔
354
      const auto& key_agreement_key = dynamic_cast<const PK_Key_Agreement_Key&>(private_key);
528✔
355
      return tls_ephemeral_key_agreement(group, key_agreement_key, encapsulated_bytes, rng, policy);
1,056✔
356
   } catch(const std::bad_cast&) {
12✔
357
      throw Invalid_Argument("provided ephemeral key is not a PK_Key_Agreement_Key");
×
358
   }
×
359
}
360

361
std::unique_ptr<PK_Key_Agreement_Key> TLS::Callbacks::tls_generate_ephemeral_key(
2,992✔
362
   const std::variant<TLS::Group_Params, DL_Group>& group, RandomNumberGenerator& rng) {
363
   if(is_dh_group(group)) {
2,992✔
364
      const DL_Group dl_group = get_dl_group(group);
12✔
365
      return std::make_unique<DH_PrivateKey>(rng, dl_group);
24✔
366
   }
12✔
367

368
   BOTAN_ASSERT_NOMSG(std::holds_alternative<TLS::Group_Params>(group));
2,980✔
369
   const auto group_params = std::get<TLS::Group_Params>(group);
2,980✔
370

371
   if(group_params.is_ecdh_named_curve()) {
2,980✔
372
      const auto ec_group = EC_Group::from_name(group_params.to_algorithm_spec().value());
256✔
373
      auto ecdh_key = std::make_unique<ECDH_PrivateKey>(rng, ec_group);
128✔
374

375
      // RFC 8446 Ch. 4.2.8.2
376
      //
377
      //   Note: Versions of TLS prior to 1.3 permitted point format
378
      //   negotiation; TLS 1.3 removes this feature in favor of a single point
379
      //   format for each curve.
380
      //
381
      // Hence, TLS 1.3 won't take Policy::use_ecc_point_compression() or
382
      // ClientHello::prefers_compressed_ec_points() into account but always use
383
      // uncompressed point encoding. Note that TLS 1.2 uses the
384
      // `tls12_generate_ephemeral_ecdh_key()` callback, which allows to specify
385
      // the point encoding format.
386
      ecdh_key->set_point_encoding(EC_Point_Format::Uncompressed);
128✔
387
      return ecdh_key;
128✔
388
   }
128✔
389

390
#if defined(BOTAN_HAS_X25519)
391
   if(group_params.is_x25519()) {
2,852✔
392
      return std::make_unique<X25519_PrivateKey>(rng);
5,696✔
393
   }
394
#endif
395

396
#if defined(BOTAN_HAS_X448)
397
   if(group_params.is_x448()) {
4✔
398
      return std::make_unique<X448_PrivateKey>(rng);
8✔
399
   }
400
#endif
401

402
   if(group_params.is_kem()) {
×
403
      throw TLS_Exception(Alert::IllegalParameter, "cannot generate an ephemeral KEX key for a KEM");
×
404
   }
405

406
   throw TLS_Exception(Alert::DecodeError, "cannot create a key offering without a group definition");
×
407
}
408

409
std::unique_ptr<PK_Key_Agreement_Key> TLS::Callbacks::tls12_generate_ephemeral_ecdh_key(
38✔
410
   TLS::Group_Params group, RandomNumberGenerator& rng, EC_Point_Format tls12_ecc_pubkey_encoding_format) {
411
   // Delegating to the "universal" callback to obtain an ECDH key pair
412
   auto key = tls_generate_ephemeral_key(group, rng);
38✔
413

414
   // For ordinary ECDH key pairs (that are derived from `ECDH_PublicKey`), we
415
   // set the internal point encoding flag for the key before passing it on into
416
   // the TLS 1.2 implementation. For user-defined keypair types (e.g. to
417
   // offload to some crypto hardware) inheriting from Botan's `ECDH_PublicKey`
418
   // might not be feasible. Such users should consider overriding this
419
   // ECDH-specific callback and ensure that their custom class handles the
420
   // public point encoding as requested by `tls12_ecc_pubkey_encoding_format`.
421
   if(auto* ecc_key = dynamic_cast<ECDH_PublicKey*>(key.get())) {
38✔
422
      ecc_key->set_point_encoding(tls12_ecc_pubkey_encoding_format);
38✔
423
   }
424

425
   return key;
38✔
426
}
×
427

428
secure_vector<uint8_t> TLS::Callbacks::tls_ephemeral_key_agreement(
2,334✔
429
   const std::variant<TLS::Group_Params, DL_Group>& group,
430
   const PK_Key_Agreement_Key& private_key,
431
   const std::vector<uint8_t>& public_value,
432
   RandomNumberGenerator& rng,
433
   const Policy& policy) {
434
   const auto kex_pub_key = [&]() {
6,958✔
435
      try {
2,334✔
436
         return tls_deserialize_peer_public_key(group, public_value);
2,334✔
437
      } catch(const Decoding_Error& ex) {
44✔
438
         // This exception means that the public key was invalid. However,
439
         // TLS' DecodeError would imply that a protocol message was invalid.
440
         throw TLS_Exception(Alert::IllegalParameter, ex.what());
44✔
441
      } catch(const Invalid_Argument& ex) {
44✔
442
         throw TLS_Exception(Alert::IllegalParameter, ex.what());
×
443
      }
×
444
   }();
2,334✔
445

446
   BOTAN_ASSERT_NONNULL(kex_pub_key);
2,290✔
447
   policy.check_peer_key_acceptable(*kex_pub_key);
2,290✔
448

449
   // RFC 8422 - 5.11.
450
   //   With X25519 and X448, a receiving party MUST check whether the
451
   //   computed premaster secret is the all-zero value and abort the
452
   //   handshake if so, as described in Section 6 of [RFC7748].
453
   //
454
   // This is done within the key agreement operation and throws
455
   // an Invalid_Argument exception if the shared secret is all-zero.
456
   try {
2,290✔
457
      const PK_Key_Agreement ka(private_key, rng, "Raw");
2,290✔
458
      return ka.derive_key(0, kex_pub_key->raw_public_key_bits()).bits_of();
6,866✔
459
   } catch(const Invalid_Argument& ex) {
2,294✔
460
      throw TLS_Exception(Alert::IllegalParameter, ex.what());
4✔
461
   }
4✔
462
}
2,286✔
463

464
void TLS::Callbacks::tls_session_established(const Session_Summary& session) {
72✔
465
   BOTAN_UNUSED(session);
72✔
466
}
72✔
467

468
std::vector<uint8_t> TLS::Callbacks::tls_provide_cert_status(const std::vector<X509_Certificate>& chain,
112✔
469
                                                             const Certificate_Status_Request& csr) {
470
   BOTAN_UNUSED(chain, csr);
112✔
471
   return std::vector<uint8_t>();
112✔
472
}
473

474
void TLS::Callbacks::tls_log_error(const char* err) {
×
475
   BOTAN_UNUSED(err);
×
476
}
×
477

478
void TLS::Callbacks::tls_log_debug(const char* what) {
×
479
   BOTAN_UNUSED(what);
×
480
}
×
481

482
void TLS::Callbacks::tls_log_debug_bin(const char* descr, const uint8_t val[], size_t val_len) {
×
483
   BOTAN_UNUSED(descr, val, val_len);
×
484
}
×
485

486
void TLS::Callbacks::tls_ssl_key_log_data(std::string_view label,
×
487
                                          std::span<const uint8_t> client_random,
488
                                          std::span<const uint8_t> secret) const {
489
   BOTAN_UNUSED(label, client_random, secret);
×
490
}
×
491

492
std::unique_ptr<KDF> TLS::Callbacks::tls12_protocol_specific_kdf(std::string_view prf_algo) const {
6,172✔
493
   if(prf_algo == "MD5" || prf_algo == "SHA-1") {
6,869✔
494
      return KDF::create_or_throw("TLS-12-PRF(SHA-256)");
697✔
495
   }
496

497
   return KDF::create_or_throw(Botan::fmt("TLS-12-PRF({})", prf_algo));
10,950✔
498
}
499

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

© 2026 Coveralls, Inc