Loading src/ssl.cpp +176 −50 Original line number Diff line number Diff line Loading @@ -763,6 +763,10 @@ void netplus::ssl::_sendHandshake(uint8_t type, const std::vector<uint8_t>& data handshake.push_back(data.size() & 0xFF); handshake.insert(handshake.end(), data.begin(), data.end()); std::cerr << "[TRANSCRIPT] _sendHandshake type=0x" << std::hex << int(type) << std::dec << " adding " << handshake.size() << " bytes, transcript was " << csock->_handshake_transcript.size() << "\n"; // IMPORTANT: Add the Handshake Layer to the transcript for Finished verification csock->_handshake_transcript.insert(csock->_handshake_transcript.end(), handshake.begin(), handshake.end()); Loading Loading @@ -993,29 +997,6 @@ void netplus::ssl::accept(std::unique_ptr<socket>& csock, bool nonblock) cssock->setNonBlock(); } // --- oben in ssl.cpp (oder anonym namespace) --- static inline const char* tlsRecTypeName(uint8_t ct) { switch (ct) { case 0x14: return "change_cipher_spec"; case 0x15: return "alert"; case 0x16: return "handshake"; case 0x17: return "application_data"; default: return "unknown"; } } static inline const char* hsTypeName(uint8_t ht) { switch (ht) { case 0x01: return "ClientHello"; case 0x02: return "ServerHello"; case 0x08: return "EncryptedExtensions"; case 0x0b: return "Certificate"; case 0x0f: return "CertificateVerify"; case 0x14: return "Finished"; default: return "UnknownHS"; } } // no-op debug helper (can be enabled for troubleshooting) static inline void dumpHex(const char*, const uint8_t*, size_t, size_t = 64) {} Loading @@ -1033,7 +1014,6 @@ std::vector<uint8_t> netplus::ssl::_fetchNextHandshakePlain() // 1) already buffered handshake message? // -------------------------------------------------------- if (_rx_handshake_buf.size() >= 4) { uint8_t ht = _rx_handshake_buf[0]; uint32_t len = (uint32_t(_rx_handshake_buf[1]) << 16) | (uint32_t(_rx_handshake_buf[2]) << 8) | Loading @@ -1045,6 +1025,10 @@ std::vector<uint8_t> netplus::ssl::_fetchNextHandshakePlain() _rx_handshake_buf.erase(_rx_handshake_buf.begin(), _rx_handshake_buf.begin() + 4 + len); std::cerr << "[TRANSCRIPT] _fetchNextHandshakePlain type=0x" << std::hex << int(msg[0]) << std::dec << " adding " << msg.size() << " bytes, transcript was " << _handshake_transcript.size() << "\n"; // transcript: handshake messages only _handshake_transcript.insert(_handshake_transcript.end(), msg.begin(), msg.end()); Loading Loading @@ -1203,8 +1187,10 @@ std::vector<uint8_t> netplus::ssl::_fetchNextHandshakeTLS13() uint64_t seq = _tls13_hs_recv_seq; // build nonce = iv XOR seq // When receiving: client uses s2c IV, server uses c2s IV uint8_t nonce[12]={0}; tls13_make_nonce(nonce, _tls13_hs_iv_s2c, seq); const uint8_t* recv_iv = _is_client ? _tls13_hs_iv_s2c : _tls13_hs_iv_c2s; tls13_make_nonce(nonce, recv_iv, seq); // AAD = record header (type=23, legacy_version=0x0303, length = ciphertext_length) uint8_t aad[5]; Loading Loading @@ -1287,8 +1273,20 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() throwSSL(NetException::Error, "TLS1.3 hs recv AEAD not initialized"); // nonce = iv XOR seq // When receiving: client uses s2c IV, server uses c2s IV uint8_t nonce[12]; tls13_make_nonce(nonce, _tls13_hs_iv_c2s, _tls13_hs_recv_seq++); const uint8_t* recv_iv = _is_client ? _tls13_hs_iv_s2c : _tls13_hs_iv_c2s; uint64_t seq_used = _tls13_hs_recv_seq++; tls13_make_nonce(nonce, recv_iv, seq_used); std::cerr << "[RECV-HS] _is_client=" << _is_client << " seq=" << seq_used << "\n"; std::cerr << "[RECV-HS] recv_iv: "; for(int i=0;i<12;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(recv_iv[i]); std::cerr << std::dec << "\n"; std::cerr << "[RECV-HS] nonce: "; for(int i=0;i<12;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(nonce[i]); std::cerr << std::dec << "\n"; std::cerr << "[RECV-HS] ct_len=" << ct_len << " data_len=" << data_len << "\n"; // AAD = outer header with length=data_len uint8_t aad[5]; Loading Loading @@ -1528,24 +1526,6 @@ void netplus::ssl::handshake_after_accept(){ throw e; }; auto hsStateName = [&](netplus::ssl::HsState s) { switch(s){ case netplus::ssl::HsState::IDLE: return "IDLE"; case netplus::ssl::HsState::READ_CLIENT_HELLO: return "READ_CLIENT_HELLO"; case netplus::ssl::HsState::TLS13_SEND_SERVER_HELLO: return "TLS13_SEND_SERVER_HELLO"; case netplus::ssl::HsState::TLS13_SEND_ENCRYPTED_FLIGHT: return "TLS13_SEND_ENCRYPTED_FLIGHT"; case netplus::ssl::HsState::TLS13_WAIT_CLIENT_FINISHED: return "TLS13_WAIT_CLIENT_FINISHED"; case netplus::ssl::HsState::SEND_SERVER_FLIGHT: return "SEND_SERVER_FLIGHT"; case netplus::ssl::HsState::WAIT_CKE: return "WAIT_CKE"; case netplus::ssl::HsState::WAIT_CCS: return "WAIT_CCS"; case netplus::ssl::HsState::WAIT_FIN: return "WAIT_FIN"; case netplus::ssl::HsState::SEND_CCS_FIN: return "SEND_CCS_FIN"; case netplus::ssl::HsState::DONE: return "DONE"; case netplus::ssl::HsState::FAIL: return "FAIL"; default: return "???"; } }; auto gen_tls13_p256_scalar = [&](uint8_t out_priv[32]) { for (;;) { netplus::fillRandomBytes(out_priv, 32); Loading Loading @@ -1801,6 +1781,9 @@ void netplus::ssl::handshake_after_accept(){ _is_tls13 = true; _chosenSuite = tls13Suite; _secure_reneg = false; // NOTE: ClientHello already added to transcript by _fetchNextHandshakePlain() _hs_state = HsState::TLS13_SEND_SERVER_HELLO; TLSDBG("TLS1.3 selected suite=" << std::hex << _chosenSuite << " group=" << _selected_group); Loading Loading @@ -2450,6 +2433,9 @@ void netplus::ssl::handshake_after_accept(){ server_pub ); std::cerr << "[TRANSCRIPT] Server adding ServerHello " << hs.size() << " bytes, transcript was " << _handshake_transcript.size() << "\n"; // transcript includes plaintext SH _handshake_transcript.insert(_handshake_transcript.end(), hs.begin(), hs.end()); Loading Loading @@ -2701,6 +2687,14 @@ 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"); std::cerr << "[DERIVE-HS] ecdhe_shared: "; for(auto b : ecdhe_shared) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << std::dec << "\n"; std::cerr << "[DERIVE-HS] transcript_len=" << _handshake_transcript.size() << "\n"; std::cerr << "[DERIVE-HS] transcript_hash: "; for(auto b : th) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << std::dec << "\n"; std::vector<uint8_t> zeros(32, 0x00); // SHA-256 hash of empty string - needed for "derived" context per RFC 8446 Loading Loading @@ -2728,6 +2722,12 @@ void netplus::ssl::_tls13_derive_handshake_keys(const std::vector<uint8_t>& ecdh std::vector<uint8_t> s_key = _hkdf_expand_label(_tls13_s_hs_secret, "key", empty, 16); std::vector<uint8_t> s_iv = _hkdf_expand_label(_tls13_s_hs_secret, "iv", empty, 12); std::cerr << "[DERIVE-HS] _is_client=" << _is_client << "\n"; std::cerr << "[DERIVE-HS] c_key: "; for(auto b:c_key) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << "\n"; std::cerr << "[DERIVE-HS] c_iv: "; for(auto b:c_iv) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << "\n"; std::cerr << "[DERIVE-HS] s_key: "; for(auto b:s_key) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << "\n"; std::cerr << "[DERIVE-HS] s_iv: "; for(auto b:s_iv) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << std::dec << "\n"; // reset handshake record seq _tls13_hs_send_seq = 0; _tls13_hs_recv_seq = 0; Loading Loading @@ -2967,9 +2967,21 @@ void netplus::ssl::handshake_after_connect(){ std::vector<uint8_t> sh; sh = _fetchNextHandshakePlain(); // Handle would-block: _fetchNextHandshakePlain returns empty if no data ready if (sh.empty()) { note("ssl::handshake_after_connect: wait ServerHello"); } const uint8_t* p = sh.data(); size_t n = sh.size(); std::cerr << "[CLI-SH] ServerHello size=" << n << " type=0x" << std::hex << (n>0?int(p[0]):-1) << std::dec << "\n"; if (n >= 5) { std::cerr << "[CLI-SH] first 5 bytes: "; for(size_t i=0;i<5;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(p[i]) << " "; std::cerr << std::dec << "\n"; } if (n < 4 + 2 + 32 + 1 + 2 + 1) { NetException e; e[NetException::Error] << "ServerHello too short"; Loading @@ -2978,6 +2990,9 @@ void netplus::ssl::handshake_after_connect(){ size_t off = 4; // skip handshake header // legacy_version (2 bytes: 0x03, 0x03) off += 2; // server_random (32) _serverRandom.assign(p + off, p + off + 32); off += 32; Loading Loading @@ -3125,10 +3140,8 @@ void netplus::ssl::handshake_after_connect(){ eoff += el; } // transcript: include ClientHello + ServerHello for TLS1.3 // NOTE: you must have already appended ClientHello earlier when sending it. // We'll append ServerHello now: _handshake_transcript.insert(_handshake_transcript.end(), sh.begin(), sh.end()); // NOTE: ServerHello was already added to transcript by _fetchNextHandshakePlain() // Do NOT add it again here! if (tls13) { if (!_client_offered_tls13 || !_tls13_got_key_share){ Loading @@ -3147,9 +3160,8 @@ void netplus::ssl::handshake_after_connect(){ throw e; } _handshake_transcript.insert(_handshake_transcript.end(), sh.begin(), sh.end()); // Derive TLS1.3 handshake keys from ECDHE shared // (transcript already includes ClientHello + ServerHello from above) _tls13_derive_handshake_keys(ecdhe_shared); // switch to TLS 1.3 handshake path Loading Loading @@ -3229,6 +3241,112 @@ void netplus::ssl::handshake_after_connect(){ _hs_state = HsState::CLI_TLS13_WAIT_CERT_VERIFY; break; } // --------------------------- // TLS 1.3: wait CertificateVerify (type 0x0f) // --------------------------- case HsState::CLI_TLS13_WAIT_CERT_VERIFY: { if (_rx_handshake_buf.empty() || _rx_handshake_off >= _rx_handshake_buf.size()) { _rx_handshake_buf = _tls13_read_record_handshake(); _rx_handshake_off = 0; } std::vector<uint8_t> msg; if (!_tls13_pop_hs_from_buf(msg)) { _rx_handshake_buf = _tls13_read_record_handshake(); _rx_handshake_off = 0; if (!_tls13_pop_hs_from_buf(msg)) note("ssl::handshake_after_connect: wait TLS1.3 CertificateVerify"); } if (msg.size() < 4 || msg[0] != 0x0f) { NetException e; e[NetException::Error] << "TLS1.3 expected CertificateVerify got " << int(msg.empty()? -1 : msg[0]); throw e; } // transcript add _handshake_transcript.insert(_handshake_transcript.end(), msg.begin(), msg.end()); // TODO: verify RSA-PSS signature over transcript // For now, skip verification _hs_state = HsState::CLI_TLS13_WAIT_FINISHED; break; } // --------------------------- // TLS 1.3: wait server Finished (type 0x14) // --------------------------- case HsState::CLI_TLS13_WAIT_FINISHED: { if (_rx_handshake_buf.empty() || _rx_handshake_off >= _rx_handshake_buf.size()) { _rx_handshake_buf = _tls13_read_record_handshake(); _rx_handshake_off = 0; } std::vector<uint8_t> msg; if (!_tls13_pop_hs_from_buf(msg)) { _rx_handshake_buf = _tls13_read_record_handshake(); _rx_handshake_off = 0; if (!_tls13_pop_hs_from_buf(msg)) note("ssl::handshake_after_connect: wait TLS1.3 server Finished"); } if (msg.size() < 4 || msg[0] != 0x14) { NetException e; e[NetException::Error] << "TLS1.3 expected server Finished got " << int(msg.empty()? -1 : msg[0]); throw e; } // Save transcript hash BEFORE adding server Finished (for client Finished) std::vector<uint8_t> th_before_sfin = sha256_hash(_handshake_transcript); // transcript add server Finished _handshake_transcript.insert(_handshake_transcript.end(), msg.begin(), msg.end()); // TODO: verify server Finished verify_data // For now, skip verification // Derive application keys using transcript AFTER server Finished _tls13_derive_application_keys(); _hs_state = HsState::CLI_TLS13_SEND_FINISHED; break; } // --------------------------- // TLS 1.3: send client Finished // --------------------------- case HsState::CLI_TLS13_SEND_FINISHED: { // Build client Finished verify_data std::vector<uint8_t> th = sha256_hash(_handshake_transcript); std::vector<uint8_t> empty; std::vector<uint8_t> finished_key = _hkdf_expand_label(_tls13_c_hs_secret, "finished", empty, 32); std::vector<uint8_t> verify_data = _hmac_sha256(finished_key, th); // Build Finished handshake message (type 0x14) std::vector<uint8_t> fin_msg; fin_msg.push_back(0x14); fin_msg.push_back(0x00); fin_msg.push_back(0x00); fin_msg.push_back(0x20); // 32 bytes fin_msg.insert(fin_msg.end(), verify_data.begin(), verify_data.end()); // Add to transcript before sending _handshake_transcript.insert(_handshake_transcript.end(), fin_msg.begin(), fin_msg.end()); // Send encrypted with handshake keys _tls13_send_record(0x16, fin_msg, /*handshake_keys=*/true); flush_out(); _handshakeDone = true; _hs_state = HsState::DONE; return; } // --------------------------- // 3) wait Certificate // --------------------------- Loading Loading @@ -3767,6 +3885,14 @@ void netplus::ssl::_tls13_send_record(uint8_t inner_type, s >>= 8; } std::cerr << "[SEND-REC] _is_client=" << _is_client << " hs_keys=" << handshake_keys << " seq=" << seq << "\n"; std::cerr << "[SEND-REC] base_iv: "; for(int i=0;i<12;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(base_iv[i]); std::cerr << std::dec << "\n"; std::cerr << "[SEND-REC] nonce: "; for(int i=0;i<12;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(nonce[i]); std::cerr << std::dec << "\n"; // ------------------------------------------------------------ // Outer TLSCiphertext header (this is the AEAD AAD) // Protected record always has outer content type 0x17 Loading Loading
src/ssl.cpp +176 −50 Original line number Diff line number Diff line Loading @@ -763,6 +763,10 @@ void netplus::ssl::_sendHandshake(uint8_t type, const std::vector<uint8_t>& data handshake.push_back(data.size() & 0xFF); handshake.insert(handshake.end(), data.begin(), data.end()); std::cerr << "[TRANSCRIPT] _sendHandshake type=0x" << std::hex << int(type) << std::dec << " adding " << handshake.size() << " bytes, transcript was " << csock->_handshake_transcript.size() << "\n"; // IMPORTANT: Add the Handshake Layer to the transcript for Finished verification csock->_handshake_transcript.insert(csock->_handshake_transcript.end(), handshake.begin(), handshake.end()); Loading Loading @@ -993,29 +997,6 @@ void netplus::ssl::accept(std::unique_ptr<socket>& csock, bool nonblock) cssock->setNonBlock(); } // --- oben in ssl.cpp (oder anonym namespace) --- static inline const char* tlsRecTypeName(uint8_t ct) { switch (ct) { case 0x14: return "change_cipher_spec"; case 0x15: return "alert"; case 0x16: return "handshake"; case 0x17: return "application_data"; default: return "unknown"; } } static inline const char* hsTypeName(uint8_t ht) { switch (ht) { case 0x01: return "ClientHello"; case 0x02: return "ServerHello"; case 0x08: return "EncryptedExtensions"; case 0x0b: return "Certificate"; case 0x0f: return "CertificateVerify"; case 0x14: return "Finished"; default: return "UnknownHS"; } } // no-op debug helper (can be enabled for troubleshooting) static inline void dumpHex(const char*, const uint8_t*, size_t, size_t = 64) {} Loading @@ -1033,7 +1014,6 @@ std::vector<uint8_t> netplus::ssl::_fetchNextHandshakePlain() // 1) already buffered handshake message? // -------------------------------------------------------- if (_rx_handshake_buf.size() >= 4) { uint8_t ht = _rx_handshake_buf[0]; uint32_t len = (uint32_t(_rx_handshake_buf[1]) << 16) | (uint32_t(_rx_handshake_buf[2]) << 8) | Loading @@ -1045,6 +1025,10 @@ std::vector<uint8_t> netplus::ssl::_fetchNextHandshakePlain() _rx_handshake_buf.erase(_rx_handshake_buf.begin(), _rx_handshake_buf.begin() + 4 + len); std::cerr << "[TRANSCRIPT] _fetchNextHandshakePlain type=0x" << std::hex << int(msg[0]) << std::dec << " adding " << msg.size() << " bytes, transcript was " << _handshake_transcript.size() << "\n"; // transcript: handshake messages only _handshake_transcript.insert(_handshake_transcript.end(), msg.begin(), msg.end()); Loading Loading @@ -1203,8 +1187,10 @@ std::vector<uint8_t> netplus::ssl::_fetchNextHandshakeTLS13() uint64_t seq = _tls13_hs_recv_seq; // build nonce = iv XOR seq // When receiving: client uses s2c IV, server uses c2s IV uint8_t nonce[12]={0}; tls13_make_nonce(nonce, _tls13_hs_iv_s2c, seq); const uint8_t* recv_iv = _is_client ? _tls13_hs_iv_s2c : _tls13_hs_iv_c2s; tls13_make_nonce(nonce, recv_iv, seq); // AAD = record header (type=23, legacy_version=0x0303, length = ciphertext_length) uint8_t aad[5]; Loading Loading @@ -1287,8 +1273,20 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() throwSSL(NetException::Error, "TLS1.3 hs recv AEAD not initialized"); // nonce = iv XOR seq // When receiving: client uses s2c IV, server uses c2s IV uint8_t nonce[12]; tls13_make_nonce(nonce, _tls13_hs_iv_c2s, _tls13_hs_recv_seq++); const uint8_t* recv_iv = _is_client ? _tls13_hs_iv_s2c : _tls13_hs_iv_c2s; uint64_t seq_used = _tls13_hs_recv_seq++; tls13_make_nonce(nonce, recv_iv, seq_used); std::cerr << "[RECV-HS] _is_client=" << _is_client << " seq=" << seq_used << "\n"; std::cerr << "[RECV-HS] recv_iv: "; for(int i=0;i<12;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(recv_iv[i]); std::cerr << std::dec << "\n"; std::cerr << "[RECV-HS] nonce: "; for(int i=0;i<12;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(nonce[i]); std::cerr << std::dec << "\n"; std::cerr << "[RECV-HS] ct_len=" << ct_len << " data_len=" << data_len << "\n"; // AAD = outer header with length=data_len uint8_t aad[5]; Loading Loading @@ -1528,24 +1526,6 @@ void netplus::ssl::handshake_after_accept(){ throw e; }; auto hsStateName = [&](netplus::ssl::HsState s) { switch(s){ case netplus::ssl::HsState::IDLE: return "IDLE"; case netplus::ssl::HsState::READ_CLIENT_HELLO: return "READ_CLIENT_HELLO"; case netplus::ssl::HsState::TLS13_SEND_SERVER_HELLO: return "TLS13_SEND_SERVER_HELLO"; case netplus::ssl::HsState::TLS13_SEND_ENCRYPTED_FLIGHT: return "TLS13_SEND_ENCRYPTED_FLIGHT"; case netplus::ssl::HsState::TLS13_WAIT_CLIENT_FINISHED: return "TLS13_WAIT_CLIENT_FINISHED"; case netplus::ssl::HsState::SEND_SERVER_FLIGHT: return "SEND_SERVER_FLIGHT"; case netplus::ssl::HsState::WAIT_CKE: return "WAIT_CKE"; case netplus::ssl::HsState::WAIT_CCS: return "WAIT_CCS"; case netplus::ssl::HsState::WAIT_FIN: return "WAIT_FIN"; case netplus::ssl::HsState::SEND_CCS_FIN: return "SEND_CCS_FIN"; case netplus::ssl::HsState::DONE: return "DONE"; case netplus::ssl::HsState::FAIL: return "FAIL"; default: return "???"; } }; auto gen_tls13_p256_scalar = [&](uint8_t out_priv[32]) { for (;;) { netplus::fillRandomBytes(out_priv, 32); Loading Loading @@ -1801,6 +1781,9 @@ void netplus::ssl::handshake_after_accept(){ _is_tls13 = true; _chosenSuite = tls13Suite; _secure_reneg = false; // NOTE: ClientHello already added to transcript by _fetchNextHandshakePlain() _hs_state = HsState::TLS13_SEND_SERVER_HELLO; TLSDBG("TLS1.3 selected suite=" << std::hex << _chosenSuite << " group=" << _selected_group); Loading Loading @@ -2450,6 +2433,9 @@ void netplus::ssl::handshake_after_accept(){ server_pub ); std::cerr << "[TRANSCRIPT] Server adding ServerHello " << hs.size() << " bytes, transcript was " << _handshake_transcript.size() << "\n"; // transcript includes plaintext SH _handshake_transcript.insert(_handshake_transcript.end(), hs.begin(), hs.end()); Loading Loading @@ -2701,6 +2687,14 @@ 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"); std::cerr << "[DERIVE-HS] ecdhe_shared: "; for(auto b : ecdhe_shared) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << std::dec << "\n"; std::cerr << "[DERIVE-HS] transcript_len=" << _handshake_transcript.size() << "\n"; std::cerr << "[DERIVE-HS] transcript_hash: "; for(auto b : th) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << std::dec << "\n"; std::vector<uint8_t> zeros(32, 0x00); // SHA-256 hash of empty string - needed for "derived" context per RFC 8446 Loading Loading @@ -2728,6 +2722,12 @@ void netplus::ssl::_tls13_derive_handshake_keys(const std::vector<uint8_t>& ecdh std::vector<uint8_t> s_key = _hkdf_expand_label(_tls13_s_hs_secret, "key", empty, 16); std::vector<uint8_t> s_iv = _hkdf_expand_label(_tls13_s_hs_secret, "iv", empty, 12); std::cerr << "[DERIVE-HS] _is_client=" << _is_client << "\n"; std::cerr << "[DERIVE-HS] c_key: "; for(auto b:c_key) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << "\n"; std::cerr << "[DERIVE-HS] c_iv: "; for(auto b:c_iv) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << "\n"; std::cerr << "[DERIVE-HS] s_key: "; for(auto b:s_key) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << "\n"; std::cerr << "[DERIVE-HS] s_iv: "; for(auto b:s_iv) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(b); std::cerr << std::dec << "\n"; // reset handshake record seq _tls13_hs_send_seq = 0; _tls13_hs_recv_seq = 0; Loading Loading @@ -2967,9 +2967,21 @@ void netplus::ssl::handshake_after_connect(){ std::vector<uint8_t> sh; sh = _fetchNextHandshakePlain(); // Handle would-block: _fetchNextHandshakePlain returns empty if no data ready if (sh.empty()) { note("ssl::handshake_after_connect: wait ServerHello"); } const uint8_t* p = sh.data(); size_t n = sh.size(); std::cerr << "[CLI-SH] ServerHello size=" << n << " type=0x" << std::hex << (n>0?int(p[0]):-1) << std::dec << "\n"; if (n >= 5) { std::cerr << "[CLI-SH] first 5 bytes: "; for(size_t i=0;i<5;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(p[i]) << " "; std::cerr << std::dec << "\n"; } if (n < 4 + 2 + 32 + 1 + 2 + 1) { NetException e; e[NetException::Error] << "ServerHello too short"; Loading @@ -2978,6 +2990,9 @@ void netplus::ssl::handshake_after_connect(){ size_t off = 4; // skip handshake header // legacy_version (2 bytes: 0x03, 0x03) off += 2; // server_random (32) _serverRandom.assign(p + off, p + off + 32); off += 32; Loading Loading @@ -3125,10 +3140,8 @@ void netplus::ssl::handshake_after_connect(){ eoff += el; } // transcript: include ClientHello + ServerHello for TLS1.3 // NOTE: you must have already appended ClientHello earlier when sending it. // We'll append ServerHello now: _handshake_transcript.insert(_handshake_transcript.end(), sh.begin(), sh.end()); // NOTE: ServerHello was already added to transcript by _fetchNextHandshakePlain() // Do NOT add it again here! if (tls13) { if (!_client_offered_tls13 || !_tls13_got_key_share){ Loading @@ -3147,9 +3160,8 @@ void netplus::ssl::handshake_after_connect(){ throw e; } _handshake_transcript.insert(_handshake_transcript.end(), sh.begin(), sh.end()); // Derive TLS1.3 handshake keys from ECDHE shared // (transcript already includes ClientHello + ServerHello from above) _tls13_derive_handshake_keys(ecdhe_shared); // switch to TLS 1.3 handshake path Loading Loading @@ -3229,6 +3241,112 @@ void netplus::ssl::handshake_after_connect(){ _hs_state = HsState::CLI_TLS13_WAIT_CERT_VERIFY; break; } // --------------------------- // TLS 1.3: wait CertificateVerify (type 0x0f) // --------------------------- case HsState::CLI_TLS13_WAIT_CERT_VERIFY: { if (_rx_handshake_buf.empty() || _rx_handshake_off >= _rx_handshake_buf.size()) { _rx_handshake_buf = _tls13_read_record_handshake(); _rx_handshake_off = 0; } std::vector<uint8_t> msg; if (!_tls13_pop_hs_from_buf(msg)) { _rx_handshake_buf = _tls13_read_record_handshake(); _rx_handshake_off = 0; if (!_tls13_pop_hs_from_buf(msg)) note("ssl::handshake_after_connect: wait TLS1.3 CertificateVerify"); } if (msg.size() < 4 || msg[0] != 0x0f) { NetException e; e[NetException::Error] << "TLS1.3 expected CertificateVerify got " << int(msg.empty()? -1 : msg[0]); throw e; } // transcript add _handshake_transcript.insert(_handshake_transcript.end(), msg.begin(), msg.end()); // TODO: verify RSA-PSS signature over transcript // For now, skip verification _hs_state = HsState::CLI_TLS13_WAIT_FINISHED; break; } // --------------------------- // TLS 1.3: wait server Finished (type 0x14) // --------------------------- case HsState::CLI_TLS13_WAIT_FINISHED: { if (_rx_handshake_buf.empty() || _rx_handshake_off >= _rx_handshake_buf.size()) { _rx_handshake_buf = _tls13_read_record_handshake(); _rx_handshake_off = 0; } std::vector<uint8_t> msg; if (!_tls13_pop_hs_from_buf(msg)) { _rx_handshake_buf = _tls13_read_record_handshake(); _rx_handshake_off = 0; if (!_tls13_pop_hs_from_buf(msg)) note("ssl::handshake_after_connect: wait TLS1.3 server Finished"); } if (msg.size() < 4 || msg[0] != 0x14) { NetException e; e[NetException::Error] << "TLS1.3 expected server Finished got " << int(msg.empty()? -1 : msg[0]); throw e; } // Save transcript hash BEFORE adding server Finished (for client Finished) std::vector<uint8_t> th_before_sfin = sha256_hash(_handshake_transcript); // transcript add server Finished _handshake_transcript.insert(_handshake_transcript.end(), msg.begin(), msg.end()); // TODO: verify server Finished verify_data // For now, skip verification // Derive application keys using transcript AFTER server Finished _tls13_derive_application_keys(); _hs_state = HsState::CLI_TLS13_SEND_FINISHED; break; } // --------------------------- // TLS 1.3: send client Finished // --------------------------- case HsState::CLI_TLS13_SEND_FINISHED: { // Build client Finished verify_data std::vector<uint8_t> th = sha256_hash(_handshake_transcript); std::vector<uint8_t> empty; std::vector<uint8_t> finished_key = _hkdf_expand_label(_tls13_c_hs_secret, "finished", empty, 32); std::vector<uint8_t> verify_data = _hmac_sha256(finished_key, th); // Build Finished handshake message (type 0x14) std::vector<uint8_t> fin_msg; fin_msg.push_back(0x14); fin_msg.push_back(0x00); fin_msg.push_back(0x00); fin_msg.push_back(0x20); // 32 bytes fin_msg.insert(fin_msg.end(), verify_data.begin(), verify_data.end()); // Add to transcript before sending _handshake_transcript.insert(_handshake_transcript.end(), fin_msg.begin(), fin_msg.end()); // Send encrypted with handshake keys _tls13_send_record(0x16, fin_msg, /*handshake_keys=*/true); flush_out(); _handshakeDone = true; _hs_state = HsState::DONE; return; } // --------------------------- // 3) wait Certificate // --------------------------- Loading Loading @@ -3767,6 +3885,14 @@ void netplus::ssl::_tls13_send_record(uint8_t inner_type, s >>= 8; } std::cerr << "[SEND-REC] _is_client=" << _is_client << " hs_keys=" << handshake_keys << " seq=" << seq << "\n"; std::cerr << "[SEND-REC] base_iv: "; for(int i=0;i<12;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(base_iv[i]); std::cerr << std::dec << "\n"; std::cerr << "[SEND-REC] nonce: "; for(int i=0;i<12;i++) std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(nonce[i]); std::cerr << std::dec << "\n"; // ------------------------------------------------------------ // Outer TLSCiphertext header (this is the AEAD AAD) // Protected record always has outer content type 0x17 Loading