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