Coverage Report

Created: 2025-04-11 06:34

/src/botan/src/lib/tls/tls_callbacks.cpp
Line
Count
Source (jump to first uncovered line)
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/ecdh.h>
16
#include <botan/ocsp.h>
17
#include <botan/pk_algs.h>
18
#include <botan/tls_algos.h>
19
#include <botan/tls_exceptn.h>
20
#include <botan/tls_policy.h>
21
#include <botan/x509path.h>
22
#include <botan/internal/ct_utils.h>
23
#include <botan/internal/stl_util.h>
24
25
#if defined(BOTAN_HAS_X25519)
26
   #include <botan/x25519.h>
27
#endif
28
29
#if defined(BOTAN_HAS_X448)
30
   #include <botan/x448.h>
31
#endif
32
33
#if defined(BOTAN_HAS_ML_KEM)
34
   #include <botan/ml_kem.h>
35
#endif
36
37
#if defined(BOTAN_HAS_FRODOKEM)
38
   #include <botan/frodokem.h>
39
#endif
40
41
#if defined(BOTAN_HAS_TLS_13_PQC)
42
   #include <botan/internal/hybrid_public_key.h>
43
#endif
44
45
namespace Botan {
46
47
103k
void TLS::Callbacks::tls_inspect_handshake_msg(const Handshake_Message& /*unused*/) {
48
   // default is no op
49
103k
}
50
51
0
std::string TLS::Callbacks::tls_server_choose_app_protocol(const std::vector<std::string>& /*unused*/) {
52
0
   return "";
53
0
}
54
55
5.23k
std::string TLS::Callbacks::tls_peer_network_identity() {
56
5.23k
   return "";
57
5.23k
}
58
59
48.3k
std::chrono::system_clock::time_point TLS::Callbacks::tls_current_timestamp() {
60
48.3k
   return std::chrono::system_clock::now();
61
48.3k
}
62
63
void TLS::Callbacks::tls_modify_extensions(Extensions& /*unused*/,
64
                                           Connection_Side /*unused*/,
65
24.0k
                                           Handshake_Type /*unused*/) {}
66
67
void TLS::Callbacks::tls_examine_extensions(const Extensions& /*unused*/,
68
                                            Connection_Side /*unused*/,
69
21.4k
                                            Handshake_Type /*unused*/) {}
70
71
244
bool TLS::Callbacks::tls_should_persist_resumption_information(const Session& session) {
72
   // RFC 5077 3.3
73
   //    The ticket_lifetime_hint field contains a hint from the server about
74
   //    how long the ticket should be stored. A value of zero is reserved to
75
   //    indicate that the lifetime of the ticket is unspecified.
76
   //
77
   // RFC 8446 4.6.1
78
   //    [A ticket_lifetime] of zero indicates that the ticket should be discarded
79
   //    immediately.
80
   //
81
   // By default we opt to keep all sessions, except for TLS 1.3 with a lifetime
82
   // hint of zero.
83
244
   return session.lifetime_hint().count() > 0 || session.version().is_pre_tls_13();
84
244
}
85
86
void TLS::Callbacks::tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain,
87
                                           const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
88
                                           const std::vector<Certificate_Store*>& trusted_roots,
89
                                           Usage_Type usage,
90
                                           std::string_view hostname,
91
0
                                           const TLS::Policy& policy) {
92
0
   if(cert_chain.empty()) {
93
0
      throw Invalid_Argument("Certificate chain was empty");
94
0
   }
95
96
0
   Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(),
97
0
                                             policy.minimum_signature_strength());
98
99
0
   Path_Validation_Result result = x509_path_validate(cert_chain,
100
0
                                                      restrictions,
101
0
                                                      trusted_roots,
102
0
                                                      hostname,
103
0
                                                      usage,
104
0
                                                      tls_current_timestamp(),
105
0
                                                      tls_verify_cert_chain_ocsp_timeout(),
106
0
                                                      ocsp_responses);
