9#include <openssl/evp.h>
10#include <openssl/kdf.h>
11#include <openssl/rand.h>
25auto get_openssl_error_string() -> std::string
27 return get_openssl_error();
30constexpr std::array<uint8_t, 9> client_initial_label = {
31 'c',
'l',
'i',
'e',
'n',
't',
' ',
'i',
'n'
34constexpr std::array<uint8_t, 9> server_initial_label = {
35 's',
'e',
'r',
'v',
'e',
'r',
' ',
'i',
'n'
38constexpr std::array<uint8_t, 8> quic_key_label = {
39 'q',
'u',
'i',
'c',
' ',
'k',
'e',
'y'
42constexpr std::array<uint8_t, 7> quic_iv_label = {
43 'q',
'u',
'i',
'c',
' ',
'i',
'v'
46constexpr std::array<uint8_t, 7> quic_hp_label = {
47 'q',
'u',
'i',
'c',
' ',
'h',
'p'
57 std::span<const uint8_t> ikm)
60 std::array<uint8_t, secret_size> prk{};
61 size_t prk_len = prk.size();
63 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF,
nullptr);
67 -1,
"Failed to create HKDF context",
"quic::hkdf");
70 int ret = EVP_PKEY_derive_init(pctx);
73 EVP_PKEY_CTX_free(pctx);
75 -1,
"HKDF derive init failed",
"quic::hkdf", get_openssl_error_string());
78 ret = EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256());
81 EVP_PKEY_CTX_free(pctx);
83 -1,
"HKDF set md failed",
"quic::hkdf", get_openssl_error_string());
86 ret = EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt.data(),
87 static_cast<int>(salt.size()));
90 EVP_PKEY_CTX_free(pctx);
92 -1,
"HKDF set salt failed",
"quic::hkdf", get_openssl_error_string());
95 ret = EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm.data(),
96 static_cast<int>(ikm.size()));
99 EVP_PKEY_CTX_free(pctx);
101 -1,
"HKDF set key failed",
"quic::hkdf", get_openssl_error_string());
104 ret = EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY);
107 EVP_PKEY_CTX_free(pctx);
109 -1,
"HKDF set mode failed",
"quic::hkdf", get_openssl_error_string());
112 ret = EVP_PKEY_derive(pctx, prk.data(), &prk_len);
113 EVP_PKEY_CTX_free(pctx);
118 -1,
"HKDF extract failed",
"quic::hkdf", get_openssl_error_string());
121 return ok(std::move(prk));
125 std::span<const uint8_t> info,
129 std::vector<uint8_t> okm(length);
131 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF,
nullptr);
135 -1,
"Failed to create HKDF context",
"quic::hkdf");
138 int ret = EVP_PKEY_derive_init(pctx);
141 EVP_PKEY_CTX_free(pctx);
143 -1,
"HKDF derive init failed",
"quic::hkdf", get_openssl_error_string());
146 ret = EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256());
149 EVP_PKEY_CTX_free(pctx);
151 -1,
"HKDF set md failed",
"quic::hkdf", get_openssl_error_string());
154 ret = EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY);
157 EVP_PKEY_CTX_free(pctx);
159 -1,
"HKDF set mode failed",
"quic::hkdf", get_openssl_error_string());
162 ret = EVP_PKEY_CTX_set1_hkdf_key(pctx, prk.data(),
163 static_cast<int>(prk.size()));
166 EVP_PKEY_CTX_free(pctx);
168 -1,
"HKDF set key failed",
"quic::hkdf", get_openssl_error_string());
171 ret = EVP_PKEY_CTX_add1_hkdf_info(pctx, info.data(),
172 static_cast<int>(info.size()));
175 EVP_PKEY_CTX_free(pctx);
177 -1,
"HKDF set info failed",
"quic::hkdf", get_openssl_error_string());
180 ret = EVP_PKEY_derive(pctx, okm.data(), &length);
181 EVP_PKEY_CTX_free(pctx);
186 -1,
"HKDF expand failed",
"quic::hkdf", get_openssl_error_string());
190 return ok(std::move(okm));
194 const std::string& label,
195 std::span<const uint8_t> context,
206 const std::string prefix =
"tls13 ";
207 std::vector<uint8_t> hkdf_label;
208 hkdf_label.reserve(2 + 1 + prefix.size() + label.size() + 1 + context.size());
211 hkdf_label.push_back(
static_cast<uint8_t
>((length >> 8) & 0xFF));
212 hkdf_label.push_back(
static_cast<uint8_t
>(length & 0xFF));
215 size_t label_len = prefix.size() + label.size();
216 hkdf_label.push_back(
static_cast<uint8_t
>(label_len));
217 for (
char c : prefix)
219 hkdf_label.push_back(
static_cast<uint8_t
>(c));
223 hkdf_label.push_back(
static_cast<uint8_t
>(c));
227 hkdf_label.push_back(
static_cast<uint8_t
>(context.size()));
228 hkdf_label.insert(hkdf_label.end(), context.begin(), context.end());
230 return expand(secret, hkdf_label, length);
241 std::span<const uint8_t> salt;
252 auto initial_secret_result =
hkdf::extract(salt, dest_cid.data());
253 if (initial_secret_result.is_err())
256 initial_secret_result.error().code,
257 "Failed to derive initial secret",
258 "quic::initial_keys",
259 initial_secret_result.error().message);
262 auto& initial_secret = initial_secret_result.value();
267 std::string(
reinterpret_cast<const char*
>(client_initial_label.data()),
268 client_initial_label.size()),
271 if (client_secret_result.is_err())
274 client_secret_result.error().code,
275 "Failed to derive client initial secret",
276 "quic::initial_keys",
277 client_secret_result.error().message);
283 std::string(
reinterpret_cast<const char*
>(server_initial_label.data()),
284 server_initial_label.size()),
287 if (server_secret_result.is_err())
290 server_secret_result.error().code,
291 "Failed to derive server initial secret",
292 "quic::initial_keys",
293 server_secret_result.error().message);
297 auto client_keys_result = derive_keys(client_secret_result.value(),
true);
298 if (client_keys_result.is_err())
301 client_keys_result.error().code,
302 "Failed to derive client keys",
303 "quic::initial_keys",
304 client_keys_result.error().message);
308 auto server_keys_result = derive_keys(server_secret_result.value(),
false);
309 if (server_keys_result.is_err())
312 server_keys_result.error().code,
313 "Failed to derive server keys",
314 "quic::initial_keys",
315 server_keys_result.error().message);
319 auto& client_keys = client_keys_result.value();
320 auto& server_keys = server_keys_result.value();
322 std::copy(client_secret_result.value().begin(),
323 client_secret_result.value().end(),
324 client_keys.secret.begin());
325 std::copy(server_secret_result.value().begin(),
326 server_secret_result.value().end(),
327 server_keys.secret.begin());
333 result.
write = std::move(client_keys);
334 result.
read = std::move(server_keys);
336 return ok(std::move(result));
343 (void)is_client_keys;
350 std::string(
reinterpret_cast<const char*
>(quic_key_label.data()),
351 quic_key_label.size()),
354 if (key_result.is_err())
357 key_result.error().code,
358 "Failed to derive AEAD key",
359 "quic::initial_keys",
360 key_result.error().message);
362 std::copy(key_result.value().begin(), key_result.value().end(),
368 std::string(
reinterpret_cast<const char*
>(quic_iv_label.data()),
369 quic_iv_label.size()),
372 if (iv_result.is_err())
375 iv_result.error().code,
376 "Failed to derive IV",
377 "quic::initial_keys",
378 iv_result.error().message);
380 std::copy(iv_result.value().begin(), iv_result.value().end(),
386 std::string(
reinterpret_cast<const char*
>(quic_hp_label.data()),
387 quic_hp_label.size()),
390 if (hp_result.is_err())
393 hp_result.error().code,
394 "Failed to derive HP key",
395 "quic::initial_keys",
396 hp_result.error().message);
398 std::copy(hp_result.value().begin(), hp_result.value().end(),
401 return ok(std::move(keys));
410 -> std::array<uint8_t, aead_iv_size>
412 std::array<uint8_t, aead_iv_size> nonce{};
413 std::copy(iv.begin(), iv.end(), nonce.begin());
416 for (
size_t i = 0; i < 8; ++i)
426 std::span<const uint8_t> header,
427 std::span<const uint8_t> payload,
434 std::vector<uint8_t> output;
435 output.reserve(header.size() + payload.size() +
aead_tag_size);
436 output.insert(output.end(), header.begin(), header.end());
437 output.resize(output.size() + payload.size() +
aead_tag_size);
439 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
443 -1,
"Failed to create cipher context",
"quic::packet_protection");
446 int ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(),
nullptr,
447 keys.key.data(), nonce.data());
450 EVP_CIPHER_CTX_free(ctx);
452 -1,
"AES-GCM encrypt init failed",
"quic::packet_protection",
453 get_openssl_error_string());
458 ret = EVP_EncryptUpdate(ctx,
nullptr, &len, header.data(),
459 static_cast<int>(header.size()));
462 EVP_CIPHER_CTX_free(ctx);
464 -1,
"AES-GCM AAD update failed",
"quic::packet_protection",
465 get_openssl_error_string());
469 ret = EVP_EncryptUpdate(ctx, output.data() + header.size(), &len,
470 payload.data(),
static_cast<int>(payload.size()));
473 EVP_CIPHER_CTX_free(ctx);
475 -1,
"AES-GCM encrypt failed",
"quic::packet_protection",
476 get_openssl_error_string());
479 int ciphertext_len = len;
481 ret = EVP_EncryptFinal_ex(ctx, output.data() + header.size() + len, &len);
484 EVP_CIPHER_CTX_free(ctx);
486 -1,
"AES-GCM encrypt final failed",
"quic::packet_protection",
487 get_openssl_error_string());
489 ciphertext_len += len;
492 ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG,
aead_tag_size,
493 output.data() + header.size() + ciphertext_len);
496 EVP_CIPHER_CTX_free(ctx);
498 -1,
"AES-GCM get tag failed",
"quic::packet_protection",
499 get_openssl_error_string());
502 EVP_CIPHER_CTX_free(ctx);
503 output.resize(header.size() + ciphertext_len +
aead_tag_size);
505 return ok(std::move(output));
509 std::span<const uint8_t> packet,
510 size_t header_length,
517 -1,
"Packet too short for decryption",
"quic::packet_protection");
522 auto header = packet.subspan(0, header_length);
523 auto ciphertext = packet.subspan(header_length,
527 std::vector<uint8_t> plaintext(ciphertext.size());
529 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
533 -1,
"Failed to create cipher context",
"quic::packet_protection");
536 int ret = EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(),
nullptr,
537 keys.key.data(), nonce.data());
540 EVP_CIPHER_CTX_free(ctx);
542 -1,
"AES-GCM decrypt init failed",
"quic::packet_protection",
543 get_openssl_error_string());
548 ret = EVP_DecryptUpdate(ctx,
nullptr, &len, header.data(),
549 static_cast<int>(header.size()));
552 EVP_CIPHER_CTX_free(ctx);
554 -1,
"AES-GCM AAD update failed",
"quic::packet_protection",
555 get_openssl_error_string());
559 ret = EVP_DecryptUpdate(ctx, plaintext.data(), &len,
560 ciphertext.data(),
static_cast<int>(ciphertext.size()));
563 EVP_CIPHER_CTX_free(ctx);
565 -1,
"AES-GCM decrypt failed",
"quic::packet_protection",
566 get_openssl_error_string());
569 int plaintext_len = len;
572 ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
aead_tag_size,
573 const_cast<uint8_t*
>(tag.data()));
576 EVP_CIPHER_CTX_free(ctx);
578 -1,
"AES-GCM set tag failed",
"quic::packet_protection",
579 get_openssl_error_string());
582 ret = EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len);
583 EVP_CIPHER_CTX_free(ctx);
588 -1,
"AES-GCM authentication failed",
"quic::packet_protection");
591 plaintext_len += len;
592 plaintext.resize(plaintext_len);
594 std::vector<uint8_t> header_copy(header.begin(), header.end());
595 return ok(std::make_pair(std::move(header_copy), std::move(plaintext)));
599 std::span<const uint8_t> sample)
605 -1,
"Sample too short for HP mask",
"quic::packet_protection");
608 std::array<uint8_t, 16> mask_full{};
610 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
614 -1,
"Failed to create cipher context",
"quic::packet_protection");
617 int ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(),
nullptr,
618 hp_key.data(),
nullptr);
621 EVP_CIPHER_CTX_free(ctx);
623 -1,
"AES-ECB init failed",
"quic::packet_protection",
624 get_openssl_error_string());
627 EVP_CIPHER_CTX_set_padding(ctx, 0);
630 ret = EVP_EncryptUpdate(ctx, mask_full.data(), &len,
634 EVP_CIPHER_CTX_free(ctx);
636 -1,
"AES-ECB encrypt failed",
"quic::packet_protection",
637 get_openssl_error_string());
640 EVP_CIPHER_CTX_free(ctx);
643 std::array<uint8_t, 5> mask{};
644 std::copy(mask_full.begin(), mask_full.begin() + 5, mask.begin());
646 return ok(std::move(mask));
650 std::span<uint8_t> header,
653 std::span<const uint8_t> sample)
656 auto mask_result = generate_hp_mask(keys.hp_key, sample);
657 if (mask_result.is_err())
660 mask_result.error().message,
664 auto& mask = mask_result.value();
667 if ((header[0] & 0x80) != 0)
670 header[0] ^= (mask[0] & 0x0F);
675 header[0] ^= (mask[0] & 0x1F);
679 for (
size_t i = 0; i < pn_length; ++i)
681 header[pn_offset + i] ^= mask[1 + i];
688 std::span<uint8_t> header,
690 std::span<const uint8_t> sample)
693 auto mask_result = generate_hp_mask(keys.hp_key, sample);
694 if (mask_result.is_err())
697 mask_result.error().code,
698 mask_result.error().message,
702 auto& mask = mask_result.value();
705 if ((header[0] & 0x80) != 0)
708 header[0] ^= (mask[0] & 0x0F);
713 header[0] ^= (mask[0] & 0x1F);
717 size_t pn_length = (header[0] & 0x03) + 1;
720 for (
size_t i = 0; i < pn_length; ++i)
722 header[pn_offset + i] ^= mask[1 + i];
725 return ok(std::make_pair(header[0], pn_length));
773 : impl_(std::make_unique<
impl>())
784 impl_->is_server =
false;
786 impl_->ssl_ctx = SSL_CTX_new(TLS_client_method());
789 return error_void(-1,
"Failed to create SSL context",
"quic::crypto",
790 get_openssl_error_string());
794 SSL_CTX_set_min_proto_version(impl_->ssl_ctx, TLS1_3_VERSION);
795 SSL_CTX_set_max_proto_version(impl_->ssl_ctx, TLS1_3_VERSION);
797 impl_->ssl = SSL_new(impl_->ssl_ctx);
800 return error_void(-1,
"Failed to create SSL object",
"quic::crypto",
801 get_openssl_error_string());
805 if (!server_name.empty())
807 SSL_set_tlsext_host_name(impl_->ssl, server_name.c_str());
811 impl_->rbio = BIO_new(BIO_s_mem());
812 impl_->wbio = BIO_new(BIO_s_mem());
813 if (!impl_->rbio || !impl_->wbio)
815 return error_void(-1,
"Failed to create BIO objects",
"quic::crypto");
818 BIO_set_nbio(impl_->rbio, 1);
819 BIO_set_nbio(impl_->wbio, 1);
821 SSL_set_bio(impl_->ssl, impl_->rbio, impl_->wbio);
822 SSL_set_connect_state(impl_->ssl);
830 impl_->is_server =
true;
832 impl_->ssl_ctx = SSL_CTX_new(TLS_server_method());
835 return error_void(-1,
"Failed to create SSL context",
"quic::crypto",
836 get_openssl_error_string());
840 SSL_CTX_set_min_proto_version(impl_->ssl_ctx, TLS1_3_VERSION);
841 SSL_CTX_set_max_proto_version(impl_->ssl_ctx, TLS1_3_VERSION);
844 if (SSL_CTX_use_certificate_file(impl_->ssl_ctx, cert_file.c_str(),
845 SSL_FILETYPE_PEM) != 1)
847 return error_void(-1,
"Failed to load certificate",
"quic::crypto",
848 get_openssl_error_string());
852 if (SSL_CTX_use_PrivateKey_file(impl_->ssl_ctx, key_file.c_str(),
853 SSL_FILETYPE_PEM) != 1)
855 return error_void(-1,
"Failed to load private key",
"quic::crypto",
856 get_openssl_error_string());
859 impl_->ssl = SSL_new(impl_->ssl_ctx);
862 return error_void(-1,
"Failed to create SSL object",
"quic::crypto",
863 get_openssl_error_string());
867 impl_->rbio = BIO_new(BIO_s_mem());
868 impl_->wbio = BIO_new(BIO_s_mem());
869 if (!impl_->rbio || !impl_->wbio)
871 return error_void(-1,
"Failed to create BIO objects",
"quic::crypto");
874 BIO_set_nbio(impl_->rbio, 1);
875 BIO_set_nbio(impl_->wbio, 1);
877 SSL_set_bio(impl_->ssl, impl_->rbio, impl_->wbio);
878 SSL_set_accept_state(impl_->ssl);
887 if (keys_result.is_err())
890 keys_result.error().message,
894 auto& keys = keys_result.value();
896 if (impl_->is_server)
913 std::span<const uint8_t> data)
919 int written = BIO_write(impl_->rbio, data.data(),
920 static_cast<int>(data.size()));
924 -1,
"Failed to write to BIO",
"quic::crypto");
928 int result = SSL_do_handshake(impl_->ssl);
931 impl_->handshake_complete =
true;
936 int err = SSL_get_error(impl_->ssl, result);
937 if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
940 -1,
"SSL handshake failed",
"quic::crypto",
941 get_openssl_error_string());
946 std::vector<uint8_t> output;
947 int pending = BIO_ctrl_pending(impl_->wbio);
950 output.resize(
static_cast<size_t>(pending));
951 int read = BIO_read(impl_->wbio, output.data(), pending);
954 output.resize(
static_cast<size_t>(read));
962 return ok(std::move(output));
967 int result = SSL_do_handshake(impl_->ssl);
970 impl_->handshake_complete =
true;
974 int err = SSL_get_error(impl_->ssl, result);
975 if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
978 -1,
"SSL handshake start failed",
"quic::crypto",
979 get_openssl_error_string());
984 std::vector<uint8_t> output;
985 int pending = BIO_ctrl_pending(impl_->wbio);
988 output.resize(
static_cast<size_t>(pending));
989 int read = BIO_read(impl_->wbio, output.data(), pending);
992 output.resize(
static_cast<size_t>(read));
1000 return ok(std::move(output));
1016 auto it = impl_->write_keys.find(level);
1017 if (it == impl_->write_keys.end())
1020 -1,
"Write keys not available for level",
"quic::crypto",
1029 auto it = impl_->read_keys.find(level);
1030 if (it == impl_->read_keys.end())
1033 -1,
"Read keys not available for level",
"quic::crypto",
1054 if (!impl_->handshake_complete)
1056 return error_void(-1,
"Handshake not complete",
"quic::crypto");
1060 if (it == impl_->write_keys.end())
1062 return error_void(-1,
"Application keys not available",
"quic::crypto");
1066 auto& old_secret = it->second.secret;
1069 if (new_secret_result.is_err())
1071 return error_void(new_secret_result.error().code,
1072 new_secret_result.error().message,
1079 if (new_keys_result.is_err())
1081 return error_void(new_keys_result.error().code,
1082 new_keys_result.error().message,
1086 auto& new_keys = new_keys_result.value();
1087 std::copy(new_secret_result.value().begin(),
1088 new_secret_result.value().end(),
1089 new_keys.secret.begin());
1092 impl_->key_phase = 1 - impl_->key_phase;
1105 if (protocols.empty())
1111 impl_->alpn_data.clear();
1112 for (
const auto& proto : protocols)
1114 if (proto.size() > 255)
1116 return error_void(-1,
"ALPN protocol too long",
"quic::crypto");
1118 impl_->alpn_data.push_back(
static_cast<uint8_t
>(proto.size()));
1119 impl_->alpn_data.insert(impl_->alpn_data.end(), proto.begin(), proto.end());
1122 if (SSL_CTX_set_alpn_protos(impl_->ssl_ctx, impl_->alpn_data.data(),
1123 static_cast<unsigned int>(impl_->alpn_data.size())) != 0)
1125 return error_void(-1,
"Failed to set ALPN protocols",
"quic::crypto",
1126 get_openssl_error_string());
1154 if (ticket_data.empty())
1156 return error_void(-1,
"Empty session ticket",
"quic::crypto");
1159 impl_->session_ticket_data.assign(ticket_data.begin(), ticket_data.end());
1165 if (impl_->session_ticket_data.empty())
1167 return error_void(-1,
"Session ticket must be set before enabling early data",
1171 impl_->max_early_data_size = max_early_data;
1172 impl_->early_data_enabled =
true;
1184 if (impl_->session_ticket_data.empty())
1186 return error_void(-1,
"No session ticket available",
"quic::crypto");
1201 impl_->session_ticket_data);
1203 if (early_secret_result.is_err())
1205 return error_void(early_secret_result.error().code,
1206 "Failed to derive early secret",
1208 early_secret_result.error().message);
1211 auto& early_secret = early_secret_result.value();
1220 if (client_early_secret_result.is_err())
1222 return error_void(client_early_secret_result.error().code,
1223 "Failed to derive client early secret",
1225 client_early_secret_result.error().message);
1230 client_early_secret_result.value(),
1233 if (zero_rtt_keys_result.is_err())
1235 return error_void(zero_rtt_keys_result.error().code,
1236 "Failed to derive 0-RTT keys",
1238 zero_rtt_keys_result.error().message);
1241 auto& zero_rtt_keys = zero_rtt_keys_result.value();
1244 std::copy(client_early_secret_result.value().begin(),
1245 client_early_secret_result.value().end(),
1246 zero_rtt_keys.secret.begin());
1251 if (impl_->is_server)
1260 impl_->has_zero_rtt_keys =
true;
QUIC Connection ID (RFC 9000 Section 5.1)
static auto expand_label(std::span< const uint8_t > secret, const std::string &label, std::span< const uint8_t > context, size_t length) -> Result< std::vector< uint8_t > >
HKDF-Expand-Label function (TLS 1.3 style)
static auto extract(std::span< const uint8_t > salt, std::span< const uint8_t > ikm) -> Result< std::array< uint8_t, secret_size > >
HKDF-Extract function.
static auto expand(std::span< const uint8_t > prk, std::span< const uint8_t > info, size_t length) -> Result< std::vector< uint8_t > >
HKDF-Expand function.
static auto derive_keys(std::span< const uint8_t > initial_secret, bool is_client_keys) -> Result< quic_keys >
Derive keys from an initial secret.
static auto derive(const connection_id &dest_cid, uint32_t version=0x00000001) -> Result< key_pair >
Derive client and server initial keys.
QUIC packet number utilities (RFC 9000 Section 17.1)
static auto unprotect_header(const quic_keys &keys, std::span< uint8_t > header, size_t pn_offset, std::span< const uint8_t > sample) -> Result< std::pair< uint8_t, size_t > >
Remove header protection.
static auto unprotect(const quic_keys &keys, std::span< const uint8_t > packet, size_t header_length, uint64_t packet_number) -> Result< std::pair< std::vector< uint8_t >, std::vector< uint8_t > > >
Unprotect (decrypt) a QUIC packet.
static auto make_nonce(std::span< const uint8_t > iv, uint64_t packet_number) -> std::array< uint8_t, aead_iv_size >
Construct nonce from IV and packet number.
static auto protect_header(const quic_keys &keys, std::span< uint8_t > header, size_t pn_offset, size_t pn_length, std::span< const uint8_t > sample) -> VoidResult
Apply header protection.
static auto protect(const quic_keys &keys, std::span< const uint8_t > header, std::span< const uint8_t > payload, uint64_t packet_number) -> Result< std::vector< uint8_t > >
Protect (encrypt) a QUIC packet.
static auto generate_hp_mask(std::span< const uint8_t > hp_key, std::span< const uint8_t > sample) -> Result< std::array< uint8_t, 5 > >
Generate header protection mask using AES-ECB.
QUIC-TLS integration handler (RFC 9001)
auto init_server(const std::string &cert_file, const std::string &key_file) -> VoidResult
Initialize as server.
auto update_keys() -> VoidResult
Perform a key update (1-RTT only)
std::unique_ptr< impl > impl_
~quic_crypto()
Destructor (cleans up OpenSSL resources)
auto start_handshake() -> Result< std::vector< uint8_t > >
Start the handshake (generate initial CRYPTO data)
quic_crypto()
Default constructor.
auto get_read_keys(encryption_level level) const -> Result< quic_keys >
Get read keys for an encryption level.
auto get_alpn() const -> std::string
Get the negotiated ALPN protocol.
auto is_early_data_accepted() const noexcept -> bool
Check if 0-RTT early data was accepted by the server.
auto get_write_keys(encryption_level level) const -> Result< quic_keys >
Get write keys for an encryption level.
auto process_crypto_data(encryption_level level, std::span< const uint8_t > data) -> Result< std::vector< uint8_t > >
Process incoming CRYPTO frame data.
auto is_server() const noexcept -> bool
Check if this is a server instance.
void set_keys(encryption_level level, const quic_keys &read_keys, const quic_keys &write_keys)
Set keys for an encryption level (used during handshake)
auto set_alpn(const std::vector< std::string > &protocols) -> VoidResult
Set ALPN protocols to offer/accept.
std::function< void( std::vector< uint8_t > ticket_data, uint32_t lifetime_hint, uint32_t ticket_age_add, uint32_t max_early_data)> session_ticket_callback_t
Callback type for receiving session tickets.
auto set_session_ticket(std::span< const uint8_t > ticket_data) -> VoidResult
Set a session ticket for 0-RTT resumption.
quic_crypto & operator=(const quic_crypto &)=delete
auto init_client(const std::string &server_name) -> VoidResult
Initialize as client.
auto current_level() const noexcept -> encryption_level
Get current encryption level.
auto derive_zero_rtt_keys() -> VoidResult
Derive 0-RTT keys from session ticket.
auto has_zero_rtt_keys() const noexcept -> bool
Check if 0-RTT keys are available.
auto key_phase() const noexcept -> uint8_t
Get current key phase (for key updates)
auto derive_initial_secrets(const connection_id &dest_cid) -> VoidResult
Derive initial secrets from destination connection ID.
void set_session_ticket_callback(session_ticket_callback_t cb)
Set callback for receiving session tickets.
auto enable_early_data(uint32_t max_early_data) -> VoidResult
Enable 0-RTT early data.
auto is_handshake_complete() const noexcept -> bool
Check if the handshake is complete.
struct ssl_ctx_st SSL_CTX
std::string get_openssl_error() noexcept
Get last OpenSSL error as string.
constexpr uint32_t version_2
QUIC version 2 (RFC 9369)
@ error
Black hole detected, reset to base.
constexpr std::array< uint8_t, 20 > initial_salt_v1
QUIC version 1 initial salt (RFC 9001 Section 5.2)
auto encryption_level_to_string(encryption_level level) -> std::string
Convert encryption level to string for debugging.
constexpr std::array< uint8_t, 20 > initial_salt_v2
QUIC version 2 initial salt (RFC 9369)
constexpr size_t secret_size
Traffic secret size (SHA-256 output)
constexpr size_t aead_tag_size
AEAD authentication tag size in bytes.
constexpr size_t hp_sample_size
Header protection sample size.
constexpr size_t aes_128_key_size
AES-128-GCM key size in bytes.
encryption_level
QUIC encryption levels (RFC 9001 Section 4)
@ application
1-RTT application data encryption
@ initial
Initial encryption (derived from DCID)
@ zero_rtt
0-RTT early data encryption
constexpr size_t aead_iv_size
AEAD IV/nonce size in bytes.
constexpr size_t hp_key_size
Header protection key size for AES-128.
const std::string & get_error_source(const simple_error &err)
constexpr const char * version() noexcept
Get the network system version string.
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
OpenSSL utilities and version definitions.
A pair of read and write keys for bidirectional communication.
quic_keys read
Keys for decrypting received packets.
quic_keys write
Keys for encrypting outgoing packets.
session_ticket_callback_t session_ticket_callback
uint32_t max_early_data_size
std::map< encryption_level, quic_keys > write_keys
std::vector< uint8_t > alpn_data
std::map< encryption_level, quic_keys > read_keys
encryption_level current_level
std::vector< uint8_t > session_ticket_data
QUIC encryption keys for a single encryption level (RFC 9001 Section 5)
std::array< uint8_t, hp_key_size > hp_key
Header protection key.
std::array< uint8_t, aes_128_key_size > key
AEAD encryption key (AES-128-GCM by default)
std::array< uint8_t, aead_iv_size > iv
AEAD initialization vector.