/src/botan/src/lib/tls/tls13/tls_cipher_state.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * TLS cipher state implementation for TLS 1.3 |
3 | | * (C) 2022 Jack Lloyd |
4 | | * 2022 Hannes Rantzsch, René Meusel - neXenio GmbH |
5 | | * |
6 | | * Botan is released under the Simplified BSD License (see license.txt) |
7 | | */ |
8 | | |
9 | | /** |
10 | | * Cipher_State state machine adapted from RFC 8446 7.1. |
11 | | * |
12 | | * 0 |
13 | | * | |
14 | | * v |
15 | | * PSK -> HKDF-Extract = Early Secret |
16 | | * | |
17 | | * +-----> Derive-Secret(., "ext binder" | "res binder", "") |
18 | | * | = binder_key |
19 | | * STATE PSK BINDER |
20 | | * This state is reached by constructing the Cipher_State using init_with_psk(). |
21 | | * The state can then be further advanced using advance_with_client_hello() once |
22 | | * the initial Client Hello is fully generated. |
23 | | * | |
24 | | * +-----> Derive-Secret(., "c e traffic", ClientHello) |
25 | | * | = client_early_traffic_secret |
26 | | * | |
27 | | * +-----> Derive-Secret(., "e exp master", ClientHello) |
28 | | * | = early_exporter_master_secret |
29 | | * v |
30 | | * Derive-Secret(., "derived", "") |
31 | | * | |
32 | | * * |
33 | | * STATE EARLY TRAFFIC |
34 | | * This state is reached by calling advance_with_client_hello(). |
35 | | * In this state the early data traffic secrets are available. TODO: implement early data. |
36 | | * The state can then be further advanced using advance_with_server_hello(). |
37 | | * * |
38 | | * | |
39 | | * v |
40 | | * (EC)DHE -> HKDF-Extract = Handshake Secret |
41 | | * | |
42 | | * +-----> Derive-Secret(., "c hs traffic", |
43 | | * | ClientHello...ServerHello) |
44 | | * | = client_handshake_traffic_secret |
45 | | * | |
46 | | * +-----> Derive-Secret(., "s hs traffic", |
47 | | * | ClientHello...ServerHello) |
48 | | * | = server_handshake_traffic_secret |
49 | | * v |
50 | | * Derive-Secret(., "derived", "") |
51 | | * | |
52 | | * * |
53 | | * STATE HANDSHAKE TRAFFIC |
54 | | * This state is reached by constructing Cipher_State using init_with_server_hello() or |
55 | | * advance_with_server_hello(). In this state the handshake traffic secrets are available. |
56 | | * The state can then be further advanced using advance_with_server_finished(). |
57 | | * * |
58 | | * | |
59 | | * v |
60 | | * 0 -> HKDF-Extract = Master Secret |
61 | | * | |
62 | | * +-----> Derive-Secret(., "c ap traffic", |
63 | | * | ClientHello...server Finished) |
64 | | * | = client_application_traffic_secret_0 |
65 | | * | |
66 | | * +-----> Derive-Secret(., "s ap traffic", |
67 | | * | ClientHello...server Finished) |
68 | | * | = server_application_traffic_secret_0 |
69 | | * | |
70 | | * +-----> Derive-Secret(., "exp master", |
71 | | * | ClientHello...server Finished) |
72 | | * | = exporter_master_secret |
73 | | * * |
74 | | * STATE SERVER APPLICATION TRAFFIC |
75 | | * This state is reached by calling advance_with_server_finished(). It allows the server |
76 | | * to send application traffic and the client to receive it. The opposite direction is not |
77 | | * yet possible in this state. The state can then be further advanced using |
78 | | * advance_with_client_finished(). |
79 | | * * |
80 | | * | |
81 | | * +-----> Derive-Secret(., "res master", |
82 | | * ClientHello...client Finished) |
83 | | * = resumption_master_secret |
84 | | * STATE COMPLETED |
85 | | * Once this state is reached the handshake is finished, both client and server can exchange |
86 | | * application data and no further cipher state advances are possible. |
87 | | */ |
88 | | |
89 | | #include <limits> |
90 | | #include <utility> |
91 | | |
92 | | #include <botan/internal/tls_cipher_state.h> |
93 | | |
94 | | #include <botan/aead.h> |
95 | | #include <botan/assert.h> |
96 | | #include <botan/secmem.h> |
97 | | #include <botan/tls_ciphersuite.h> |
98 | | #include <botan/hash.h> |
99 | | #include <botan/tls_magic.h> |
100 | | |
101 | | #include <botan/internal/hkdf.h> |
102 | | #include <botan/internal/hmac.h> |
103 | | #include <botan/internal/loadstor.h> |
104 | | |
105 | | namespace Botan::TLS { |
106 | | |
107 | | namespace { |
108 | | // RFC 8446 5.3 |
109 | | // Each AEAD algorithm will specify a range of possible lengths for the |
110 | | // per-record nonce, from N_MIN bytes to N_MAX bytes of input [RFC5116]. |
111 | | // The length of the TLS per-record nonce (iv_length) is set to the |
112 | | // larger of 8 bytes and N_MIN for the AEAD algorithm (see [RFC5116], |
113 | | // Section 4). |
114 | | // |
115 | | // N_MIN is 12 for AES_GCM and AES_CCM as per RFC 5116 and also 12 for ChaCha20 per RFC 8439. |
116 | | constexpr size_t NONCE_LENGTH = 12; |
117 | | } |
118 | | |
119 | | std::unique_ptr<Cipher_State> Cipher_State::init_with_server_hello( |
120 | | const Connection_Side side, |
121 | | secure_vector<uint8_t>&& shared_secret, |
122 | | const Ciphersuite& cipher, |
123 | | const Transcript_Hash& transcript_hash) |
124 | 0 | { |
125 | 0 | auto cs = std::unique_ptr<Cipher_State>(new Cipher_State(side, cipher.prf_algo())); |
126 | 0 | cs->advance_without_psk(); |
127 | 0 | cs->advance_with_server_hello(cipher, std::move(shared_secret), transcript_hash); |
128 | 0 | return cs; |
129 | 0 | } |
130 | | |
131 | | std::unique_ptr<Cipher_State> Cipher_State::init_with_psk( |
132 | | const Connection_Side side, |
133 | | const Cipher_State::PSK_Type type, |
134 | | secure_vector<uint8_t>&& psk, |
135 | | const Ciphersuite& cipher) |
136 | 0 | { |
137 | 0 | auto cs = std::unique_ptr<Cipher_State>(new Cipher_State(side, cipher.prf_algo())); |
138 | 0 | cs->advance_with_psk(type, std::move(psk)); |
139 | 0 | return cs; |
140 | 0 | } |
141 | | |
142 | | void Cipher_State::advance_with_client_hello(const Transcript_Hash &transcript_hash) |
143 | 0 | { |
144 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::PskBinder); |
145 | |
|
146 | 0 | zap(m_binder_key); |
147 | | |
148 | | // TODO: Currently 0-RTT is not yet implemented, hence we don't derive the |
149 | | // early traffic secret for now. |
150 | | // |
151 | | // const auto client_early_traffic_secret = derive_secret(m_early_secret, "c e traffic", transcript_hash); |
152 | | // derive_write_traffic_key(client_early_traffic_secret); |
153 | |
|
154 | 0 | m_exporter_master_secret = derive_secret(m_early_secret, "e exp master", transcript_hash); |
155 | |
|
156 | 0 | m_salt = derive_secret(m_early_secret, "derived", empty_hash()); |
157 | 0 | zap(m_early_secret); |
158 | |
|
159 | 0 | m_state = State::EarlyTraffic; |
160 | 0 | } |
161 | | |
162 | | void Cipher_State::advance_with_server_finished(const Transcript_Hash& transcript_hash) |
163 | 0 | { |
164 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); |
165 | |
|
166 | 0 | const auto master_secret = hkdf_extract(secure_vector<uint8_t>(m_hash->output_length(), 0x00)); |
167 | |
|
168 | 0 | auto client_application_traffic_secret = derive_secret(master_secret, "c ap traffic", transcript_hash); |
169 | 0 | auto server_application_traffic_secret = derive_secret(master_secret, "s ap traffic", transcript_hash); |
170 | | |
171 | | // Note: the secrets for processing client's application data |
172 | | // are not derived before the client's Finished message |
173 | | // was seen and the handshake can be considered finished. |
174 | 0 | if(m_connection_side == Connection_Side::SERVER) |
175 | 0 | { |
176 | 0 | derive_write_traffic_key(server_application_traffic_secret); |
177 | 0 | m_read_application_traffic_secret = std::move(client_application_traffic_secret); |
178 | 0 | m_write_application_traffic_secret = std::move(server_application_traffic_secret); |
179 | 0 | } |
180 | 0 | else |
181 | 0 | { |
182 | 0 | derive_read_traffic_key(server_application_traffic_secret); |
183 | 0 | m_read_application_traffic_secret = std::move(server_application_traffic_secret); |
184 | 0 | m_write_application_traffic_secret = std::move(client_application_traffic_secret); |
185 | 0 | } |
186 | |
|
187 | 0 | m_exporter_master_secret = derive_secret(master_secret, "exp master", transcript_hash); |
188 | |
|
189 | 0 | m_state = State::ServerApplicationTraffic; |
190 | 0 | } |
191 | | |
192 | | void Cipher_State::advance_with_client_finished(const Transcript_Hash& transcript_hash) |
193 | 0 | { |
194 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::ServerApplicationTraffic); |
195 | |
|
196 | 0 | zap(m_finished_key); |
197 | 0 | zap(m_peer_finished_key); |
198 | | |
199 | | // With the client's Finished message, the handshake is complete and |
200 | | // we can process client application data. |
201 | 0 | if(m_connection_side == Connection_Side::SERVER) |
202 | 0 | { |
203 | 0 | derive_read_traffic_key(m_read_application_traffic_secret); |
204 | 0 | } |
205 | 0 | else |
206 | 0 | { |
207 | 0 | derive_write_traffic_key(m_write_application_traffic_secret); |
208 | 0 | } |
209 | |
|
210 | 0 | const auto master_secret = hkdf_extract(secure_vector<uint8_t>(m_hash->output_length(), 0x00)); |
211 | |
|
212 | 0 | m_resumption_master_secret = derive_secret(master_secret, "res master", transcript_hash); |
213 | | |
214 | | // This was the final state change; the salt is no longer needed. |
215 | 0 | zap(m_salt); |
216 | |
|
217 | 0 | m_state = State::Completed; |
218 | 0 | } |
219 | | |
220 | | std::vector<uint8_t> Cipher_State::current_nonce(const uint64_t seq_no, const secure_vector<uint8_t>& iv) const |
221 | 0 | { |
222 | | // RFC 8446 5.3 |
223 | | // The per-record nonce for the AEAD construction is formed as follows: |
224 | | // |
225 | | // 1. The 64-bit record sequence number is encoded in network byte |
226 | | // order and padded to the left with zeros to iv_length. |
227 | | // |
228 | | // 2. The padded sequence number is XORed with either the static |
229 | | // client_write_iv or server_write_iv (depending on the role). |
230 | 0 | std::vector<uint8_t> nonce(NONCE_LENGTH); |
231 | 0 | store_be(seq_no, nonce.data() + (NONCE_LENGTH-sizeof(seq_no))); |
232 | 0 | xor_buf(nonce, iv.data(), iv.size()); |
233 | 0 | return nonce; |
234 | 0 | } |
235 | | |
236 | | uint64_t Cipher_State::encrypt_record_fragment(const std::vector<uint8_t>& header, secure_vector<uint8_t>& fragment) |
237 | 0 | { |
238 | 0 | BOTAN_ASSERT_NONNULL(m_encrypt); |
239 | |
|
240 | 0 | m_encrypt->set_key(m_write_key); |
241 | 0 | m_encrypt->set_associated_data_vec(header); |
242 | 0 | m_encrypt->start(current_nonce(m_write_seq_no, m_write_iv)); |
243 | 0 | m_encrypt->finish(fragment); |
244 | |
|
245 | 0 | return m_write_seq_no++; |
246 | 0 | } |
247 | | |
248 | | uint64_t Cipher_State::decrypt_record_fragment(const std::vector<uint8_t>& header, |
249 | | secure_vector<uint8_t>& encrypted_fragment) |
250 | 0 | { |
251 | 0 | BOTAN_ASSERT_NONNULL(m_decrypt); |
252 | 0 | BOTAN_ARG_CHECK(encrypted_fragment.size() >= m_decrypt->minimum_final_size(), |
253 | 0 | "fragment too short to decrypt"); |
254 | |
|
255 | 0 | m_decrypt->set_key(m_read_key); |
256 | 0 | m_decrypt->set_associated_data_vec(header); |
257 | 0 | m_decrypt->start(current_nonce(m_read_seq_no, m_read_iv)); |
258 | |
|
259 | 0 | m_decrypt->finish(encrypted_fragment); |
260 | |
|
261 | 0 | return m_read_seq_no++; |
262 | 0 | } |
263 | | |
264 | | size_t Cipher_State::encrypt_output_length(const size_t input_length) const |
265 | 0 | { |
266 | 0 | BOTAN_ASSERT_NONNULL(m_encrypt); |
267 | 0 | return m_encrypt->output_length(input_length); |
268 | 0 | } |
269 | | |
270 | | size_t Cipher_State::decrypt_output_length(const size_t input_length) const |
271 | 0 | { |
272 | 0 | BOTAN_ASSERT_NONNULL(m_decrypt); |
273 | 0 | return m_decrypt->output_length(input_length); |
274 | 0 | } |
275 | | |
276 | | size_t Cipher_State::minimum_decryption_input_length() const |
277 | 0 | { |
278 | 0 | BOTAN_ASSERT_NONNULL(m_decrypt); |
279 | 0 | return m_decrypt->minimum_final_size(); |
280 | 0 | } |
281 | | |
282 | | bool Cipher_State::must_expect_unprotected_alert_traffic() const |
283 | 0 | { |
284 | | // Client side: |
285 | | // After successfully receiving a Server Hello we expect servers to send |
286 | | // alerts as protected records only, just like they start protecting their |
287 | | // handshake data at this point. |
288 | 0 | if(m_connection_side == CLIENT && m_state == State::EarlyTraffic) |
289 | 0 | { return true; } |
290 | | |
291 | | // Server side: |
292 | | // Servers must expect clients to send unprotected alerts during the hand- |
293 | | // shake. In particular, in the response to the server's first protected |
294 | | // flight. We don't expect the client to send alerts protected under the |
295 | | // early traffic secret. |
296 | | // |
297 | | // TODO: when implementing PSK and/or early data for the server, we might |
298 | | // need to reconsider this decision. |
299 | 0 | if(m_connection_side == SERVER && |
300 | 0 | (m_state == State::HandshakeTraffic || |
301 | 0 | m_state == State::ServerApplicationTraffic)) |
302 | 0 | { return true; } |
303 | | |
304 | 0 | return false; |
305 | 0 | } |
306 | | |
307 | | bool Cipher_State::can_encrypt_application_traffic() const |
308 | 0 | { |
309 | | // TODO: when implementing early traffic (0-RTT) this will likely need |
310 | | // to allow `State::EarlyTraffic`. |
311 | |
|
312 | 0 | if(m_connection_side == Connection_Side::CLIENT && m_state != State::Completed) |
313 | 0 | { return false; } |
314 | | |
315 | 0 | if (m_connection_side == Connection_Side::SERVER && m_state != State::ServerApplicationTraffic && m_state != State::Completed) |
316 | 0 | { return false; } |
317 | | |
318 | 0 | return !m_write_key.empty() && !m_write_iv.empty(); |
319 | 0 | } |
320 | | |
321 | | bool Cipher_State::can_decrypt_application_traffic() const |
322 | 0 | { |
323 | | // TODO: when implementing early traffic (0-RTT) this will likely need |
324 | | // to allow `State::EarlyTraffic`. |
325 | |
|
326 | 0 | if(m_connection_side == Connection_Side::CLIENT && m_state != State::ServerApplicationTraffic && m_state != State::Completed) |
327 | 0 | { return false; } |
328 | | |
329 | 0 | if(m_connection_side == Connection_Side::SERVER && m_state != State::Completed) |
330 | 0 | { return false; } |
331 | | |
332 | 0 | return !m_read_key.empty() && !m_read_iv.empty(); |
333 | 0 | } |
334 | | |
335 | | bool Cipher_State::is_compatible_with(const Ciphersuite& cipher) const |
336 | 0 | { |
337 | 0 | if(!cipher.usable_in_version(Protocol_Version::TLS_V13)) |
338 | 0 | return false; |
339 | | |
340 | 0 | if(m_hash && m_hash->name() != cipher.prf_algo()) |
341 | 0 | return false; |
342 | | |
343 | 0 | BOTAN_ASSERT_NOMSG((m_encrypt == nullptr) == (m_decrypt == nullptr)); |
344 | | // TODO: Find a better way to check that the instantiated cipher algorithm |
345 | | // is compatible with the one required by the cipher suite. |
346 | | // AEAD_Mode::create() sets defaults the tag length to 16 which is then |
347 | | // reported via AEAD_Mode::name() and hinders the trivial string comparison. |
348 | 0 | if(m_encrypt && m_encrypt->name() != cipher.cipher_algo() && |
349 | 0 | m_encrypt->name() != cipher.cipher_algo() + "(16)") |
350 | 0 | return false; |
351 | | |
352 | 0 | return true; |
353 | 0 | } |
354 | | |
355 | | std::vector<uint8_t> Cipher_State::psk_binder_mac(const Transcript_Hash& transcript_hash_with_truncated_client_hello) |
356 | 0 | { |
357 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::PskBinder); |
358 | |
|
359 | 0 | auto hmac = HMAC(m_hash->new_object()); |
360 | 0 | hmac.set_key(m_binder_key); |
361 | 0 | hmac.update(transcript_hash_with_truncated_client_hello); |
362 | 0 | return hmac.final_stdvec(); |
363 | 0 | } |
364 | | |
365 | | std::vector<uint8_t> Cipher_State::finished_mac(const Transcript_Hash& transcript_hash) const |
366 | 0 | { |
367 | 0 | BOTAN_ASSERT_NOMSG(m_connection_side != Connection_Side::SERVER || m_state == State::HandshakeTraffic); |
368 | 0 | BOTAN_ASSERT_NOMSG(m_connection_side != Connection_Side::CLIENT || m_state == State::ServerApplicationTraffic); |
369 | 0 | BOTAN_ASSERT_NOMSG(!m_finished_key.empty()); |
370 | |
|
371 | 0 | auto hmac = HMAC(m_hash->new_object()); |
372 | 0 | hmac.set_key(m_finished_key); |
373 | 0 | hmac.update(transcript_hash); |
374 | 0 | return hmac.final_stdvec(); |
375 | 0 | } |
376 | | |
377 | | bool Cipher_State::verify_peer_finished_mac(const Transcript_Hash& transcript_hash, |
378 | | const std::vector<uint8_t>& peer_mac) const |
379 | 0 | { |
380 | 0 | BOTAN_ASSERT_NOMSG(m_connection_side != Connection_Side::SERVER || m_state == State::ServerApplicationTraffic); |
381 | 0 | BOTAN_ASSERT_NOMSG(m_connection_side != Connection_Side::CLIENT || m_state == State::HandshakeTraffic); |
382 | 0 | BOTAN_ASSERT_NOMSG(!m_peer_finished_key.empty()); |
383 | |
|
384 | 0 | auto hmac = HMAC(m_hash->new_object()); |
385 | 0 | hmac.set_key(m_peer_finished_key); |
386 | 0 | hmac.update(transcript_hash); |
387 | 0 | return hmac.verify_mac(peer_mac); |
388 | 0 | } |
389 | | |
390 | | secure_vector<uint8_t> Cipher_State::psk(const std::vector<uint8_t>& nonce) const |
391 | 0 | { |
392 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::Completed); |
393 | |
|
394 | 0 | return derive_secret(m_resumption_master_secret, "resumption", nonce); |
395 | 0 | } |
396 | | |
397 | | |
398 | | secure_vector<uint8_t> Cipher_State::export_key(const std::string& label, |
399 | | const std::string& context, |
400 | | size_t length) const |
401 | 0 | { |
402 | 0 | BOTAN_ASSERT_NOMSG(can_export_keys()); |
403 | |
|
404 | 0 | m_hash->update(context); |
405 | 0 | const auto context_hash = m_hash->final_stdvec(); |
406 | 0 | return hkdf_expand_label(derive_secret(m_exporter_master_secret, label, empty_hash()), |
407 | 0 | "exporter", context_hash, length); |
408 | 0 | } |
409 | | |
410 | | |
411 | | namespace { |
412 | | |
413 | | std::unique_ptr<MessageAuthenticationCode> create_hmac(const std::string& hash) |
414 | 0 | { |
415 | 0 | return std::make_unique<HMAC>(HashFunction::create_or_throw(hash)); |
416 | 0 | } |
417 | | |
418 | | } |
419 | | |
420 | | Cipher_State::Cipher_State(Connection_Side whoami, const std::string& hash_function) |
421 | | : m_state(State::Uninitialized) |
422 | | , m_connection_side(whoami) |
423 | | , m_extract(std::make_unique<HKDF_Extract>(create_hmac(hash_function))) |
424 | | , m_expand(std::make_unique<HKDF_Expand>(create_hmac(hash_function))) |
425 | | , m_hash(HashFunction::create_or_throw(hash_function)) |
426 | | , m_salt(m_hash->output_length(), 0x00) |
427 | | , m_write_seq_no(0) |
428 | 0 | , m_read_seq_no(0) {} |
429 | | |
430 | 0 | Cipher_State::~Cipher_State() = default; |
431 | | |
432 | | void Cipher_State::advance_without_psk() |
433 | 0 | { |
434 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::Uninitialized); |
435 | | |
436 | | // We are not using `m_early_secret` here because the secret won't be needed |
437 | | // in any further state advancement methods. |
438 | 0 | const auto early_secret = hkdf_extract(secure_vector<uint8_t>(m_hash->output_length(), 0x00)); |
439 | 0 | m_salt = derive_secret(early_secret, "derived", empty_hash()); |
440 | | |
441 | | // Without PSK we skip the `PskBinder` state and go right to `EarlyTraffic`. |
442 | 0 | m_state = State::EarlyTraffic; |
443 | 0 | } |
444 | | |
445 | | void Cipher_State::advance_with_psk(PSK_Type type, secure_vector<uint8_t>&& psk) |
446 | 0 | { |
447 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::Uninitialized); |
448 | |
|
449 | 0 | m_early_secret = hkdf_extract(std::move(psk)); |
450 | |
|
451 | 0 | const char* binder_label = |
452 | 0 | (type == PSK_Type::RESUMPTION) |
453 | 0 | ? "res binder" |
454 | 0 | : "ext binder"; |
455 | | |
456 | | // RFC 8446 4.2.11.2 |
457 | | // The PskBinderEntry is computed in the same way as the Finished message |
458 | | // [...] but with the BaseKey being the binder_key derived via the key |
459 | | // schedule from the corresponding PSK which is being offered. |
460 | | // |
461 | | // Hence we are doing the binder key derivation and expansion in one go. |
462 | 0 | const auto binder_key = derive_secret(m_early_secret, binder_label, empty_hash()); |
463 | 0 | m_binder_key = hkdf_expand_label(binder_key, "finished", {}, m_hash->output_length()); |
464 | |
|
465 | 0 | m_state = State::PskBinder; |
466 | 0 | } |
467 | | |
468 | | void Cipher_State::advance_with_server_hello(const Ciphersuite& cipher, |
469 | | secure_vector<uint8_t>&& shared_secret, |
470 | | const Transcript_Hash& transcript_hash) |
471 | 0 | { |
472 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::EarlyTraffic); |
473 | 0 | BOTAN_ASSERT_NOMSG(!m_encrypt); |
474 | 0 | BOTAN_ASSERT_NOMSG(!m_decrypt); |
475 | 0 | BOTAN_STATE_CHECK(is_compatible_with(cipher)); |
476 | |
|
477 | 0 | m_encrypt = AEAD_Mode::create_or_throw(cipher.cipher_algo(), ENCRYPTION); |
478 | 0 | m_decrypt = AEAD_Mode::create_or_throw(cipher.cipher_algo(), DECRYPTION); |
479 | |
|
480 | 0 | const auto handshake_secret = hkdf_extract(std::move(shared_secret)); |
481 | |
|
482 | 0 | const auto client_handshake_traffic_secret = derive_secret(handshake_secret, "c hs traffic", transcript_hash); |
483 | 0 | const auto server_handshake_traffic_secret = derive_secret(handshake_secret, "s hs traffic", transcript_hash); |
484 | |
|
485 | 0 | if(m_connection_side == Connection_Side::SERVER) |
486 | 0 | { |
487 | 0 | derive_read_traffic_key(client_handshake_traffic_secret, true); |
488 | 0 | derive_write_traffic_key(server_handshake_traffic_secret, true); |
489 | 0 | } |
490 | 0 | else |
491 | 0 | { |
492 | 0 | derive_read_traffic_key(server_handshake_traffic_secret, true); |
493 | 0 | derive_write_traffic_key(client_handshake_traffic_secret, true); |
494 | 0 | } |
495 | |
|
496 | 0 | m_salt = derive_secret(handshake_secret, "derived", empty_hash()); |
497 | |
|
498 | 0 | m_state = State::HandshakeTraffic; |
499 | 0 | } |
500 | | |
501 | | void Cipher_State::derive_write_traffic_key(const secure_vector<uint8_t>& traffic_secret, |
502 | | const bool handshake_traffic_secret) |
503 | 0 | { |
504 | 0 | BOTAN_ASSERT_NONNULL(m_encrypt); |
505 | |
|
506 | 0 | m_write_key = hkdf_expand_label(traffic_secret, "key", {}, m_encrypt->minimum_keylength()); |
507 | 0 | m_write_iv = hkdf_expand_label(traffic_secret, "iv", {}, NONCE_LENGTH); |
508 | 0 | m_write_seq_no = 0; |
509 | |
|
510 | 0 | if(handshake_traffic_secret) |
511 | 0 | { |
512 | | // Key derivation for the MAC in the "Finished" handshake message as described in RFC 8446 4.4.4 |
513 | | // (will be cleared in advance_with_server_finished()) |
514 | 0 | m_finished_key = hkdf_expand_label(traffic_secret, "finished", {}, m_hash->output_length()); |
515 | 0 | } |
516 | 0 | } |
517 | | |
518 | | void Cipher_State::derive_read_traffic_key(const secure_vector<uint8_t>& traffic_secret, |
519 | | const bool handshake_traffic_secret) |
520 | 0 | { |
521 | 0 | BOTAN_ASSERT_NONNULL(m_encrypt); |
522 | |
|
523 | 0 | m_read_key = hkdf_expand_label(traffic_secret, "key", {}, m_encrypt->minimum_keylength()); |
524 | 0 | m_read_iv = hkdf_expand_label(traffic_secret, "iv", {}, NONCE_LENGTH); |
525 | 0 | m_read_seq_no = 0; |
526 | |
|
527 | 0 | if(handshake_traffic_secret) |
528 | 0 | { |
529 | | // Key derivation for the MAC in the "Finished" handshake message as described in RFC 8446 4.4.4 |
530 | | // (will be cleared in advance_with_client_finished()) |
531 | 0 | m_peer_finished_key = hkdf_expand_label(traffic_secret, "finished", {}, m_hash->output_length()); |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | | secure_vector<uint8_t> Cipher_State::hkdf_extract(secure_vector<uint8_t>&& ikm) const |
536 | 0 | { |
537 | 0 | return m_extract->derive_key(m_hash->output_length(), ikm, m_salt, std::vector<uint8_t>()); |
538 | 0 | } |
539 | | |
540 | | secure_vector<uint8_t> Cipher_State::hkdf_expand_label( |
541 | | const secure_vector<uint8_t>& secret, |
542 | | const std::string& label, |
543 | | const std::vector<uint8_t>& context, |
544 | | const size_t length) const |
545 | 0 | { |
546 | | // assemble (serialized) HkdfLabel |
547 | 0 | secure_vector<uint8_t> hkdf_label; |
548 | 0 | hkdf_label.reserve(2 /* length */ + |
549 | 0 | (label.size() + |
550 | 0 | 6 /* 'tls13 ' */ + |
551 | 0 | 1 /* length field*/) + |
552 | 0 | (context.size() + |
553 | 0 | 1 /* length field*/)); |
554 | | |
555 | | // length |
556 | 0 | BOTAN_ARG_CHECK(length <= std::numeric_limits<uint16_t>::max(), "invalid length"); |
557 | 0 | const auto len = static_cast<uint16_t>(length); |
558 | 0 | hkdf_label.push_back(get_byte<0>(len)); |
559 | 0 | hkdf_label.push_back(get_byte<1>(len)); |
560 | | |
561 | | // label |
562 | 0 | const std::string prefix = "tls13 "; |
563 | 0 | BOTAN_ARG_CHECK(prefix.size() + label.size() <= 255, "label too large"); |
564 | 0 | hkdf_label.push_back(static_cast<uint8_t>(prefix.size() + label.size())); |
565 | 0 | hkdf_label.insert(hkdf_label.end(), prefix.cbegin(), prefix.cend()); |
566 | 0 | hkdf_label.insert(hkdf_label.end(), label.cbegin(), label.cend()); |
567 | | |
568 | | // context |
569 | 0 | BOTAN_ARG_CHECK(context.size() <= 255, "context too large"); |
570 | 0 | hkdf_label.push_back(static_cast<uint8_t>(context.size())); |
571 | 0 | hkdf_label.insert(hkdf_label.end(), context.cbegin(), context.cend()); |
572 | | |
573 | | // HKDF-Expand |
574 | 0 | return m_expand->derive_key(length, secret, hkdf_label, std::vector<uint8_t>() /* just pleasing botan's interface */); |
575 | 0 | } |
576 | | |
577 | | secure_vector<uint8_t> Cipher_State::derive_secret( |
578 | | const secure_vector<uint8_t>& secret, |
579 | | const std::string& label, |
580 | | const Transcript_Hash& messages_hash) const |
581 | 0 | { |
582 | 0 | return hkdf_expand_label(secret, label, messages_hash, m_hash->output_length()); |
583 | 0 | } |
584 | | |
585 | | std::vector<uint8_t> Cipher_State::empty_hash() const |
586 | 0 | { |
587 | 0 | m_hash->update(""); |
588 | 0 | return m_hash->final_stdvec(); |
589 | 0 | } |
590 | | |
591 | | void Cipher_State::update_read_keys() |
592 | 0 | { |
593 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::ServerApplicationTraffic || |
594 | 0 | m_state == State::Completed); |
595 | |
|
596 | 0 | m_read_application_traffic_secret = |
597 | 0 | hkdf_expand_label(m_read_application_traffic_secret, "traffic upd", {}, m_hash->output_length()); |
598 | |
|
599 | 0 | derive_read_traffic_key(m_read_application_traffic_secret); |
600 | 0 | } |
601 | | |
602 | | void Cipher_State::update_write_keys() |
603 | 0 | { |
604 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::ServerApplicationTraffic || |
605 | 0 | m_state == State::Completed); |
606 | 0 | m_write_application_traffic_secret = |
607 | 0 | hkdf_expand_label(m_write_application_traffic_secret, "traffic upd", {}, m_hash->output_length()); |
608 | |
|
609 | 0 | derive_write_traffic_key(m_write_application_traffic_secret); |
610 | 0 | } |
611 | | |
612 | | void Cipher_State::clear_read_keys() |
613 | 0 | { |
614 | 0 | zap(m_read_key); |
615 | 0 | zap(m_read_iv); |
616 | 0 | zap(m_read_application_traffic_secret); |
617 | 0 | } |
618 | | |
619 | | void Cipher_State::clear_write_keys() |
620 | 0 | { |
621 | 0 | zap(m_write_key); |
622 | 0 | zap(m_write_iv); |
623 | 0 | zap(m_write_application_traffic_secret); |
624 | 0 | } |
625 | | |
626 | | } // namespace Botan::TLS |