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());
88 const std::array<uint8_t, secret_size> zero_salt{};
89 if (salt.empty()) salt = zero_salt;
90 ret = EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt.data(),
91 static_cast<int>(salt.size()));
94 EVP_PKEY_CTX_free(pctx);
96 -1,
"HKDF set salt failed",
"quic::hkdf", get_openssl_error_string());
99 ret = EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm.data(),
100 static_cast<int>(ikm.size()));
103 EVP_PKEY_CTX_free(pctx);
105 -1,
"HKDF set key failed",
"quic::hkdf", get_openssl_error_string());
108 ret = EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY);
111 EVP_PKEY_CTX_free(pctx);
113 -1,
"HKDF set mode failed",
"quic::hkdf", get_openssl_error_string());
116 ret = EVP_PKEY_derive(pctx, prk.data(), &prk_len);
117 EVP_PKEY_CTX_free(pctx);
122 -1,
"HKDF extract failed",
"quic::hkdf", get_openssl_error_string());
125 return ok(std::move(prk));
129 std::span<const uint8_t> info,
133 std::vector<uint8_t> okm(length);
135 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF,
nullptr);
139 -1,
"Failed to create HKDF context",
"quic::hkdf");
142 int ret = EVP_PKEY_derive_init(pctx);
145 EVP_PKEY_CTX_free(pctx);
147 -1,
"HKDF derive init failed",
"quic::hkdf", get_openssl_error_string());
150 ret = EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256());
153 EVP_PKEY_CTX_free(pctx);
155 -1,
"HKDF set md failed",
"quic::hkdf", get_openssl_error_string());
158 ret = EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY);
161 EVP_PKEY_CTX_free(pctx);
163 -1,
"HKDF set mode failed",
"quic::hkdf", get_openssl_error_string());
166 ret = EVP_PKEY_CTX_set1_hkdf_key(pctx, prk.data(),
167 static_cast<int>(prk.size()));
170 EVP_PKEY_CTX_free(pctx);
172 -1,
"HKDF set key failed",
"quic::hkdf", get_openssl_error_string());
175 ret = EVP_PKEY_CTX_add1_hkdf_info(pctx, info.data(),
176 static_cast<int>(info.size()));
179 EVP_PKEY_CTX_free(pctx);
181 -1,
"HKDF set info failed",
"quic::hkdf", get_openssl_error_string());
184 ret = EVP_PKEY_derive(pctx, okm.data(), &length);
185 EVP_PKEY_CTX_free(pctx);
190 -1,
"HKDF expand failed",
"quic::hkdf", get_openssl_error_string());
194 return ok(std::move(okm));
198 const std::string& label,
199 std::span<const uint8_t> context,
210 const std::string prefix =
"tls13 ";
211 std::vector<uint8_t> hkdf_label;
212 hkdf_label.reserve(2 + 1 + prefix.size() + label.size() + 1 + context.size());
215 hkdf_label.push_back(
static_cast<uint8_t
>((length >> 8) & 0xFF));
216 hkdf_label.push_back(
static_cast<uint8_t
>(length & 0xFF));
219 size_t label_len = prefix.size() + label.size();
220 hkdf_label.push_back(
static_cast<uint8_t
>(label_len));
221 for (
char c : prefix)
223 hkdf_label.push_back(
static_cast<uint8_t
>(c));
227 hkdf_label.push_back(
static_cast<uint8_t
>(c));
231 hkdf_label.push_back(
static_cast<uint8_t
>(context.size()));
232 hkdf_label.insert(hkdf_label.end(), context.begin(), context.end());
234 return expand(secret, hkdf_label, length);
245 std::span<const uint8_t> salt;
256 auto initial_secret_result =
hkdf::extract(salt, dest_cid.data());
257 if (initial_secret_result.is_err())
260 initial_secret_result.error().code,
261 "Failed to derive initial secret",
262 "quic::initial_keys",
263 initial_secret_result.error().message);
266 auto& initial_secret = initial_secret_result.value();
271 std::string(
reinterpret_cast<const char*
>(client_initial_label.data()),
272 client_initial_label.size()),
275 if (client_secret_result.is_err())
278 client_secret_result.error().code,
279 "Failed to derive client initial secret",
280 "quic::initial_keys",
281 client_secret_result.error().message);
287 std::string(
reinterpret_cast<const char*
>(server_initial_label.data()),
288 server_initial_label.size()),
291 if (server_secret_result.is_err())
294 server_secret_result.error().code,
295 "Failed to derive server initial secret",
296 "quic::initial_keys",
297 server_secret_result.error().message);
301 auto client_keys_result = derive_keys(client_secret_result.value(),
true);
302 if (client_keys_result.is_err())
305 client_keys_result.error().code,
306 "Failed to derive client keys",
307 "quic::initial_keys",
308 client_keys_result.error().message);
312 auto server_keys_result = derive_keys(server_secret_result.value(),
false);
313 if (server_keys_result.is_err())
316 server_keys_result.error().code,
317 "Failed to derive server keys",
318 "quic::initial_keys",
319 server_keys_result.error().message);
323 auto& client_keys = client_keys_result.value();
324 auto& server_keys = server_keys_result.value();
326 std::copy(client_secret_result.value().begin(),
327 client_secret_result.value().end(),
328 client_keys.secret.begin());
329 std::copy(server_secret_result.value().begin(),
330 server_secret_result.value().end(),
331 server_keys.secret.begin());
337 result.
write = std::move(client_keys);
338 result.
read = std::move(server_keys);
340 return ok(std::move(result));
347 (void)is_client_keys;
354 std::string(
reinterpret_cast<const char*
>(quic_key_label.data()),
355 quic_key_label.size()),
358 if (key_result.is_err())
361 key_result.error().code,
362 "Failed to derive AEAD key",
363 "quic::initial_keys",
364 key_result.error().message);
366 std::copy(key_result.value().begin(), key_result.value().end(),
372 std::string(
reinterpret_cast<const char*
>(quic_iv_label.data()),
373 quic_iv_label.size()),
376 if (iv_result.is_err())
379 iv_result.error().code,
380 "Failed to derive IV",
381 "quic::initial_keys",
382 iv_result.error().message);
384 std::copy(iv_result.value().begin(), iv_result.value().end(),
390 std::string(
reinterpret_cast<const char*
>(quic_hp_label.data()),
391 quic_hp_label.size()),
394 if (hp_result.is_err())
397 hp_result.error().code,
398 "Failed to derive HP key",
399 "quic::initial_keys",
400 hp_result.error().message);
402 std::copy(hp_result.value().begin(), hp_result.value().end(),
405 return ok(std::move(keys));
414 -> std::array<uint8_t, aead_iv_size>
416 std::array<uint8_t, aead_iv_size> nonce{};
417 std::copy(iv.begin(), iv.end(), nonce.begin());
420 for (
size_t i = 0; i < 8; ++i)
430 std::span<const uint8_t> header,
431 std::span<const uint8_t> payload,
438 std::vector<uint8_t> output;
439 output.reserve(header.size() + payload.size() +
aead_tag_size);
440 output.insert(output.end(), header.begin(), header.end());
441 output.resize(output.size() + payload.size() +
aead_tag_size);
443 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
447 -1,
"Failed to create cipher context",
"quic::packet_protection");
450 int ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(),
nullptr,
451 keys.key.data(), nonce.data());
454 EVP_CIPHER_CTX_free(ctx);
456 -1,
"AES-GCM encrypt init failed",
"quic::packet_protection",
457 get_openssl_error_string());
462 ret = EVP_EncryptUpdate(ctx,
nullptr, &len, header.data(),
463 static_cast<int>(header.size()));
466 EVP_CIPHER_CTX_free(ctx);
468 -1,
"AES-GCM AAD update failed",
"quic::packet_protection",
469 get_openssl_error_string());
473 ret = EVP_EncryptUpdate(ctx, output.data() + header.size(), &len,
474 payload.data(),
static_cast<int>(payload.size()));
477 EVP_CIPHER_CTX_free(ctx);
479 -1,
"AES-GCM encrypt failed",
"quic::packet_protection",
480 get_openssl_error_string());
483 int ciphertext_len = len;
485 ret = EVP_EncryptFinal_ex(ctx, output.data() + header.size() + len, &len);
488 EVP_CIPHER_CTX_free(ctx);
490 -1,
"AES-GCM encrypt final failed",
"quic::packet_protection",
491 get_openssl_error_string());
493 ciphertext_len += len;
496 ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG,
aead_tag_size,
497 output.data() + header.size() + ciphertext_len);
500 EVP_CIPHER_CTX_free(ctx);
502 -1,
"AES-GCM get tag failed",
"quic::packet_protection",
503 get_openssl_error_string());
506 EVP_CIPHER_CTX_free(ctx);
507 output.resize(header.size() + ciphertext_len +
aead_tag_size);
509 return ok(std::move(output));
513 std::span<const uint8_t> packet,
514 size_t header_length,
521 -1,
"Packet too short for decryption",
"quic::packet_protection");
526 auto header = packet.subspan(0, header_length);
527 auto ciphertext = packet.subspan(header_length,
531 std::vector<uint8_t> plaintext(ciphertext.size());
533 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
537 -1,
"Failed to create cipher context",
"quic::packet_protection");
540 int ret = EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(),
nullptr,
541 keys.key.data(), nonce.data());
544 EVP_CIPHER_CTX_free(ctx);
546 -1,
"AES-GCM decrypt init failed",
"quic::packet_protection",
547 get_openssl_error_string());
552 ret = EVP_DecryptUpdate(ctx,
nullptr, &len, header.data(),
553 static_cast<int>(header.size()));
556 EVP_CIPHER_CTX_free(ctx);
558 -1,
"AES-GCM AAD update failed",
"quic::packet_protection",
559 get_openssl_error_string());
563 ret = EVP_DecryptUpdate(ctx, plaintext.data(), &len,
564 ciphertext.data(),
static_cast<int>(ciphertext.size()));
567 EVP_CIPHER_CTX_free(ctx);
569 -1,
"AES-GCM decrypt failed",
"quic::packet_protection",
570 get_openssl_error_string());
573 int plaintext_len = len;
576 ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
aead_tag_size,
577 const_cast<uint8_t*
>(tag.data()));
580 EVP_CIPHER_CTX_free(ctx);
582 -1,
"AES-GCM set tag failed",
"quic::packet_protection",
583 get_openssl_error_string());
586 ret = EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len);
587 EVP_CIPHER_CTX_free(ctx);
592 -1,
"AES-GCM authentication failed",
"quic::packet_protection");
595 plaintext_len += len;
596 plaintext.resize(plaintext_len);
598 std::vector<uint8_t> header_copy(header.begin(), header.end());
599 return ok(std::make_pair(std::move(header_copy), std::move(plaintext)));
603 std::span<const uint8_t> sample)
609 -1,
"Sample too short for HP mask",
"quic::packet_protection");
612 std::array<uint8_t, 16> mask_full{};
614 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
618 -1,
"Failed to create cipher context",
"quic::packet_protection");
621 int ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(),
nullptr,
622 hp_key.data(),
nullptr);
625 EVP_CIPHER_CTX_free(ctx);
627 -1,
"AES-ECB init failed",
"quic::packet_protection",
628 get_openssl_error_string());
631 EVP_CIPHER_CTX_set_padding(ctx, 0);
634 ret = EVP_EncryptUpdate(ctx, mask_full.data(), &len,
638 EVP_CIPHER_CTX_free(ctx);
640 -1,
"AES-ECB encrypt failed",
"quic::packet_protection",
641 get_openssl_error_string());
644 EVP_CIPHER_CTX_free(ctx);
647 std::array<uint8_t, 5> mask{};
648 std::copy(mask_full.begin(), mask_full.begin() + 5, mask.begin());
650 return ok(std::move(mask));
654 std::span<uint8_t> header,
657 std::span<const uint8_t> sample)
660 auto mask_result = generate_hp_mask(keys.hp_key, sample);
661 if (mask_result.is_err())
664 mask_result.error().message,
668 auto& mask = mask_result.value();
671 if ((header[0] & 0x80) != 0)
674 header[0] ^= (mask[0] & 0x0F);
679 header[0] ^= (mask[0] & 0x1F);
683 for (
size_t i = 0; i < pn_length; ++i)
685 header[pn_offset + i] ^= mask[1 + i];
692 std::span<uint8_t> header,
694 std::span<const uint8_t> sample)
697 auto mask_result = generate_hp_mask(keys.hp_key, sample);
698 if (mask_result.is_err())
701 mask_result.error().
code,
702 mask_result.error().message,
706 auto& mask = mask_result.value();
709 if ((header[0] & 0x80) != 0)
712 header[0] ^= (mask[0] & 0x0F);
717 header[0] ^= (mask[0] & 0x1F);
721 size_t pn_length = (header[0] & 0x03) + 1;
724 for (
size_t i = 0; i < pn_length; ++i)
726 header[pn_offset + i] ^= mask[1 + i];
729 return ok(std::make_pair(header[0], pn_length));
783 : impl_(std::make_unique<
impl>())
795 impl_->is_server =
false;
797 impl_->ssl_ctx = SSL_CTX_new(TLS_client_method());
800 return error_void(-1,
"Failed to create SSL context",
"quic::crypto",
801 get_openssl_error_string());
805 SSL_CTX_set_min_proto_version(impl_->ssl_ctx, TLS1_3_VERSION);
806 SSL_CTX_set_max_proto_version(impl_->ssl_ctx, TLS1_3_VERSION);
808 impl_->ssl = SSL_new(impl_->ssl_ctx);
811 return error_void(-1,
"Failed to create SSL object",
"quic::crypto",
812 get_openssl_error_string());
816 if (!server_name.empty())
818 SSL_set_tlsext_host_name(impl_->ssl, server_name.c_str());
822 impl_->rbio = BIO_new(BIO_s_mem());
823 impl_->wbio = BIO_new(BIO_s_mem());
824 if (!impl_->rbio || !impl_->wbio)
827 BIO_free(impl_->rbio);
828 BIO_free(impl_->wbio);
829 impl_->rbio =
nullptr;
830 impl_->wbio =
nullptr;
831 return error_void(-1,
"Failed to create BIO objects",
"quic::crypto");
834 BIO_set_nbio(impl_->rbio, 1);
835 BIO_set_nbio(impl_->wbio, 1);
837 SSL_set_bio(impl_->ssl, impl_->rbio, impl_->wbio);
838 SSL_set_connect_state(impl_->ssl);
847 impl_->is_server =
true;
849 impl_->ssl_ctx = SSL_CTX_new(TLS_server_method());
852 return error_void(-1,
"Failed to create SSL context",
"quic::crypto",
853 get_openssl_error_string());
857 SSL_CTX_set_min_proto_version(impl_->ssl_ctx, TLS1_3_VERSION);
858 SSL_CTX_set_max_proto_version(impl_->ssl_ctx, TLS1_3_VERSION);
861 if (SSL_CTX_use_certificate_file(impl_->ssl_ctx, cert_file.c_str(),
862 SSL_FILETYPE_PEM) != 1)
864 return error_void(-1,
"Failed to load certificate",
"quic::crypto",
865 get_openssl_error_string());
869 if (SSL_CTX_use_PrivateKey_file(impl_->ssl_ctx, key_file.c_str(),
870 SSL_FILETYPE_PEM) != 1)
872 return error_void(-1,
"Failed to load private key",
"quic::crypto",
873 get_openssl_error_string());
876 impl_->ssl = SSL_new(impl_->ssl_ctx);
879 return error_void(-1,
"Failed to create SSL object",
"quic::crypto",
880 get_openssl_error_string());
884 impl_->rbio = BIO_new(BIO_s_mem());
885 impl_->wbio = BIO_new(BIO_s_mem());
886 if (!impl_->rbio || !impl_->wbio)
889 BIO_free(impl_->rbio);
890 BIO_free(impl_->wbio);
891 impl_->rbio =
nullptr;
892 impl_->wbio =
nullptr;
893 return error_void(-1,
"Failed to create BIO objects",
"quic::crypto");
896 BIO_set_nbio(impl_->rbio, 1);
897 BIO_set_nbio(impl_->wbio, 1);
899 SSL_set_bio(impl_->ssl, impl_->rbio, impl_->wbio);
900 SSL_set_accept_state(impl_->ssl);
909 if (keys_result.is_err())
912 keys_result.error().message,
916 auto& keys = keys_result.value();
918 if (impl_->is_server)
935 std::span<const uint8_t> data)
941 int written = BIO_write(impl_->rbio, data.data(),
942 static_cast<int>(data.size()));
946 -1,
"Failed to write to BIO",
"quic::crypto");
950 int result = SSL_do_handshake(impl_->ssl);
953 impl_->handshake_complete =
true;
958 int err = SSL_get_error(impl_->ssl, result);
959 if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
962 -1,
"SSL handshake failed",
"quic::crypto",
963 get_openssl_error_string());
968 std::vector<uint8_t> output;
969 int pending = BIO_ctrl_pending(impl_->wbio);
972 output.resize(
static_cast<size_t>(pending));
973 int read = BIO_read(impl_->wbio, output.data(), pending);
976 output.resize(
static_cast<size_t>(read));
984 return ok(std::move(output));
989 int result = SSL_do_handshake(impl_->ssl);
992 impl_->handshake_complete =
true;
996 int err = SSL_get_error(impl_->ssl, result);
997 if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
1000 -1,
"SSL handshake start failed",
"quic::crypto",
1001 get_openssl_error_string());
1006 std::vector<uint8_t> output;
1007 int pending = BIO_ctrl_pending(impl_->wbio);
1010 output.resize(
static_cast<size_t>(pending));
1011 int read = BIO_read(impl_->wbio, output.data(), pending);
1014 output.resize(
static_cast<size_t>(read));
1022 return ok(std::move(output));
1038 auto it = impl_->write_keys.find(level);
1039 if (it == impl_->write_keys.end())
1042 -1,
"Write keys not available for level",
"quic::crypto",
1051 auto it = impl_->read_keys.find(level);
1052 if (it == impl_->read_keys.end())
1055 -1,
"Read keys not available for level",
"quic::crypto",
1076 if (!impl_->handshake_complete)
1078 return error_void(-1,
"Handshake not complete",
"quic::crypto");
1082 if (it == impl_->write_keys.end())
1084 return error_void(-1,
"Application keys not available",
"quic::crypto");
1088 auto& old_secret = it->second.secret;
1091 if (new_secret_result.is_err())
1093 return error_void(new_secret_result.error().code,
1094 new_secret_result.error().message,
1101 if (new_keys_result.is_err())
1103 return error_void(new_keys_result.error().code,
1104 new_keys_result.error().message,
1108 auto& new_keys = new_keys_result.value();
1109 std::copy(new_secret_result.value().begin(),
1110 new_secret_result.value().end(),
1111 new_keys.secret.begin());
1114 impl_->key_phase = 1 - impl_->key_phase;
1127 if (protocols.empty())
1133 impl_->alpn_data.clear();
1134 for (
const auto& proto : protocols)
1136 if (proto.size() > 255)
1138 return error_void(-1,
"ALPN protocol too long",
"quic::crypto");
1140 impl_->alpn_data.push_back(
static_cast<uint8_t
>(proto.size()));
1141 impl_->alpn_data.insert(impl_->alpn_data.end(), proto.begin(), proto.end());
1144 if (SSL_CTX_set_alpn_protos(impl_->ssl_ctx, impl_->alpn_data.data(),
1145 static_cast<unsigned int>(impl_->alpn_data.size())) != 0)
1147 return error_void(-1,
"Failed to set ALPN protocols",
"quic::crypto",
1148 get_openssl_error_string());
1176 if (ticket_data.empty())
1178 return error_void(-1,
"Empty session ticket",
"quic::crypto");
1181 impl_->session_ticket_data.assign(ticket_data.begin(), ticket_data.end());
1187 if (impl_->session_ticket_data.empty())
1189 return error_void(-1,
"Session ticket must be set before enabling early data",
1193 impl_->max_early_data_size = max_early_data;
1194 impl_->early_data_enabled =
true;
1206 if (impl_->session_ticket_data.empty())
1208 return error_void(-1,
"No session ticket available",
"quic::crypto");
1223 impl_->session_ticket_data);
1225 if (early_secret_result.is_err())
1227 return error_void(early_secret_result.error().code,
1228 "Failed to derive early secret",
1230 early_secret_result.error().message);
1233 auto& early_secret = early_secret_result.value();
1242 if (client_early_secret_result.is_err())
1244 return error_void(client_early_secret_result.error().code,
1245 "Failed to derive client early secret",
1247 client_early_secret_result.error().message);
1252 client_early_secret_result.value(),
1255 if (zero_rtt_keys_result.is_err())
1257 return error_void(zero_rtt_keys_result.error().code,
1258 "Failed to derive 0-RTT keys",
1260 zero_rtt_keys_result.error().message);
1263 auto& zero_rtt_keys = zero_rtt_keys_result.value();
1266 std::copy(client_early_secret_result.value().begin(),
1267 client_early_secret_result.value().end(),
1268 zero_rtt_keys.secret.begin());
1273 if (impl_->is_server)
1282 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.