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

randombit / botan / 5454043422

04 Jul 2023 11:13AM CUT coverage: 91.659% (-0.07%) from 91.732%
5454043422

Pull #3609

github

web-flow
Merge 5741e183c into cf8d8a6ca
Pull Request #3609: [TLS 1.3] Hybrid PQ/T key establishment

78635 of 85791 relevant lines covered (91.66%)

12300857.61 hits per line

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

86.92
/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,381✔
40
   // default is no op
41
}
6,381✔
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() {
734✔
48
   return "";
734✔
49
}
50

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

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

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

63
bool TLS::Callbacks::tls_should_persist_resumption_information(const Session& session) {
2,541✔
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,541✔
76
}
77

78
void TLS::Callbacks::tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain,
106✔
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()) {
106✔
85
      throw Invalid_Argument("Certificate chain was empty");
×
86
   }
87

88
   Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(),
106✔
89
                                             policy.minimum_signature_strength());
318✔
90

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

100
   if(!result.successful_validation()) {
106✔
101
      throw TLS_Exception(Alert::BadCertificate, "Certificate validation failure: " + result.result_string());
×
102
   }
103
}
106✔
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(
247✔
115
   const std::vector<X509_Certificate>& chain, const Certificate_Status_Request& csr) {
116
   std::vector<std::vector<uint8_t>> result(chain.size());
247✔
117
   if(!chain.empty()) {
247✔
118
      result[0] = tls_provide_cert_status(chain, csr);
247✔
119
   }
120
   return result;
239✔
121
}
8✔
122

123
std::vector<uint8_t> TLS::Callbacks::tls_sign_message(const Private_Key& key,
920✔
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);
920✔
129

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

133
bool TLS::Callbacks::tls_verify_message(const Public_Key& key,
1,130✔
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,130✔
139

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

143
std::unique_ptr<Private_Key> TLS::Callbacks::tls_kem_generate_key(TLS::Group_Params group, RandomNumberGenerator& rng) {
997✔
144
#if defined(BOTAN_HAS_KYBER)
145
   if(is_kyber(group)) {
997✔
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)) {
996✔
152
      return Hybrid_KEM_PrivateKey::generate_from_group(group, rng);
1✔
153
   }
154
#endif
155

156
   return tls_generate_ephemeral_key(group, rng);
2,985✔
157
}
158

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

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

172
#if defined(BOTAN_HAS_KYBER)
173
         if(is_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
      }();
2✔
180

181
      return PK_KEM_Encryptor(*kem_pub_key, "Raw").encrypt(rng);
2✔
182
   }
2✔
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);
357✔
188
   return KEM_Encapsulation(ephemeral_keypair->public_value(),
356✔
189
                            tls_ephemeral_key_agreement(group, *ephemeral_keypair, encoded_public_key, rng, policy));
1,070✔
190
}
358✔
191

192
secure_vector<uint8_t> TLS::Callbacks::tls_kem_decapsulate(TLS::Group_Params group,
422✔
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)) {
422✔
198
      PK_KEM_Decryptor kemdec(private_key, rng, "Raw");
2✔
199
      return kemdec.decrypt(encapsulated_bytes, 0, {});
2✔
200
   }
2✔
201

202
   try {
420✔
203
      auto& key_agreement_key = dynamic_cast<const PK_Key_Agreement_Key&>(private_key);
420✔
204
      return tls_ephemeral_key_agreement(group, key_agreement_key, encapsulated_bytes, rng, policy);
840✔
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,586✔
213
   return std::holds_alternative<DL_Group>(group) || is_dh(std::get<TLS::Group_Params>(group));
9,156✔
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,578✔
231
   const std::variant<TLS::Group_Params, DL_Group>& group, RandomNumberGenerator& rng) {
232
   if(is_dh_group(group)) {
2,578✔
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,566✔
238
   const auto group_params = std::get<TLS::Group_Params>(group);
2,566✔
239

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

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

251
   throw TLS_Exception(Alert::DecodeError, "cannot create a key offering without a group definition");
×
252
}
253

254
secure_vector<uint8_t> TLS::Callbacks::tls_ephemeral_key_agreement(
1,988✔
255
   const std::variant<TLS::Group_Params, DL_Group>& group,
256
   const PK_Key_Agreement_Key& private_key,
257
   const std::vector<uint8_t>& public_value,
258
   RandomNumberGenerator& rng,
259
   const Policy& policy) {
260
   auto agree = [&](const PK_Key_Agreement_Key& sk, const auto& pk) {
3,972✔
261
      PK_Key_Agreement ka(sk, rng, "Raw");
1,984✔
262
      return ka.derive_key(0, pk.public_value()).bits_of();
5,952✔
263
   };
3,972✔
264

265
   if(is_dh_group(group)) {
1,988✔
266
      // TLS 1.2 allows specifying arbitrary DL_Group parameters in-lieu of
267
      // a standardized DH group identifier.
268
      const auto dl_group = get_dl_group(group);
8✔
269

270
      auto Y = BigInt::decode(public_value);
8✔
271

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

282
      DH_PublicKey peer_key(dl_group, Y);
8✔
283
      policy.check_peer_key_acceptable(peer_key);
8✔
284

285
      return agree(private_key, peer_key);
8✔
286
   }
24✔
287

288
   BOTAN_ASSERT_NOMSG(std::holds_alternative<TLS::Group_Params>(group));
1,980✔
289
   const auto group_params = std::get<TLS::Group_Params>(group);
1,980✔
290

291
   if(is_ecdh(group_params)) {
1,980✔
292
      const EC_Group ec_group(group_param_to_string(group_params));
231✔
293
      ECDH_PublicKey peer_key(ec_group, ec_group.OS2ECP(public_value));
231✔
294
      policy.check_peer_key_acceptable(peer_key);
227✔
295

296
      return agree(private_key, peer_key);
227✔
297
   }
231✔
298

299
#if defined(BOTAN_HAS_CURVE_25519)
300
   if(is_x25519(group_params)) {
1,749✔
301
      if(public_value.size() != 32) {
1,749✔
302
         throw TLS_Exception(Alert::HandshakeFailure, "Invalid X25519 key size");
×
303
      }
304

305
      Curve25519_PublicKey peer_key(public_value);
1,749✔
306
      policy.check_peer_key_acceptable(peer_key);
1,749✔
307

308
      return agree(private_key, peer_key);
1,749✔
309
   }
1,749✔
310
#endif
311

312
   throw TLS_Exception(Alert::IllegalParameter, "Did not recognize the key exchange group");
×
313
}
314

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

© 2025 Coveralls, Inc