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

randombit / botan / 11380319839

17 Oct 2024 07:26AM UTC coverage: 91.126% (+0.007%) from 91.119%
11380319839

push

github

web-flow
Merge pull request #4384 from Rohde-Schwarz/refactor/x25519_x448_zero_rejection

Refactor: Centralize X25519/X448 all-zero result rejection

91004 of 99866 relevant lines covered (91.13%)

9508010.62 hits per line

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

82.43
/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/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_KYBER)
34
   #include <botan/kyber.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
void TLS::Callbacks::tls_inspect_handshake_msg(const Handshake_Message& /*unused*/) {
5,772✔
48
   // default is no op
49
}
5,772✔
50

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

55
std::string TLS::Callbacks::tls_peer_network_identity() {
738✔
56
   return "";
738✔
57
}
58

59
std::chrono::system_clock::time_point TLS::Callbacks::tls_current_timestamp() {
5,119✔
60
   return std::chrono::system_clock::now();
5,119✔
61
}
62

63
void TLS::Callbacks::tls_modify_extensions(Extensions& /*unused*/,
2,158✔
64
                                           Connection_Side /*unused*/,
65
                                           Handshake_Type /*unused*/) {}
2,158✔
66

67
void TLS::Callbacks::tls_examine_extensions(const Extensions& /*unused*/,
4,150✔
68
                                            Connection_Side /*unused*/,
69
                                            Handshake_Type /*unused*/) {}
4,150✔
70

71
bool TLS::Callbacks::tls_should_persist_resumption_information(const Session& session) {
2,563✔
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
   return session.lifetime_hint().count() > 0 || session.version().is_pre_tls_13();
2,563✔
84
}
85

86
void TLS::Callbacks::tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain,
105✔
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
                                           const TLS::Policy& policy) {
92
   if(cert_chain.empty()) {
105✔
93
      throw Invalid_Argument("Certificate chain was empty");
×
94
   }
95

96
   Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(),
105✔
97
                                             policy.minimum_signature_strength());
315✔
98

99
   Path_Validation_Result result = x509_path_validate(cert_chain,
105✔
100
                                                      restrictions,
101
                                                      trusted_roots,
102
                                                      hostname,
103
                                                      usage,
104
                                                      tls_current_timestamp(),
105✔
105
                                                      tls_verify_cert_chain_ocsp_timeout(),
105✔
106
                                                      ocsp_responses);
105✔
107

108
   if(!result.successful_validation()) {
105✔
109
      throw TLS_Exception(Alert::BadCertificate, "Certificate validation failure: " + result.result_string());
×
110
   }
111
}
105✔
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
                                               const TLS::Policy& policy) {
117
   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
   throw TLS_Exception(Alert::CertificateUnknown, "Application did not provide a means to validate the raw public key");
×
121
}
122

123
std::optional<OCSP::Response> TLS::Callbacks::tls_parse_ocsp_response(const std::vector<uint8_t>& raw_response) {
×
124
   try {
×
125
      return OCSP::Response(raw_response);
×
126
   } catch(const Decoding_Error&) {
×
127
      // ignore parsing errors and just ignore the broken OCSP response
128
      return std::nullopt;
×
129
   }
×
130
}
131

132
std::vector<std::vector<uint8_t>> TLS::Callbacks::tls_provide_cert_chain_status(
252✔
133
   const std::vector<X509_Certificate>& chain, const Certificate_Status_Request& csr) {
134
   std::vector<std::vector<uint8_t>> result(chain.size());
252✔
135
   if(!chain.empty()) {
252✔
136
      result[0] = tls_provide_cert_status(chain, csr);
252✔
137
   }
138
   return result;
244✔
139
}
8✔
140

141
std::vector<uint8_t> TLS::Callbacks::tls_sign_message(const Private_Key& key,
923✔
142
                                                      RandomNumberGenerator& rng,
143
                                                      std::string_view padding,
144
                                                      Signature_Format format,
145
                                                      const std::vector<uint8_t>& msg) {
146
   PK_Signer signer(key, rng, padding, format);
923✔
147

148
   return signer.sign_message(msg, rng);
1,845✔
149
}
923✔
150

