Loading src/ssl.cpp +111 −157 Original line number Diff line number Diff line Loading @@ -1654,10 +1654,6 @@ void netplus::ssl::handshake_after_accept(){ if (ch.size() < 4) throwSSL(NetException::Error, "ClientHello too short"); if (ch[0] != 0x01) throwSSL(NetException::Error, "Expected ClientHello"); // ✅ CRITICAL FIX: Save raw ClientHello bytes (including handshake header) for transcript hash // This must be done BEFORE parsing to preserve exact bytes for TLS 1.3 handshake verification _clientHelloRawBytes = ch; auto readU16 = [&](size_t& p) -> uint16_t { if (p + 2 > ch.size()) throwSSL(NetException::Error, "parse underrun u16"); uint16_t x = (uint16_t(ch[p]) << 8) | ch[p+1]; Loading Loading @@ -2361,68 +2357,34 @@ void netplus::ssl::handshake_after_accept(){ case HsState::TLS13_WAIT_CLIENT_FINISHED: { // Safety check: handshake keys must be initialized if (!_aes13_hs_recv) { NetException n; n[NetException::Note] << "TLS1.3: waiting for handshake keys to be initialized"; throw n; } // ⚠️ Save transcript hash BEFORE fetching client Finished // The client computes its Finished over CH..server_Finished (not including client Finished) std::vector<uint8_t> th_before_client_finished = sha256_hash(_handshake_transcript); // ✅ Read ENCRYPTED handshake record (one at a time) // ✅ FIXED: Read ENCRYPTED handshake record (not plaintext!) // After ServerHello, all handshake messages are encrypted with handshake keys std::vector<uint8_t> msg = _tls13_read_record_handshake(); if (msg.empty()) { // Need more data - throw Note so event loop waits // Must throw Note so event loop waits for more data NetException n; n[NetException::Note] << "TLS1.3: waiting for client Finished"; throw n; } // Append to handshake buffer _rx_handshake_buf.insert(_rx_handshake_buf.end(), msg.begin(), msg.end()); // Check if we have a complete Finished message (4-byte header + 32-byte body) if (_rx_handshake_buf.size() < 4) { // Incomplete header - need more data NetException n; n[NetException::Note] << "TLS1.3: incomplete Finished header"; throw n; } uint32_t len = (uint32_t(_rx_handshake_buf[1]) << 16) | (uint32_t(_rx_handshake_buf[2]) << 8) | (uint32_t(_rx_handshake_buf[3]) << 0); if (_rx_handshake_buf.size() < 4 + len) { // Incomplete body - need more data NetException n; n[NetException::Note] << "TLS1.3: incomplete Finished body, have " << _rx_handshake_buf.size() << " need " << (4 + len); throw n; } std::vector<uint8_t> finished_msg(_rx_handshake_buf.begin(), _rx_handshake_buf.begin() + 4 + len); _rx_handshake_buf.erase(_rx_handshake_buf.begin(), _rx_handshake_buf.begin() + 4 + len); // Validate message if (finished_msg.size() < 4) // msg enthält Handshake Msg (type+len24+body) if (msg.size() < 4) throwSSL(NetException::Error, "TLS1.3 client Finished too short"); uint8_t ht = finished_msg[0]; uint8_t ht = msg[0]; uint32_t len = (uint32_t(msg[1]) << 16) | (uint32_t(msg[2]) << 8) | uint32_t(msg[3]); if (ht != 0x14) throwSSL(NetException::Error, "TLS1.3 expected Finished from client"); if (len != 32 || finished_msg.size() != 4 + 32) if (len != 32 || msg.size() != 4 + 32) throwSSL(NetException::Error, "TLS1.3 Finished verify_data wrong size"); std::vector<uint8_t> client_verify(finished_msg.begin() + 4, finished_msg.end()); std::vector<uint8_t> client_verify(msg.begin() + 4, msg.end()); // finished_key = HKDF-Expand-Label(c_hs_secret, "finished", "", 32) std::vector<uint8_t> finished_key = _hkdf_expand_label( Loading Loading @@ -2460,22 +2422,19 @@ void netplus::ssl::handshake_after_accept(){ // Calling it again here would use wrong transcript (includes client Finished). _handshakeDone = true; _hs_state = HsState::DONE; _hs_state = HsState::ESTABLISHED; return; } case HsState::TLS13_SEND_ENCRYPTED_FLIGHT: { if (!_tls13_encflight_queued) { _tls13_encflight_queued = true; // Set flag FIRST to prevent re-entry if exception occurs if (!_aes13_hs_send || !_aes13_hs_recv) throwSSL(NetException::Error, "TLS1.3 missing handshake keys"); // 1) EncryptedExtensions (extensions = 2-byte length of 0 = 0x0000) std::vector<uint8_t> ee; ee.push_back(0x00); // extensions length high byte ee.push_back(0x00); // extensions length low byte (length = 0) // 1) EncryptedExtensions (body = extension list length = 0) std::vector<uint8_t> ee = {0x00, 0x00}; _tls13_send_handshake(0x08, ee, true); // 2) Certificate Loading @@ -2493,6 +2452,8 @@ void netplus::ssl::handshake_after_accept(){ // 5) Derive application keys AFTER transcript contains server Finished _tls13_derive_application_keys(); _tls13_encflight_queued = true; } try { Loading @@ -2509,6 +2470,16 @@ void netplus::ssl::handshake_after_accept(){ return; } case HsState::FAIL:{ NetException e; e[NetException::Error] << "Handshake Failed"; throw e; } case HsState::DONE: _handshakeDone = true; return; case HsState::TLS13_SEND_SERVER_HELLO: { // Reset per-handshake shared secrets Loading Loading @@ -2605,16 +2576,6 @@ void netplus::ssl::handshake_after_accept(){ continue; } case HsState::FAIL:{ NetException e; e[NetException::Error] << "Handshake Failed"; throw e; } case HsState::DONE: _handshakeDone = true; return; default: throwSSL(netplus::NetException::Error, "invalid handshake state"); } Loading Loading @@ -2897,13 +2858,6 @@ void netplus::ssl::_tls13_derive_handshake_keys(const std::vector<uint8_t>& ecdh if (th.size() != 32) throwSSL(NetException::Error, "TLS1.3: transcript hash wrong size"); // ✅ DEBUG: Log transcript hash for verification std::cerr << "[TLS] _tls13_derive_handshake_keys: transcript size=" << _handshake_transcript.size() << " hash(first 16 bytes): "; for (int i = 0; i < 16 && i < (int)th.size(); i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << (int)th[i] << " "; std::cerr << std::dec << std::endl; std::vector<uint8_t> zeros(32, 0x00); // SHA-256 hash of empty string - needed for "derived" context per RFC 8446 Loading Loading
src/ssl.cpp +111 −157 Original line number Diff line number Diff line Loading @@ -1654,10 +1654,6 @@ void netplus::ssl::handshake_after_accept(){ if (ch.size() < 4) throwSSL(NetException::Error, "ClientHello too short"); if (ch[0] != 0x01) throwSSL(NetException::Error, "Expected ClientHello"); // ✅ CRITICAL FIX: Save raw ClientHello bytes (including handshake header) for transcript hash // This must be done BEFORE parsing to preserve exact bytes for TLS 1.3 handshake verification _clientHelloRawBytes = ch; auto readU16 = [&](size_t& p) -> uint16_t { if (p + 2 > ch.size()) throwSSL(NetException::Error, "parse underrun u16"); uint16_t x = (uint16_t(ch[p]) << 8) | ch[p+1]; Loading Loading @@ -2361,68 +2357,34 @@ void netplus::ssl::handshake_after_accept(){ case HsState::TLS13_WAIT_CLIENT_FINISHED: { // Safety check: handshake keys must be initialized if (!_aes13_hs_recv) { NetException n; n[NetException::Note] << "TLS1.3: waiting for handshake keys to be initialized"; throw n; } // ⚠️ Save transcript hash BEFORE fetching client Finished // The client computes its Finished over CH..server_Finished (not including client Finished) std::vector<uint8_t> th_before_client_finished = sha256_hash(_handshake_transcript); // ✅ Read ENCRYPTED handshake record (one at a time) // ✅ FIXED: Read ENCRYPTED handshake record (not plaintext!) // After ServerHello, all handshake messages are encrypted with handshake keys std::vector<uint8_t> msg = _tls13_read_record_handshake(); if (msg.empty()) { // Need more data - throw Note so event loop waits // Must throw Note so event loop waits for more data NetException n; n[NetException::Note] << "TLS1.3: waiting for client Finished"; throw n; } // Append to handshake buffer _rx_handshake_buf.insert(_rx_handshake_buf.end(), msg.begin(), msg.end()); // Check if we have a complete Finished message (4-byte header + 32-byte body) if (_rx_handshake_buf.size() < 4) { // Incomplete header - need more data NetException n; n[NetException::Note] << "TLS1.3: incomplete Finished header"; throw n; } uint32_t len = (uint32_t(_rx_handshake_buf[1]) << 16) | (uint32_t(_rx_handshake_buf[2]) << 8) | (uint32_t(_rx_handshake_buf[3]) << 0); if (_rx_handshake_buf.size() < 4 + len) { // Incomplete body - need more data NetException n; n[NetException::Note] << "TLS1.3: incomplete Finished body, have " << _rx_handshake_buf.size() << " need " << (4 + len); throw n; } std::vector<uint8_t> finished_msg(_rx_handshake_buf.begin(), _rx_handshake_buf.begin() + 4 + len); _rx_handshake_buf.erase(_rx_handshake_buf.begin(), _rx_handshake_buf.begin() + 4 + len); // Validate message if (finished_msg.size() < 4) // msg enthält Handshake Msg (type+len24+body) if (msg.size() < 4) throwSSL(NetException::Error, "TLS1.3 client Finished too short"); uint8_t ht = finished_msg[0]; uint8_t ht = msg[0]; uint32_t len = (uint32_t(msg[1]) << 16) | (uint32_t(msg[2]) << 8) | uint32_t(msg[3]); if (ht != 0x14) throwSSL(NetException::Error, "TLS1.3 expected Finished from client"); if (len != 32 || finished_msg.size() != 4 + 32) if (len != 32 || msg.size() != 4 + 32) throwSSL(NetException::Error, "TLS1.3 Finished verify_data wrong size"); std::vector<uint8_t> client_verify(finished_msg.begin() + 4, finished_msg.end()); std::vector<uint8_t> client_verify(msg.begin() + 4, msg.end()); // finished_key = HKDF-Expand-Label(c_hs_secret, "finished", "", 32) std::vector<uint8_t> finished_key = _hkdf_expand_label( Loading Loading @@ -2460,22 +2422,19 @@ void netplus::ssl::handshake_after_accept(){ // Calling it again here would use wrong transcript (includes client Finished). _handshakeDone = true; _hs_state = HsState::DONE; _hs_state = HsState::ESTABLISHED; return; } case HsState::TLS13_SEND_ENCRYPTED_FLIGHT: { if (!_tls13_encflight_queued) { _tls13_encflight_queued = true; // Set flag FIRST to prevent re-entry if exception occurs if (!_aes13_hs_send || !_aes13_hs_recv) throwSSL(NetException::Error, "TLS1.3 missing handshake keys"); // 1) EncryptedExtensions (extensions = 2-byte length of 0 = 0x0000) std::vector<uint8_t> ee; ee.push_back(0x00); // extensions length high byte ee.push_back(0x00); // extensions length low byte (length = 0) // 1) EncryptedExtensions (body = extension list length = 0) std::vector<uint8_t> ee = {0x00, 0x00}; _tls13_send_handshake(0x08, ee, true); // 2) Certificate Loading @@ -2493,6 +2452,8 @@ void netplus::ssl::handshake_after_accept(){ // 5) Derive application keys AFTER transcript contains server Finished _tls13_derive_application_keys(); _tls13_encflight_queued = true; } try { Loading @@ -2509,6 +2470,16 @@ void netplus::ssl::handshake_after_accept(){ return; } case HsState::FAIL:{ NetException e; e[NetException::Error] << "Handshake Failed"; throw e; } case HsState::DONE: _handshakeDone = true; return; case HsState::TLS13_SEND_SERVER_HELLO: { // Reset per-handshake shared secrets Loading Loading @@ -2605,16 +2576,6 @@ void netplus::ssl::handshake_after_accept(){ continue; } case HsState::FAIL:{ NetException e; e[NetException::Error] << "Handshake Failed"; throw e; } case HsState::DONE: _handshakeDone = true; return; default: throwSSL(netplus::NetException::Error, "invalid handshake state"); } Loading Loading @@ -2897,13 +2858,6 @@ void netplus::ssl::_tls13_derive_handshake_keys(const std::vector<uint8_t>& ecdh if (th.size() != 32) throwSSL(NetException::Error, "TLS1.3: transcript hash wrong size"); // ✅ DEBUG: Log transcript hash for verification std::cerr << "[TLS] _tls13_derive_handshake_keys: transcript size=" << _handshake_transcript.size() << " hash(first 16 bytes): "; for (int i = 0; i < 16 && i < (int)th.size(); i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << (int)th[i] << " "; std::cerr << std::dec << std::endl; std::vector<uint8_t> zeros(32, 0x00); // SHA-256 hash of empty string - needed for "derived" context per RFC 8446 Loading