107
108
0
   if(!result.successful_validation()) {
109
0
      throw TLS_Exception(Alert::BadCertificate, "Certificate validation failure: " + result.result_string());
110
0
   }
111
0
}
112
113
void TLS::Callbacks::tls_verify_raw_public_key(const Public_Key& raw_public_key,
114
                                               Usage_Type usage,
115
                                               std::string_view hostname,
116
0
                                               const TLS::Policy& policy) {
117
0
   BOTAN_UNUSED(raw_public_key, usage, hostname, policy);
118
   // There is no good default implementation for authenticating raw public key.
119
   // Applications that wish to use them for authentication, must override this.
120
0
   throw TLS_Exception(Alert::CertificateUnknown, "Application did not provide a means to validate the raw public key");
121
0
}
122
123
0
std::optional<OCSP::Response> TLS::Callbacks::tls_parse_ocsp_response(const std::vector<uint8_t>& raw_response) {
124
0
   try {
125
0
      return OCSP::Response(raw_response);
126
0
   } catch(const Decoding_Error&) {
127
      // ignore parsing errors and just ignore the broken OCSP response
128
0
      return std::nullopt;
129
0
   }
130
0
}
131
132
std::vector<std::vector<uint8_t>> TLS::Callbacks::tls_provide_cert_chain_status(
133
0
   const std::vector<X509_Certificate>& chain, const Certificate_Status_Request& csr) {
134
0
   std::vector<std::vector<uint8_t>> result(chain.size());
135
0
   if(!chain.empty()) {
136
0
      result[0] = tls_provide_cert_status(chain, csr);
137
0
   }
138
0
   return result;
139
0
}
140
141
std::vector<uint8_t> TLS::Callbacks::tls_sign_message(const Private_Key& key,
142
                                                      RandomNumberGenerator& rng,
143
                                                      std::string_view padding,
144
                                                      Signature_Format format,
145
0
                                                      const std::vector<uint8_t>& msg) {
146
0
   PK_Signer signer(key, rng, padding, format);
147
148
0
   return signer.sign_message(msg, rng);
149
0
}
150
151
bool TLS::Callbacks::tls_verify_message(const Public_Key& key,
152
                                        std::string_view padding,
153
                                        Signature_Format format,
154
                                        const std::vector<uint8_t>& msg,
155
0
                                        const std::vector<uint8_t>& sig) {
156
0
   PK_Verifier verifier(key, padding, format);
157
158
0
   return verifier.verify_message(msg, sig);
159
0
}
160
161
namespace {
162
163
17.6k
bool is_dh_group(const std::variant<TLS::Group_Params, DL_Group>& group) {
164
17.6k
   return std::holds_alternative<DL_Group>(group) || std::get<TLS::Group_Params>(group).is_dh_named_group();
165
17.6k
}
166
167
0
DL_Group get_dl_group(const std::variant<TLS::Group_Params, DL_Group>& group) {
168
0
   BOTAN_ASSERT_NOMSG(is_dh_group(group));
169
170
   // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of
171
   // a standardized DH group identifier. TLS 1.3 just offers pre-defined
172
   // groups.
173
0
   return std::visit(
174
0
      overloaded{[](const DL_Group& dl_group) { return dl_group; },
175
0
                 [&](TLS::Group_Params group_param) { return DL_Group::from_name(group_param.to_string().value()); }},
176
0
      group);
177
0
}
178
179
}  // namespace
180
181
std::unique_ptr<Public_Key> TLS::Callbacks::tls_deserialize_peer_public_key(
182
2.51k
   const std::variant<TLS::Group_Params, DL_Group>& group, std::span<const uint8_t> key_bits) {
183
2.51k
   if(is_dh_group(group)) {
184
      // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of
185
      // a standardized DH group identifier.
186
0
      const auto dl_group = get_dl_group(group);
187
188
0
      auto Y = BigInt::from_bytes(key_bits);
189
190
      /*
191
       * A basic check for key validity. As we do not know q here we
192
       * cannot check that Y is in the right subgroup. However since
193
       * our key is ephemeral there does not seem to be any
194
       * advantage to bogus keys anyway.
195
       */
196
0
      if(Y <= 1 || Y >= dl_group.get_p() - 1) {
197
0
         throw Decoding_Error("Server sent bad DH key for DHE exchange");
198
0
      }
199
200
0
      return std::make_unique<DH_PublicKey>(dl_group, Y);
201
0
   }
202
203
   // The special case for TLS 1.2 with an explicit DH group definition is
204
   // handled above. All other cases are based on the opaque group definition.
205
2.51k
   BOTAN_ASSERT_NOMSG(std::holds_alternative<TLS::Group_Params>(group));
206
2.51k
   const auto group_params = std::get<TLS::Group_Params>(group);
207
208
2.51k
   if(group_params.is_ecdh_named_curve()) {
209
1.90k
      const auto ec_group = EC_Group::from_name(group_params.to_string().value());
210
1.90k
      return std::make_unique<ECDH_PublicKey>(ec_group, EC_AffinePoint(ec_group, key_bits));
211
1.90k
   }
212
213
612
#if defined(BOTAN_HAS_X25519)
214
612
   if(group_params.is_x25519()) {
215
9
      return std::make_unique<X25519_PublicKey>(key_bits);
216
9
   }
217
603
#endif
218
219
603
#if defined(BOTAN_HAS_X448)
220
603
   if(group_params.is_x448()) {
221
603
      return std::make_unique<X448_PublicKey>(key_bits);
222
603
   }
223
0
#endif
224
225
0
#if defined(BOTAN_HAS_TLS_13_PQC)
226
0
   if(group_params.is_pqc_hybrid()) {
227
0
      return Hybrid_KEM_PublicKey::load_for_group(group_params, key_bits);
228
0
   }
229
0
#endif
230
231
0
#if defined(BOTAN_HAS_ML_KEM)
232
0
   if(group_params.is_pure_ml_kem()) {
233
0
      return std::make_unique<ML_KEM_PublicKey>(key_bits, ML_KEM_Mode(group_params.to_string().value()));
234
0
   }
235
0
#endif
236
237
0
#if defined(BOTAN_HAS_FRODOKEM)
238
0
   if(group_params.is_pure_frodokem()) {
239
0
      return std::make_unique<FrodoKEM_PublicKey>(key_bits, FrodoKEMMode(group_params.to_string().value()));
240
0
   }
241
0
#endif
242
243
0
   throw Decoding_Error("cannot create a key offering without a group definition");
244
0
}
245
246
0
std::unique_ptr<Private_Key> TLS::Callbacks::tls_kem_generate_key(TLS::Group_Params group, RandomNumberGenerator& rng) {
247
0
#if defined(BOTAN_HAS_ML_KEM)
248
0
   if(group.is_pure_ml_kem()) {
249
0
      return std::make_unique<ML_KEM_PrivateKey>(rng, ML_KEM_Mode(group.to_string().value()));
250
0
   }
251
0
#endif
252
253
0
#if defined(BOTAN_HAS_FRODOKEM)
254
0
   if(group.is_pure_frodokem()) {
255
0
      return std::make_unique<FrodoKEM_PrivateKey>(rng, FrodoKEMMode(group.to_string().value()));
256
0
   }
257
0
#endif
258
259
0
#if defined(BOTAN_HAS_TLS_13_PQC)
260
0
   if(group.is_pqc_hybrid()) {
261
0
      return Hybrid_KEM_PrivateKey::generate_from_group(group, rng);
262
0
   }
263
0
#endif
264
265
0
   return tls_generate_ephemeral_key(group, rng);
266
0
}
267
268
KEM_Encapsulation TLS::Callbacks::tls_kem_encapsulate(TLS::Group_Params group,
269
                                                      const std::vector<uint8_t>& encoded_public_key,
270
                                                      RandomNumberGenerator& rng,
271
0
                                                      const Policy& policy) {
272
0
   if(group.is_kem()) {
273
0
      auto kem_pub_key = [&] {
274
0
         try {
275
0
            return tls_deserialize_peer_public_key(group, encoded_public_key);
276
0
         } catch(const Decoding_Error& ex) {
277
            // This exception means that the public key was invalid. However,
278
            // TLS' DecodeError would imply that a protocol message was invalid.
279
0
            throw TLS_Exception(Alert::IllegalParameter, ex.what());
280
0
         }
281
0
      }();
282
283
0
      BOTAN_ASSERT_NONNULL(kem_pub_key);
284
0
      policy.check_peer_key_acceptable(*kem_pub_key);
285
286
0
      try {
287
0
         return PK_KEM_Encryptor(*kem_pub_key, "Raw").encrypt(rng);
288
0
      } catch(const Invalid_Argument& ex) {
289
0
         throw TLS_Exception(Alert::IllegalParameter, ex.what());
290
0
      }
291
0
   } else {
292
      // TODO: We could use the KEX_to_KEM_Adapter to remove the case distinction
293
      //       of KEM and KEX. However, the workarounds in this adapter class
294
      //       should first be addressed.
295
0
      auto ephemeral_keypair = tls_generate_ephemeral_key(group, rng);
296
0
      BOTAN_ASSERT_NONNULL(ephemeral_keypair);
297
0
      return {ephemeral_keypair->public_value(),
298
0
              tls_ephemeral_key_agreement(group, *ephemeral_keypair, encoded_public_key, rng, policy)};
299
0
   }
300
0
}
301
302
secure_vector<uint8_t> TLS::Callbacks::tls_kem_decapsulate(TLS::Group_Params group,
303
                                                           const Private_Key& private_key,
304
                                                           const std::vector<uint8_t>& encapsulated_bytes,
305
                                                           RandomNumberGenerator& rng,
306
0
                                                           const Policy& policy) {
307
0
   if(group.is_kem()) {
308
0
      PK_KEM_Decryptor kemdec(private_key, rng, "Raw");
309
0
      if(encapsulated_bytes.size() != kemdec.encapsulated_key_length()) {
310
0
         throw TLS_Exception(Alert::IllegalParameter, "Invalid encapsulated key length");
311
0
      }
312
0
      return kemdec.decrypt(encapsulated_bytes, 0, {});
313
0
   }
314
315
0
   try {
316
0
      auto& key_agreement_key = dynamic_cast<const PK_Key_Agreement_Key&>(private_key);
317
0
      return tls_ephemeral_key_agreement(group, key_agreement_key, encapsulated_bytes, rng, policy);
318
0
   } catch(const std::bad_cast&) {
319
0
      throw Invalid_Argument("provided ephemeral key is not a PK_Key_Agreement_Key");
320
0
   }
321
0
}
322
323
std::unique_ptr<PK_Key_Agreement_Key> TLS::Callbacks::tls_generate_ephemeral_key(
324
15.1k
   const std::variant<TLS::Group_Params, DL_Group>& group, RandomNumberGenerator& rng) {
325
15.1k
   if(is_dh_group(group)) {
326
0
      const DL_Group dl_group = get_dl_group(group);
327
0
      return std::make_unique<DH_PrivateKey>(rng, dl_group);
328
0
   }
329
330
15.1k
   BOTAN_ASSERT_NOMSG(std::holds_alternative<TLS::Group_Params>(group));
331
15.1k
   const auto group_params = std::get<TLS::Group_Params>(group);
332
333
15.1k
   if(group_params.is_ecdh_named_curve()) {
334
12.8k
      const auto ec_group = EC_Group::from_name(group_params.to_string().value());
335
12.8k
      return std::make_unique<ECDH_PrivateKey>(rng, ec_group);
336
12.8k
   }
337
338
2.31k
#if defined(BOTAN_HAS_X25519)
339
2.31k
   if(group_params.is_x25519()) {
340
575
      return std::make_unique<X25519_PrivateKey>(rng);
341
575
   }
342
1.74k
#endif
343
344
1.74k
#if defined(BOTAN_HAS_X448)
345
1.74k
   if(group_params.is_x448()) {
346
1.74k
      return std::make_unique<X448_PrivateKey>(rng);
347
1.74k
   }
348
0
#endif
349
350
0
   if(group_params.is_kem()) {
351
0
      throw TLS_Exception(Alert::IllegalParameter, "cannot generate an ephemeral KEX key for a KEM");
352
0
   }
353
354
0
   throw TLS_Exception(Alert::DecodeError, "cannot create a key offering without a group definition");
355
0
}
356
357
secure_vector<uint8_t> TLS::Callbacks::tls_ephemeral_key_agreement(
358
   const std::variant<TLS::Group_Params, DL_Group>& group,
359
   const PK_Key_Agreement_Key& private_key,
360
   const std::vector<uint8_t>& public_value,
361
   RandomNumberGenerator& rng,
362
2.51k
   const Policy& policy) {
363
2.51k
   const auto kex_pub_key = [&]() {
364
2.51k
      try {
365
2.51k
         return tls_deserialize_peer_public_key(group, public_value);
366
2.51k
      } catch(const Decoding_Error& ex) {
367
         // This exception means that the public key was invalid. However,
368
         // TLS' DecodeError would imply that a protocol message was invalid.
369
178
         throw TLS_Exception(Alert::IllegalParameter, ex.what());
370
178
      }
371
2.51k
   }();
372
373
2.51k
   BOTAN_ASSERT_NONNULL(kex_pub_key);
374
2.51k
   policy.check_peer_key_acceptable(*kex_pub_key);
375
376
   // RFC 8422 - 5.11.
377
   //   With X25519 and X448, a receiving party MUST check whether the
378
   //   computed premaster secret is the all-zero value and abort the
379
   //   handshake if so, as described in Section 6 of [RFC7748].
380
   //
381
   // This is done within the key agreement operation and throws
382
   // an Invalid_Argument exception if the shared secret is all-zero.
383
2.51k
   try {
384
2.51k
      PK_Key_Agreement ka(private_key, rng, "Raw");
385
2.51k
      return ka.derive_key(0, kex_pub_key->raw_public_key_bits()).bits_of();
386
2.51k
   } catch(const Invalid_Argument& ex) {
387
2
      throw TLS_Exception(Alert::IllegalParameter, ex.what());
388
2
   }
389
2.51k
}
390
391
244
void TLS::Callbacks::tls_session_established(const Session_Summary& session) {
392
244
   BOTAN_UNUSED(session);
393
244
}
394
395
std::vector<uint8_t> TLS::Callbacks::tls_provide_cert_status(const std::vector<X509_Certificate>& chain,
396
1
                                                             const Certificate_Status_Request& csr) {
397
1
   BOTAN_UNUSED(chain, csr);
398
1
   return std::vector<uint8_t>();
399
1
}
400
401
0
void TLS::Callbacks::tls_log_error(const char* err) {
402
0
   BOTAN_UNUSED(err);
403
0
}
404
405
0
void TLS::Callbacks::tls_log_debug(const char* what) {
406
0
   BOTAN_UNUSED(what);
407
0
}
408
409
0
void TLS::Callbacks::tls_log_debug_bin(const char* descr, const uint8_t val[], size_t val_len) {
410
0
   BOTAN_UNUSED(descr, val, val_len);
411
0
}
412
413
void TLS::Callbacks::tls_ssl_key_log_data(std::string_view label,
414
                                          std::span<const uint8_t> client_random,
415
0
                                          std::span<const uint8_t> secret) const {
416
0
   BOTAN_UNUSED(label, client_random, secret);
417
0
}
418
419
}  // namespace Botan