/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 | | * | |
20 | | * +-----> Derive-Secret(., "c e traffic", ClientHello) |
21 | | * | = client_early_traffic_secret |
22 | | * | |
23 | | * +-----> Derive-Secret(., "e exp master", ClientHello) |
24 | | * | = early_exporter_master_secret |
25 | | * v |
26 | | * Derive-Secret(., "derived", "") |
27 | | * | |
28 | | * * |
29 | | * STATE EARLY TRAFFIC |
30 | | * This state is reached by constructing Cipher_State using init_with_psk() (not yet implemented). |
31 | | * The state can then be further advanced using advance_with_server_hello(). |
32 | | * * |
33 | | * | |
34 | | * v |
35 | | * (EC)DHE -> HKDF-Extract = Handshake Secret |
36 | | * | |
37 | | * +-----> Derive-Secret(., "c hs traffic", |
38 | | * | ClientHello...ServerHello) |
39 | | * | = client_handshake_traffic_secret |
40 | | * | |
41 | | * +-----> Derive-Secret(., "s hs traffic", |
42 | | * | ClientHello...ServerHello) |
43 | | * | = server_handshake_traffic_secret |
44 | | * v |
45 | | * Derive-Secret(., "derived", "") |
46 | | * | |
47 | | * * |
48 | | * STATE HANDSHAKE TRAFFIC |
49 | | * This state is reached by constructing Cipher_State using init_with_server_hello(). |
50 | | * In this state the handshake traffic secrets are available. The state can then be further |
51 | | * advanced using advance_with_server_finished(). |
52 | | * * |
53 | | * | |
54 | | * v |
55 | | * 0 -> HKDF-Extract = Master Secret |
56 | | * | |
57 | | * +-----> Derive-Secret(., "c ap traffic", |
58 | | * | ClientHello...server Finished) |
59 | | * | = client_application_traffic_secret_0 |
60 | | * | |
61 | | * +-----> Derive-Secret(., "s ap traffic", |
62 | | * | ClientHello...server Finished) |
63 | | * | = server_application_traffic_secret_0 |
64 | | * | |
65 | | * +-----> Derive-Secret(., "exp master", |
66 | | * | ClientHello...server Finished) |
67 | | * | = exporter_master_secret |
68 | | * * |
69 | | * STATE APPLICATION TRAFFIC |
70 | | * This state is reached by calling advance_with_server_finished(). The state can then be further |
71 | | * advanced using advance_with_client_finished(). |
72 | | * * |
73 | | * | |
74 | | * +-----> Derive-Secret(., "res master", |
75 | | * ClientHello...client Finished) |
76 | | * = resumption_master_secret |
77 | | * STATE COMPLETED |
78 | | */ |
79 | | |
80 | | #include <limits> |
81 | | #include <utility> |
82 | | |
83 | | #include <botan/internal/tls_cipher_state.h> |
84 | | |
85 | | #include <botan/aead.h> |
86 | | #include <botan/assert.h> |
87 | | #include <botan/secmem.h> |
88 | | #include <botan/tls_ciphersuite.h> |
89 | | #include <botan/hash.h> |
90 | | #include <botan/tls_magic.h> |
91 | | |
92 | | #include <botan/internal/hkdf.h> |
93 | | #include <botan/internal/hmac.h> |
94 | | #include <botan/internal/loadstor.h> |
95 | | |
96 | | namespace Botan::TLS { |
97 | | |
98 | | namespace { |
99 | | // RFC 8446 5.3 |
100 | | // Each AEAD algorithm will specify a range of possible lengths for the |
101 | | // per-record nonce, from N_MIN bytes to N_MAX bytes of input [RFC5116]. |
102 | | // The length of the TLS per-record nonce (iv_length) is set to the |
103 | | // larger of 8 bytes and N_MIN for the AEAD algorithm (see [RFC5116], |
104 | | // Section 4). |
105 | | // |
106 | | // N_MIN is 12 for AES_GCM and AES_CCM as per RFC 5116 and also 12 for ChaCha20 per RFC 8439. |
107 | | constexpr size_t NONCE_LENGTH = 12; |
108 | | } |
109 | | |
110 | | std::unique_ptr<Cipher_State> Cipher_State::init_with_server_hello( |
111 | | const Connection_Side side, |
112 | | secure_vector<uint8_t>&& shared_secret, |
113 | | const Ciphersuite& cipher, |
114 | | const Transcript_Hash& transcript_hash) |
115 | 0 | { |
116 | 0 | auto cs = std::unique_ptr<Cipher_State>(new Cipher_State(side, cipher)); |
117 | 0 | cs->advance_without_psk(); |
118 | 0 | cs->advance_with_server_hello(std::move(shared_secret), transcript_hash); |
119 | 0 | return cs; |
120 | 0 | } |
121 | | |
122 | | void Cipher_State::advance_with_server_finished(const Transcript_Hash& transcript_hash) |
123 | 0 | { |
124 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); |
125 | |
|
126 | 0 | zap(m_finished_key); |
127 | 0 | zap(m_peer_finished_key); |
128 | |
|
129 | 0 | const auto master_secret = hkdf_extract(secure_vector<uint8_t>(m_hash->output_length(), 0x00)); |
130 | |
|
131 | 0 | auto client_application_traffic_secret = derive_secret(master_secret, "c ap traffic", transcript_hash); |
132 | 0 | auto server_application_traffic_secret = derive_secret(master_secret, "s ap traffic", transcript_hash); |
133 | |
|
134 | 0 | if(m_connection_side == Connection_Side::SERVER) |
135 | 0 | { |
136 | 0 | derive_read_traffic_key(client_application_traffic_secret); |
137 | 0 | derive_write_traffic_key(server_application_traffic_secret); |
138 | 0 | m_read_application_traffic_secret = std::move(client_application_traffic_secret); |
139 | 0 | m_write_application_traffic_secret = std::move(server_application_traffic_secret); |
140 | 0 | } |
141 | 0 | else |
142 | 0 | { |
143 | 0 | derive_read_traffic_key(server_application_traffic_secret); |
144 | 0 | derive_write_traffic_key(client_application_traffic_secret); |
145 | 0 | m_read_application_traffic_secret = std::move(server_application_traffic_secret); |
146 | 0 | m_write_application_traffic_secret = std::move(client_application_traffic_secret); |
147 | 0 | } |
148 | |
|
149 | 0 | m_exporter_master_secret = derive_secret(master_secret, "exp master", transcript_hash); |
150 | |
|
151 | 0 | m_state = State::ApplicationTraffic; |
152 | 0 | } |
153 | | |
154 | | void Cipher_State::advance_with_client_finished(const Transcript_Hash& transcript_hash) |
155 | 0 | { |
156 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::ApplicationTraffic); |
157 | |
|
158 | 0 | const auto master_secret = hkdf_extract(secure_vector<uint8_t>(m_hash->output_length(), 0x00)); |
159 | |
|
160 | 0 | m_resumption_master_secret = derive_secret(master_secret, "res master", transcript_hash); |
161 | | |
162 | | // This was the final state change; the salt is no longer needed. |
163 | 0 | zap(m_salt); |
164 | |
|
165 | 0 | m_state = State::Completed; |
166 | 0 | } |
167 | | |
168 | | std::vector<uint8_t> Cipher_State::current_nonce(const uint64_t seq_no, const secure_vector<uint8_t>& iv) const |
169 | 0 | { |
170 | | // RFC 8446 5.3 |
171 | | // The per-record nonce for the AEAD construction is formed as follows: |
172 | | // |
173 | | // 1. The 64-bit record sequence number is encoded in network byte |
174 | | // order and padded to the left with zeros to iv_length. |
175 | | // |
176 | | // 2. The padded sequence number is XORed with either the static |
177 | | // client_write_iv or server_write_iv (depending on the role). |
178 | 0 | std::vector<uint8_t> nonce(NONCE_LENGTH); |
179 | 0 | store_be(seq_no, nonce.data() + (NONCE_LENGTH-sizeof(seq_no))); |
180 | 0 | xor_buf(nonce, iv.data(), iv.size()); |
181 | 0 | return nonce; |
182 | 0 | } |
183 | | |
184 | | uint64_t Cipher_State::encrypt_record_fragment(const std::vector<uint8_t>& header, secure_vector<uint8_t>& fragment) |
185 | 0 | { |
186 | 0 | m_encrypt->set_key(m_write_key); |
187 | 0 | m_encrypt->set_associated_data_vec(header); |
188 | 0 | m_encrypt->start(current_nonce(m_write_seq_no, m_write_iv)); |
189 | 0 | m_encrypt->finish(fragment); |
190 | |
|
191 | 0 | return m_write_seq_no++; |
192 | 0 | } |
193 | | |
194 | | uint64_t Cipher_State::decrypt_record_fragment(const std::vector<uint8_t>& header, |
195 | | secure_vector<uint8_t>& encrypted_fragment) |
196 | 0 | { |
197 | 0 | BOTAN_ARG_CHECK(encrypted_fragment.size() >= m_decrypt->minimum_final_size(), |
198 | 0 | "fragment too short to decrypt"); |
199 | |
|
200 | 0 | m_decrypt->set_key(m_read_key); |
201 | 0 | m_decrypt->set_associated_data_vec(header); |
202 | 0 | m_decrypt->start(current_nonce(m_read_seq_no, m_read_iv)); |
203 | |
|
204 | 0 | m_decrypt->finish(encrypted_fragment); |
205 | |
|
206 | 0 | return m_read_seq_no++; |
207 | 0 | } |
208 | | |
209 | | size_t Cipher_State::encrypt_output_length(const size_t input_length) const |
210 | 0 | { |
211 | 0 | return m_encrypt->output_length(input_length); |
212 | 0 | } |
213 | | |
214 | | size_t Cipher_State::decrypt_output_length(const size_t input_length) const |
215 | 0 | { |
216 | 0 | return m_decrypt->output_length(input_length); |
217 | 0 | } |
218 | | |
219 | | size_t Cipher_State::minimum_decryption_input_length() const |
220 | 0 | { |
221 | 0 | return m_decrypt->minimum_final_size(); |
222 | 0 | } |
223 | | |
224 | | std::vector<uint8_t> Cipher_State::finished_mac(const Transcript_Hash& transcript_hash) const |
225 | 0 | { |
226 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); |
227 | |
|
228 | 0 | auto hmac = HMAC(m_hash->new_object()); |
229 | 0 | hmac.set_key(m_finished_key); |
230 | 0 | hmac.update(transcript_hash); |
231 | 0 | return hmac.final_stdvec(); |
232 | 0 | } |
233 | | |
234 | | bool Cipher_State::verify_peer_finished_mac(const Transcript_Hash& transcript_hash, |
235 | | const std::vector<uint8_t>& peer_mac) const |
236 | 0 | { |
237 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); |
238 | |
|
239 | 0 | auto hmac = HMAC(m_hash->new_object()); |
240 | 0 | hmac.set_key(m_peer_finished_key); |
241 | 0 | hmac.update(transcript_hash); |
242 | 0 | return hmac.verify_mac(peer_mac); |
243 | 0 | } |
244 | | |
245 | | secure_vector<uint8_t> Cipher_State::psk(const std::vector<uint8_t>& nonce) const |
246 | 0 | { |
247 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::Completed); |
248 | |
|
249 | 0 | return derive_secret(m_resumption_master_secret, "resumption", nonce); |
250 | 0 | } |
251 | | |
252 | | |
253 | | secure_vector<uint8_t> Cipher_State::export_key(const std::string& label, |
254 | | const std::string& context, |
255 | | size_t length) const |
256 | 0 | { |
257 | 0 | BOTAN_ASSERT_NOMSG(can_export_keys()); |
258 | |
|
259 | 0 | m_hash->update(context); |
260 | 0 | const auto context_hash = m_hash->final_stdvec(); |
261 | 0 | return hkdf_expand_label(derive_secret(m_exporter_master_secret, label, empty_hash()), |
262 | 0 | "exporter", context_hash, length); |
263 | 0 | } |
264 | | |
265 | | |
266 | | namespace { |
267 | | |
268 | | std::unique_ptr<MessageAuthenticationCode> create_hmac(const Ciphersuite& cipher) |
269 | 0 | { |
270 | 0 | return std::make_unique<HMAC>(HashFunction::create_or_throw(cipher.prf_algo())); |
271 | 0 | } |
272 | | |
273 | | } |
274 | | |
275 | | Cipher_State::Cipher_State(Connection_Side whoami, const Ciphersuite& cipher) |
276 | | : m_state(State::Uninitialized) |
277 | | , m_connection_side(whoami) |
278 | | , m_encrypt(AEAD_Mode::create(cipher.cipher_algo(), ENCRYPTION)) |
279 | | , m_decrypt(AEAD_Mode::create(cipher.cipher_algo(), DECRYPTION)) |
280 | | , m_extract(std::make_unique<HKDF_Extract>(create_hmac(cipher))) |
281 | | , m_expand(std::make_unique<HKDF_Expand>(create_hmac(cipher))) |
282 | | , m_hash(HashFunction::create_or_throw(cipher.prf_algo())) |
283 | | , m_salt(m_hash->output_length(), 0x00) |
284 | | , m_write_seq_no(0) |
285 | 0 | , m_read_seq_no(0) {} |
286 | | |
287 | 0 | Cipher_State::~Cipher_State() = default; |
288 | | |
289 | | void Cipher_State::advance_without_psk() |
290 | 0 | { |
291 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::Uninitialized); |
292 | |
|
293 | 0 | const auto early_secret = hkdf_extract(secure_vector<uint8_t>(m_hash->output_length(), 0x00)); |
294 | 0 | m_salt = derive_secret(early_secret, "derived", empty_hash()); |
295 | |
|
296 | 0 | m_state = State::EarlyTraffic; |
297 | 0 | } |
298 | | |
299 | | void Cipher_State::advance_with_server_hello(secure_vector<uint8_t>&& shared_secret, |
300 | | const Transcript_Hash& transcript_hash) |
301 | 0 | { |
302 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::EarlyTraffic); |
303 | |
|
304 | 0 | const auto handshake_secret = hkdf_extract(std::move(shared_secret)); |
305 | |
|
306 | 0 | const auto client_handshake_traffic_secret = derive_secret(handshake_secret, "c hs traffic", transcript_hash); |
307 | 0 | const auto server_handshake_traffic_secret = derive_secret(handshake_secret, "s hs traffic", transcript_hash); |
308 | |
|
309 | 0 | if(m_connection_side == Connection_Side::SERVER) |
310 | 0 | { |
311 | 0 | derive_read_traffic_key(client_handshake_traffic_secret, true); |
312 | 0 | derive_write_traffic_key(server_handshake_traffic_secret, true); |
313 | 0 | } |
314 | 0 | else |
315 | 0 | { |
316 | 0 | derive_read_traffic_key(server_handshake_traffic_secret, true); |
317 | 0 | derive_write_traffic_key(client_handshake_traffic_secret, true); |
318 | 0 | } |
319 | |
|
320 | 0 | m_salt = derive_secret(handshake_secret, "derived", empty_hash()); |
321 | |
|
322 | 0 | m_state = State::HandshakeTraffic; |
323 | 0 | } |
324 | | |
325 | | void Cipher_State::derive_write_traffic_key(const secure_vector<uint8_t>& traffic_secret, |
326 | | const bool handshake_traffic_secret) |
327 | 0 | { |
328 | 0 | m_write_key = hkdf_expand_label(traffic_secret, "key", {}, m_encrypt->minimum_keylength()); |
329 | 0 | m_write_iv = hkdf_expand_label(traffic_secret, "iv", {}, NONCE_LENGTH); |
330 | 0 | m_write_seq_no = 0; |
331 | |
|
332 | 0 | if(handshake_traffic_secret) |
333 | 0 | { |
334 | | // Key derivation for the MAC in the "Finished" handshake message as described in RFC 8446 4.4.4 |
335 | | // (will be cleared in advance_with_server_finished()) |
336 | 0 | m_finished_key = hkdf_expand_label(traffic_secret, "finished", {}, m_hash->output_length()); |
337 | 0 | } |
338 | 0 | } |
339 | | |
340 | | void Cipher_State::derive_read_traffic_key(const secure_vector<uint8_t>& traffic_secret, |
341 | | const bool handshake_traffic_secret) |
342 | 0 | { |
343 | 0 | m_read_key = hkdf_expand_label(traffic_secret, "key", {}, m_encrypt->minimum_keylength()); |
344 | 0 | m_read_iv = hkdf_expand_label(traffic_secret, "iv", {}, NONCE_LENGTH); |
345 | 0 | m_read_seq_no = 0; |
346 | |
|
347 | 0 | if(handshake_traffic_secret) |
348 | 0 | { |
349 | | // Key derivation for the MAC in the "Finished" handshake message as described in RFC 8446 4.4.4 |
350 | | // (will be cleared in advance_with_server_finished()) |
351 | 0 | m_peer_finished_key = hkdf_expand_label(traffic_secret, "finished", {}, m_hash->output_length()); |
352 | 0 | } |
353 | 0 | } |
354 | | |
355 | | secure_vector<uint8_t> Cipher_State::hkdf_extract(secure_vector<uint8_t>&& ikm) const |
356 | 0 | { |
357 | 0 | return m_extract->derive_key(m_hash->output_length(), ikm, m_salt, std::vector<uint8_t>()); |
358 | 0 | } |
359 | | |
360 | | secure_vector<uint8_t> Cipher_State::hkdf_expand_label( |
361 | | const secure_vector<uint8_t>& secret, |
362 | | const std::string& label, |
363 | | const std::vector<uint8_t>& context, |
364 | | const size_t length) const |
365 | 0 | { |
366 | | // assemble (serialized) HkdfLabel |
367 | 0 | secure_vector<uint8_t> hkdf_label; |
368 | 0 | hkdf_label.reserve(2 /* length */ + |
369 | 0 | (label.size() + |
370 | 0 | 6 /* 'tls13 ' */ + |
371 | 0 | 1 /* length field*/) + |
372 | 0 | (context.size() + |
373 | 0 | 1 /* length field*/)); |
374 | | |
375 | | // length |
376 | 0 | BOTAN_ARG_CHECK(length <= std::numeric_limits<uint16_t>::max(), "invalid length"); |
377 | 0 | const auto len = static_cast<uint16_t>(length); |
378 | 0 | hkdf_label.push_back(get_byte<0>(len)); |
379 | 0 | hkdf_label.push_back(get_byte<1>(len)); |
380 | | |
381 | | // label |
382 | 0 | const std::string prefix = "tls13 "; |
383 | 0 | BOTAN_ARG_CHECK(prefix.size() + label.size() <= 255, "label too large"); |
384 | 0 | hkdf_label.push_back(static_cast<uint8_t>(prefix.size() + label.size())); |
385 | 0 | hkdf_label.insert(hkdf_label.end(), prefix.cbegin(), prefix.cend()); |
386 | 0 | hkdf_label.insert(hkdf_label.end(), label.cbegin(), label.cend()); |
387 | | |
388 | | // context |
389 | 0 | BOTAN_ARG_CHECK(context.size() <= 255, "context too large"); |
390 | 0 | hkdf_label.push_back(static_cast<uint8_t>(context.size())); |
391 | 0 | hkdf_label.insert(hkdf_label.end(), context.cbegin(), context.cend()); |
392 | | |
393 | | // HKDF-Expand |
394 | 0 | return m_expand->derive_key(length, secret, hkdf_label, std::vector<uint8_t>() /* just pleasing botan's interface */); |
395 | 0 | } |
396 | | |
397 | | secure_vector<uint8_t> Cipher_State::derive_secret( |
398 | | const secure_vector<uint8_t>& secret, |
399 | | const std::string& label, |
400 | | const Transcript_Hash& messages_hash) const |
401 | 0 | { |
402 | 0 | return hkdf_expand_label(secret, label, messages_hash, m_hash->output_length()); |
403 | 0 | } |
404 | | |
405 | | std::vector<uint8_t> Cipher_State::empty_hash() const |
406 | 0 | { |
407 | 0 | m_hash->update(""); |
408 | 0 | return m_hash->final_stdvec(); |
409 | 0 | } |
410 | | |
411 | | void Cipher_State::update_read_keys() |
412 | 0 | { |
413 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::ApplicationTraffic || |
414 | 0 | m_state == State::Completed); |
415 | |
|
416 | 0 | m_read_application_traffic_secret = |
417 | 0 | hkdf_expand_label(m_read_application_traffic_secret, "traffic upd", {}, m_hash->output_length()); |
418 | |
|
419 | 0 | derive_read_traffic_key(m_read_application_traffic_secret); |
420 | 0 | } |
421 | | |
422 | | void Cipher_State::update_write_keys() |
423 | 0 | { |
424 | 0 | BOTAN_ASSERT_NOMSG(m_state == State::ApplicationTraffic || |
425 | 0 | m_state == State::Completed); |
426 | 0 | m_write_application_traffic_secret = |
427 | 0 | hkdf_expand_label(m_write_application_traffic_secret, "traffic upd", {}, m_hash->output_length()); |
428 | |
|
429 | 0 | derive_write_traffic_key(m_write_application_traffic_secret); |
430 | 0 | } |
431 | | |
432 | | void Cipher_State::clear_read_keys() |
433 | 0 | { |
434 | 0 | zap(m_read_key); |
435 | 0 | zap(m_read_iv); |
436 | 0 | zap(m_read_application_traffic_secret); |
437 | 0 | } |
438 | | |
439 | | void Cipher_State::clear_write_keys() |
440 | 0 | { |
441 | 0 | zap(m_write_key); |
442 | 0 | zap(m_write_iv); |
443 | 0 | zap(m_write_application_traffic_secret); |
444 | 0 | } |
445 | | |
446 | | } // namespace Botan::TLS |