151
bool TLS::Callbacks::tls_verify_message(const Public_Key& key,
1,164✔
152
                                        std::string_view padding,
153
                                        Signature_Format format,
154
                                        const std::vector<uint8_t>& msg,
155
                                        const std::vector<uint8_t>& sig) {
156
   PK_Verifier verifier(key, padding, format);
1,164✔
157

158
   return verifier.verify_message(msg, sig);
2,328✔
159
}
1,164✔
160

161
std::unique_ptr<Private_Key> TLS::Callbacks::tls_kem_generate_key(TLS::Group_Params group, RandomNumberGenerator& rng) {
1,014✔
162
#if defined(BOTAN_HAS_KYBER)
163
   if(group.is_pure_kyber()) {
1,014✔
164
      return std::make_unique<Kyber_PrivateKey>(rng, KyberMode(group.to_string().value()));
3✔
165
   }
166
#endif
167

168
#if defined(BOTAN_HAS_FRODOKEM)
169
   if(group.is_pure_frodokem()) {
1,013✔
170
      return std::make_unique<FrodoKEM_PrivateKey>(rng, FrodoKEMMode(group.to_string().value()));
×
171
   }
172
#endif
173

174
#if defined(BOTAN_HAS_TLS_13_PQC)
175
   if(group.is_pqc_hybrid()) {
1,013✔
176
      return Hybrid_KEM_PrivateKey::generate_from_group(group, rng);
3✔
177
   }
178
#endif
179

180
   return tls_generate_ephemeral_key(group, rng);
3,030✔
181
}
182

183
KEM_Encapsulation TLS::Callbacks::tls_kem_encapsulate(TLS::Group_Params group,
368✔
184
                                                      const std::vector<uint8_t>& encoded_public_key,
185
                                                      RandomNumberGenerator& rng,
186
                                                      const Policy& policy) {
187
   if(group.is_kem()) {
368✔
188
      auto kem_pub_key = [&]() -> std::unique_ptr<Public_Key> {
9✔
189

190
#if defined(BOTAN_HAS_TLS_13_PQC)
191
         if(group.is_pqc_hybrid()) {
3✔
192
            return Hybrid_KEM_PublicKey::load_for_group(group, encoded_public_key);
4✔
193
         }
194
#endif
195

196
#if defined(BOTAN_HAS_KYBER)
197
         if(group.is_pure_kyber()) {
1✔
198
            return std::make_unique<Kyber_PublicKey>(encoded_public_key, KyberMode(group.to_string().value()));
4✔
199
         }
200
#endif
201

202
#if defined(BOTAN_HAS_FRODOKEM)
203
         if(group.is_pure_frodokem()) {
×
204
            return std::make_unique<FrodoKEM_PublicKey>(encoded_public_key, FrodoKEMMode(group.to_string().value()));
×
205
         }
206
#endif
207

208
         throw TLS_Exception(Alert::IllegalParameter, "KEM is not supported");
×
209
      }();
3✔
210

211
      return PK_KEM_Encryptor(*kem_pub_key, "Raw").encrypt(rng);
3✔
212
   }
3✔
213

214
   // TODO: We could use the KEX_to_KEM_Adapter to remove the case distinction
215
   //       of KEM and KEX. However, the workarounds in this adapter class
216
   //       should first be addressed.
217
   auto ephemeral_keypair = tls_generate_ephemeral_key(group, rng);
365✔
218
   return KEM_Encapsulation(ephemeral_keypair->public_value(),
364✔
219
                            tls_ephemeral_key_agreement(group, *ephemeral_keypair, encoded_public_key, rng, policy));
1,094✔
220
}
364✔
221

