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

randombit / botan / 6222234799

18 Sep 2023 12:05PM UTC coverage: 91.633% (-0.09%) from 91.726%
6222234799

Pull #3609

github

web-flow
Merge 44df5bbc1 into a8d4d183d
Pull Request #3609: [TLS 1.3] Hybrid PQ/T key establishment

79627 of 86898 relevant lines covered (91.63%)

8488841.32 hits per line

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

85.61
/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_CURVE_25519)
26
   #include <botan/curve25519.h>
27
#endif
28

29
#if defined(BOTAN_HAS_KYBER)
30
   #include <botan/kyber.h>
31
#endif
32

33
#if defined(BOTAN_HAS_TLS_13_PQC)
34
   #include <botan/internal/hybrid_public_key.h>
35
#endif
36

37
namespace Botan {
38

39
void TLS::Callbacks::tls_inspect_handshake_msg(const Handshake_Message& /*unused*/) {
6,234✔
40
   // default is no op
41
}
6,234✔
42

43
std::string TLS::Callbacks::tls_server_choose_app_protocol(const std::vector<std::string>& /*unused*/) {
×
44
   return "";
×
45
}
46

47
std::string TLS::Callbacks::tls_peer_network_identity() {
738✔
48
   return "";
738✔
49
}
50

51
std::chrono::system_clock::time_point TLS::Callbacks::tls_current_timestamp() {
5,079✔
52
   return std::chrono::system_clock::now();
5,079✔
53
}
54

55
void TLS::Callbacks::tls_modify_extensions(Extensions& /*unused*/,
5,610✔
56
                                           Connection_Side /*unused*/,
57
                                           Handshake_Type /*unused*/) {}
5,610✔
58

59
void TLS::Callbacks::tls_examine_extensions(const Extensions& /*unused*/,
4,128✔
60
                                            Connection_Side /*unused*/,
61
                                            Handshake_Type /*unused*/) {}
4,128✔
62

63
bool TLS::Callbacks::tls_should_persist_resumption_information(const Session& session) {
2,556✔
64
   // RFC 5077 3.3
65
   //    The ticket_lifetime_hint field contains a hint from the server about
66
   //    how long the ticket should be stored. A value of zero is reserved to
67
   //    indicate that the lifetime of the ticket is unspecified.
68
   //
69
   // RFC 8446 4.6.1
70
   //    [A ticket_lifetime] of zero indicates that the ticket should be discarded
71
   //    immediately.
72
   //
73
   // By default we opt to keep all sessions, except for TLS 1.3 with a lifetime
74
   // hint of zero.
75
   return session.lifetime_hint().count() > 0 || session.version().is_pre_tls_13();
2,556✔
76
}
77

78
void TLS::Callbacks::tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain,
96✔
79
                                           const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
80
                                           const std::vector<Certificate_Store*>& trusted_roots,
81
                                           Usage_Type usage,
82
                                           std::string_view hostname,
83
                                           const TLS::Policy& policy) {
84
   if(cert_chain.empty()) {
96✔
85
      throw Invalid_Argument("Certificate chain was empty");
×
86
   }
87

88
   Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(),
96✔
89
                                             policy.minimum_signature_strength());
288✔
90

91
   Path_Validation_Result result = x509_path_validate(cert_chain,
96✔
92
                                                      restrictions,
93
                                                      trusted_roots,
94
                                                      (usage == Usage_Type::TLS_SERVER_AUTH ? hostname : ""),
95
                                                      usage,
96
                                                      tls_current_timestamp(),
96✔
97
                                                      tls_verify_cert_chain_ocsp_timeout(),
96✔
98
                                                      ocsp_responses);
99✔
99

100
   if(!result.successful_validation()) {
96✔
101
      throw TLS_Exception(Alert::BadCertificate, "Certificate validation failure: " + result.result_string());
×
102
   }
103
}
96✔
104

105
std::optional<OCSP::Response> TLS::Callbacks::tls_parse_ocsp_response(const std::vector<uint8_t>& raw_response) {
×
106
   try {
×
107
      return OCSP::Response(raw_response);
×
108
   } catch(const Decoding_Error&) {
×
109
      // ignore parsing errors and just ignore the broken OCSP response
110
      return std::nullopt;
×
111
   }
×
112
}
113

114
std::vector<std::vector<uint8_t>> TLS::Callbacks::tls_provide_cert_chain_status(
249✔
115
   const std::vector<X509_Certificate>& chain, const Certificate_Status_Request& csr) {
116
   std::vector<std::vector<uint8_t>> result(chain.size());
249✔
117
   if(!chain.empty()) {
249✔
118
      result[0] = tls_provide_cert_status(chain, csr);
249✔
119
   }
120
   return result;
241✔
121
}
8✔
122

123
std::vector<uint8_t> TLS::Callbacks::tls_sign_message(const Private_Key& key,
914✔
124
                                                      RandomNumberGenerator& rng,
125
                                                      std::string_view padding,
126
                                                      Signature_Format format,
127
                                                      const std::vector<uint8_t>& msg) {
128
   PK_Signer signer(key, rng, padding, format);
914✔
129

130
   return signer.sign_message(msg, rng);
1,827✔
131
}
914✔
132

133
bool TLS::Callbacks::tls_verify_message(const Public_Key& key,
1,131✔
134
                                        std::string_view padding,
135
                                        Signature_Format format,
136
                                        const std::vector<uint8_t>& msg,
137
                                        const std::vector<uint8_t>& sig) {
138
   PK_Verifier verifier(key, padding, format);
1,131✔
139

140
   return verifier.verify_message(msg, sig);
2,262✔
141
}
1,131✔
142

143
std::unique_ptr<Private_Key> TLS::Callbacks::tls_kem_generate_key(TLS::Group_Params group, RandomNumberGenerator& rng) {
1,006✔
144
#if defined(BOTAN_HAS_KYBER)
145
   if(is_pure_kyber(group)) {
1,006✔
146
      return std::make_unique<Kyber_PrivateKey>(rng, KyberMode(group_param_to_string(group)));
1✔
147
   }
148
#endif
149

150
#if defined(BOTAN_HAS_TLS_13_PQC)
151
   if(is_hybrid(group)) {
1,005✔
152
      return Hybrid_KEM_PrivateKey::generate_from_group(group, rng);
3✔
153
   }
154
#endif
155

156
   return tls_generate_ephemeral_key(group, rng);
3,006✔
157
}
158

159
KEM_Encapsulation TLS::Callbacks::tls_kem_encapsulate(TLS::Group_Params group,
363✔
160
                                                      const std::vector<uint8_t>& encoded_public_key,
161
                                                      RandomNumberGenerator& rng,
162
                                                      const Policy& policy) {
163
   if(is_kem(group)) {
363✔
164
      auto kem_pub_key = [&]() -> std::unique_ptr<Public_Key> {
6✔
165

166
#if defined(BOTAN_HAS_TLS_13_PQC)
167
         if(is_hybrid(group)) {
3✔
168
            return Hybrid_KEM_PublicKey::load_for_group(group, encoded_public_key);
4✔
169
         }
170
#endif
171

172
#if defined(BOTAN_HAS_KYBER)
173
         if(is_pure_kyber(group)) {
1✔
174
            return std::make_unique<Kyber_PublicKey>(encoded_public_key, KyberMode(group_param_to_string(group)));
2✔
175
         }
176
#endif
177

178
         throw TLS_Exception(Alert::IllegalParameter, "KEM is not supported");
×
179
      }();
3✔
180

181
      return PK_KEM_Encryptor(*kem_pub_key, "Raw").encrypt(rng);
3✔
182
   }
3✔
183

184
   // TODO: We could use the KEX_to_KEM_Adapter to remove the case distinction
185
   //       of KEM and KEX. However, the workarounds in this adapter class
186
   //       should first be addressed.
187
   auto ephemeral_keypair = tls_generate_ephemeral_key(group, rng);
360✔
188
   return KEM_Encapsulation(ephemeral_keypair->public_value(),
359✔
189
                            tls_ephemeral_key_agreement(group, *ephemeral_keypair, encoded_public_key, rng, policy));
1,079✔
190
}
362✔
191

192
secure_vector<uint8_t> TLS::Callbacks::tls_kem_decapsulate(TLS::Group_Params group,
426✔
193
                                                           const Private_Key& private_key,
194
                                                           const std::vector<uint8_t>& encapsulated_bytes,
195
                                                           RandomNumberGenerator& rng,
196
                                                           const Policy& policy) {
197
   if(is_kem(group)) {
426✔
198
      PK_KEM_Decryptor kemdec(private_key, rng, "Raw");
4✔
199
      return kemdec.decrypt(encapsulated_bytes, 0, {});
4✔
200
   }
4✔
201

202
   try {
422✔
203
      auto& key_agreement_key = dynamic_cast<const PK_Key_Agreement_Key&>(private_key);
422✔
204
      return tls_ephemeral_key_agreement(group, key_agreement_key, encapsulated_bytes, rng, policy);
844✔
205
   } catch(const std::bad_cast&) {
1✔
206
      throw Invalid_Argument("provided ephemeral key is not a PK_Key_Agreement_Key");
×
207
   }
×
208
}
209

210
namespace {
211

212
bool is_dh_group(const std::variant<TLS::Group_Params, DL_Group>& group) {
4,587✔
213
   return std::holds_alternative<DL_Group>(group) || is_dh(std::get<TLS::Group_Params>(group));
9,158✔
214
}
215

216
DL_Group get_dl_group(const std::variant<TLS::Group_Params, DL_Group>& group) {
20✔
217
   BOTAN_ASSERT_NOMSG(is_dh_group(group));
20✔
218

219
   // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of
220
   // a standardized DH group identifier. TLS 1.3 just offers pre-defined
221
   // groups.
222
   return std::visit(
20✔
223
      overloaded{[](const DL_Group& dl_group) { return dl_group; },
8✔
224
                 [&](TLS::Group_Params group_param) { return DL_Group(group_param_to_string(group_param)); }},
24✔
225
      group);
20✔
226
}
227

228
}  // namespace
229