222
secure_vector<uint8_t> TLS::Callbacks::tls_kem_decapsulate(TLS::Group_Params group,
429✔
223
                                                           const Private_Key& private_key,
224
                                                           const std::vector<uint8_t>& encapsulated_bytes,
225
                                                           RandomNumberGenerator& rng,
226
                                                           const Policy& policy) {
227
   if(group.is_kem()) {
429✔
228
      PK_KEM_Decryptor kemdec(private_key, rng, "Raw");
4✔
229
      return kemdec.decrypt(encapsulated_bytes, 0, {});
4✔
230
   }
4✔
231

232
   try {
425✔
233
      auto& key_agreement_key = dynamic_cast<const PK_Key_Agreement_Key&>(private_key);
425✔
234
      return tls_ephemeral_key_agreement(group, key_agreement_key, encapsulated_bytes, rng, policy);
850✔
235
   } catch(const std::bad_cast&) {
1✔
236
      throw Invalid_Argument("provided ephemeral key is not a PK_Key_Agreement_Key");
×
237
   }
×
238
}
239

240
namespace {
241

242
bool is_dh_group(const std::variant<TLS::Group_Params, DL_Group>& group) {
4,644✔
243
   return std::holds_alternative<DL_Group>(group) || std::get<TLS::Group_Params>(group).is_dh_named_group();
9,268✔
244
}
245

246
DL_Group get_dl_group(const std::variant<TLS::Group_Params, DL_Group>& group) {
24✔
247
   BOTAN_ASSERT_NOMSG(is_dh_group(group));
24✔
248

249
   // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of
250
   // a standardized DH group identifier. TLS 1.3 just offers pre-defined
251
   // groups.
252
   return std::visit(
24✔
253
      overloaded{[](const DL_Group& dl_group) { return dl_group; },
10✔
254
                 [&](TLS::Group_Params group_param) { return DL_Group(group_param.to_string().value()); }},
42✔
255
      group);
24✔
256
}
257

258
}  // namespace
259

260
std::unique_ptr<PK_Key_Agreement_Key> TLS::Callbacks::tls_generate_ephemeral_key(
2,610✔
261
   const std::variant<TLS::Group_Params, DL_Group>& group, RandomNumberGenerator& rng) {
262
   if(is_dh_group(group)) {
2,610✔
263
      const DL_Group dl_group = get_dl_group(group);
14✔
264
      return std::make_unique<DH_PrivateKey>(rng, dl_group);
28✔
265
   }
14✔
266

267
   BOTAN_ASSERT_NOMSG(std::holds_alternative<TLS::Group_Params>(group));
2,596✔
268
   const auto group_params = std::get<TLS::Group_Params>(group);
2,596✔
269

270
   if(group_params.is_ecdh_named_curve()) {
2,596✔
271
      const auto ec_group = EC_Group::from_name(group_params.to_string().value());
172✔
272
      return std::make_unique<ECDH_PrivateKey>(rng, ec_group);
172✔
273
   }
86✔
274

275
#if defined(BOTAN_HAS_X25519)
276
   if(group_params.is_x25519()) {
2,510✔
277
      return std::make_unique<X25519_PrivateKey>(rng);
5,012✔
278
   }
279
#endif
280

281
#if defined(BOTAN_HAS_X448)
282
   if(group_params.is_x448()) {
4✔
283
      return std::make_unique<X448_PrivateKey>(rng);
8✔
284
   }
285
#endif
286

287
   if(group_params.is_kem()) {
×
288
      throw TLS_Exception(Alert::IllegalParameter, "cannot generate an ephemeral KEX key for a KEM");
×
289
   }
290

291
   throw TLS_Exception(Alert::DecodeError, "cannot create a key offering without a group definition");
×
292
}
293