230
std::unique_ptr<PK_Key_Agreement_Key> TLS::Callbacks::tls_generate_ephemeral_key(
2,581✔
231
   const std::variant<TLS::Group_Params, DL_Group>& group, RandomNumberGenerator& rng) {
232
   if(is_dh_group(group)) {
2,581✔
233
      const DL_Group dl_group = get_dl_group(group);
12✔
234
      return std::make_unique<DH_PrivateKey>(rng, dl_group);
24✔
235
   }
12✔
236

237
   BOTAN_ASSERT_NOMSG(std::holds_alternative<TLS::Group_Params>(group));
2,569✔
238
   const auto group_params = std::get<TLS::Group_Params>(group);
2,569✔
239

240
   if(is_ecdh(group_params)) {
2,569✔
241
      const EC_Group ec_group(group_param_to_string(group_params));
83✔
242
      return std::make_unique<ECDH_PrivateKey>(rng, ec_group);
166✔
243
   }
83✔
244

245
#if defined(BOTAN_HAS_CURVE_25519)
246
   if(is_x25519(group_params)) {
2,486✔
247
      return std::make_unique<X25519_PrivateKey>(rng);
4,972✔
248
   }
249
#endif
250

251
   if(is_kem(group_params)) {
×
252
      throw TLS_Exception(Alert::IllegalParameter, "cannot generate an ephemeral KEX key for a KEM");
×
253
   }
254

255
   throw TLS_Exception(Alert::DecodeError, "cannot create a key offering without a group definition");
×
256
}
257

258
secure_vector<uint8_t> TLS::Callbacks::tls_ephemeral_key_agreement(
1,986✔
259
   const std::variant<TLS::Group_Params, DL_Group>& group,
260
   const PK_Key_Agreement_Key& private_key,
261
   const std::vector<uint8_t>& public_value,
262
   RandomNumberGenerator& rng,
263
   const Policy& policy) {
264
   auto agree = [&](const PK_Key_Agreement_Key& sk, const auto& pk) {
3,968✔
265
      PK_Key_Agreement ka(sk, rng, "Raw");
1,982✔
266
      return ka.derive_key(0, pk.public_value()).bits_of();
5,946✔
267
   };
3,968✔
268

269
   if(is_dh_group(group)) {
1,986✔
270
      // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of
271
      // a standardized DH group identifier.
272
      const auto dl_group = get_dl_group(group);
8✔
273

274
      auto Y = BigInt::decode(public_value);
8✔
275

276
      /*
277
       * A basic check for key validity. As we do not know q here we
278
       * cannot check that Y is in the right subgroup. However since
279
       * our key is ephemeral there does not seem to be any
280
       * advantage to bogus keys anyway.
281
       */
282
      if(Y <= 1 || Y >= dl_group.get_p() - 1) {
16✔
283
         throw TLS_Exception(Alert::IllegalParameter, "Server sent bad DH key for DHE exchange");
×
284
      }
285

286
      DH_PublicKey peer_key(dl_group, Y);
8✔
287
      policy.check_peer_key_acceptable(peer_key);
8✔
288

289
      return agree(private_key, peer_key);
8✔
290
   }
24✔
291

292
   BOTAN_ASSERT_NOMSG(std::holds_alternative<TLS::Group_Params>(group));
1,978✔
293
   const auto group_params = std::get<TLS::Group_Params>(group);
1,978✔
294

295
   if(is_ecdh(group_params)) {
1,978✔
296
      const EC_Group ec_group(group_param_to_string(group_params));
61✔
297
      ECDH_PublicKey peer_key(ec_group, ec_group.OS2ECP(public_value));
61✔
298
      policy.check_peer_key_acceptable(peer_key);
57✔
299

300
      return agree(private_key, peer_key);
57✔
301
   }
61✔
302

303
#if defined(BOTAN_HAS_CURVE_25519)
304
   if(is_x25519(group_params)) {
1,917✔
305
      if(public_value.size() != 32) {
1,917✔
306
         throw TLS_Exception(Alert::HandshakeFailure, "Invalid X25519 key size");
×
307
      }
308

309
      Curve25519_PublicKey peer_key(public_value);
1,917✔
310
      policy.check_peer_key_acceptable(peer_key);
1,917✔
311

312
      return agree(private_key, peer_key);
1,917✔
313
   }
1,917✔
314
#endif
315

316
   throw TLS_Exception(Alert::IllegalParameter, "Did not recognize the key exchange group");
×
317
}
318

319
}  // 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