294
secure_vector<uint8_t> TLS::Callbacks::tls_ephemeral_key_agreement(
2,010✔
295
   const std::variant<TLS::Group_Params, DL_Group>& group,
296
   const PK_Key_Agreement_Key& private_key,
297
   const std::vector<uint8_t>& public_value,
298
   RandomNumberGenerator& rng,
299
   const Policy& policy) {
300
   auto agree = [&](const PK_Key_Agreement_Key& sk, const auto& pk) {
4,016✔
301
      PK_Key_Agreement ka(sk, rng, "Raw");
2,006✔
302
      return ka.derive_key(0, pk.public_value()).bits_of();
8,024✔
303
   };
4,016✔
304

305
   if(is_dh_group(group)) {
2,010✔
306
      // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of
307
      // a standardized DH group identifier.
308
      const auto dl_group = get_dl_group(group);
10✔
309

310
      auto Y = BigInt::from_bytes(public_value);
10✔
311

312
      /*
313
       * A basic check for key validity. As we do not know q here we
314
       * cannot check that Y is in the right subgroup. However since
315
       * our key is ephemeral there does not seem to be any
316
       * advantage to bogus keys anyway.
317
       */
318
      if(Y <= 1 || Y >= dl_group.get_p() - 1) {
20✔
319
         throw TLS_Exception(Alert::IllegalParameter, "Server sent bad DH key for DHE exchange");
×
320
      }
321

322
      DH_PublicKey peer_key(dl_group, Y);
10✔
323
      policy.check_peer_key_acceptable(peer_key);
10✔
324

325
      return agree(private_key, peer_key);
10✔
326
   }
30✔
327

328
   BOTAN_ASSERT_NOMSG(std::holds_alternative<TLS::Group_Params>(group));
2,000✔
329
   const auto group_params = std::get<TLS::Group_Params>(group);
2,000✔
330

331
   if(group_params.is_ecdh_named_curve()) {
2,000✔
332
      const auto ec_group = EC_Group::from_name(group_params.to_string().value());
128✔
333
      ECDH_PublicKey peer_key(ec_group, ec_group.OS2ECP(public_value));
64✔
334
      policy.check_peer_key_acceptable(peer_key);
60✔
335

336
      return agree(private_key, peer_key);
60✔
337
   }
64✔
338

339
#if defined(BOTAN_HAS_X25519)
340
   if(group_params.is_x25519()) {
1,936✔
341
      if(public_value.size() != 32) {
1,932✔
342
         throw TLS_Exception(Alert::HandshakeFailure, "Invalid X25519 key size");
×
343
      }
344

345
      X25519_PublicKey peer_key(public_value);
1,932✔
346
      policy.check_peer_key_acceptable(peer_key);
1,932✔
347

348
      // RFC 8422 - 5.11.
349
      //   With X25519 and X448, a receiving party MUST check whether the
350
      //   computed premaster secret is the all-zero value and abort the
351
      //   handshake if so, as described in Section 6 of [RFC7748].
352
      //
353
      // This is done within the key agreement operation and throws
354
      // an Invalid_Argument exception if the shared secret is all-zero.
355
      return agree(private_key, peer_key);
1,932✔
356
   }
1,932✔
357
#endif
358

359
#if defined(BOTAN_HAS_X448)
360
   if(group_params.is_x448()) {
4✔
361
      if(public_value.size() != 56) {
4✔
362
         throw TLS_Exception(Alert::HandshakeFailure, "Invalid X448 key size");
×
363
      }
364

365
      X448_PublicKey peer_key(public_value);
4✔
366
      policy.check_peer_key_acceptable(peer_key);
4✔
367

368
      // RFC 8422 - 5.11.
369
      //   With X25519 and X448, a receiving party MUST check whether the
370
      //   computed premaster secret is the all-zero value and abort the
371
      //   handshake if so, as described in Section 6 of [RFC7748].
372
      //
373
      // This is done within the key agreement operation and throws
374
      // an Invalid_Argument exception if the shared secret is all-zero.
375
      return agree(private_key, peer_key);
4✔
376
   }
4✔
377
#endif
378

379
   throw TLS_Exception(Alert::IllegalParameter, "Did not recognize the key exchange group");
×
380
}
381

382
}  // namespace Botan
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