Loading src/crypto/rsa_pss_sha256.cpp +2 −11 Original line number Diff line number Diff line Loading @@ -95,26 +95,18 @@ std::vector<uint8_t> rsa_pss_sha256::emsa_pss_encode(const std::vector<uint8_t>& std::vector<uint8_t> rsa_pss_sha256::sign(netplus::rsa& key, const std::vector<uint8_t>& message) { std::cerr << "[DEBUG] rsa_pss_sha256::sign: message.size=" << message.size() << std::endl; // hash message std::vector<uint8_t> mHash = sha256(message); std::cerr << "[DEBUG] rsa_pss_sha256::sign: mHash.size=" << mHash.size() << std::endl; if (mHash.size() != 32) { std::cerr << "[DEBUG] rsa_pss_sha256::sign: mHash wrong size" << std::endl; if (mHash.size() != 32) return {}; } // emBits = modBits-1 size_t modBits = key.n.bitLength(); size_t emBits = modBits - 1; std::cerr << "[DEBUG] rsa_pss_sha256::sign: modBits=" << modBits << ", emBits=" << emBits << std::endl; std::vector<uint8_t> EM = emsa_pss_encode(mHash, emBits); std::cerr << "[DEBUG] rsa_pss_sha256::sign: EM.size=" << EM.size() << std::endl; if (EM.empty()) { std::cerr << "[DEBUG] rsa_pss_sha256::sign: EM empty" << std::endl; if (EM.empty()) return {}; } netplus::rsa::bigInt m = rsa::bytesToBigIntBE(EM); Loading @@ -123,7 +115,6 @@ std::vector<uint8_t> rsa_pss_sha256::sign(netplus::rsa& key, size_t k = (modBits + 7) / 8; std::vector<uint8_t> out = rsa::bigIntToBytesBE(s, k); std::cerr << "[DEBUG] rsa_pss_sha256::sign: out.size=" << out.size() << std::endl; return out; } Loading src/event/iocp.cpp +82 −10 Original line number Diff line number Diff line Loading @@ -398,11 +398,75 @@ namespace netplus { continue; } // append read bytes into con::RecvData owner->RecvData.append(buf->data.buf, (size_t)bytes); // For SSL: need to handle handshake and decryption bool isSSL = (owner->csock->_Type == sockettype::SSL); if (isSSL) { ssl* sslSock = static_cast<ssl*>(owner->csock.get()); // Feed raw bytes into SSL layer's receive buffer sslSock->_rx_tcp_buf.insert( sslSock->_rx_tcp_buf.end(), buf->data.buf, buf->data.buf + bytes ); // Check if handshake is done if (!sslSock->getHandshakeDone()) { try { sslSock->handshake_after_accept(); // let user process it // If there's pending data to send (handshake response) if (sslSock->hasPendingWrite()) { sslSock->flush_out(); } } catch (NetException& e) { if (e.getErrorType() == NetException::Note) { // Need more data - repost recv try { post_recv(st, *owner); } catch (...) {} continue; } // Real error - disconnect ev->DisconnectEvent(*owner, tid, 0); remove_con(st, cs); try { owner->csock->close(); } catch (...) {} continue; } // If handshake still not done, wait for more data if (!sslSock->getHandshakeDone()) { try { post_recv(st, *owner); } catch (...) {} continue; } } // Handshake done - try to decrypt application data try { buffer decrypted(BLOCKSIZE); size_t decLen = sslSock->recvData(decrypted, 0); if (decLen > 0) { owner->RecvData.append(decrypted.data.buf, decLen); ev->RequestEvent(*owner, tid, (ULONG_PTR)decLen); } } catch (NetException& e) { if (e.getErrorType() != NetException::Note) { ev->DisconnectEvent(*owner, tid, 0); remove_con(st, cs); try { owner->csock->close(); } catch (...) {} continue; } // Note = no complete record yet, just continue } } else { // Plain TCP: append directly owner->RecvData.append(buf->data.buf, (size_t)bytes); ev->RequestEvent(*owner, tid, (ULONG_PTR)bytes); } // if user filled SendData -> try send try { Loading @@ -425,7 +489,14 @@ namespace netplus { } } else if (buf->operation == OP_WRITE) { // send complete -> update offset // For SSL, the send encrypts all pending data at once // So we clear SendData entirely on completion if (owner->csock->getSocketType() == sockettype::SSL) { // SSL: all data was encrypted and sent owner->SendData.clear(); owner->SendOff = 0; } else { // Plain TCP: track bytes sent owner->SendOff += (size_t)bytes; // finished sending? clear buffer Loading @@ -433,6 +504,7 @@ namespace netplus { owner->SendData.clear(); owner->SendOff = 0; } } owner->WritePending.store(false); Loading src/socket.h +0 −2 Original line number Diff line number Diff line Loading @@ -166,7 +166,6 @@ namespace netplus { virtual void sendDataWSA(buffer& data, int flags) = 0; virtual void recvDataWSA(buffer& data, int flags) = 0; virtual void prime_read(buffer& data) = 0; virtual void onWriteComplete(size_t bytes) {}; #endif virtual void bind() = 0; Loading Loading @@ -332,7 +331,6 @@ namespace netplus { void sendDataWSA(buffer& data, int flags); void recvDataWSA(buffer& data, int flags); void prime_read(buffer& data); void onWriteComplete(size_t bytes) override; #endif bool loadServerPrivateKeyDer(const std::string& keyDerPath); Loading src/ssl.cpp +97 −222 Original line number Diff line number Diff line Loading @@ -2238,8 +2238,6 @@ void netplus::ssl::handshake_after_accept(){ case HsState::TLS13_WAIT_CLIENT_FINISHED: { std::cerr << "[HS] TLS13_WAIT_CLIENT_FINISHED entry\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); Loading @@ -2247,15 +2245,12 @@ void netplus::ssl::handshake_after_accept(){ // wir erwarten encrypted records (outer 0x17) mit handshake keys std::vector<uint8_t> msg = _fetchNextHandshakePlain(); if (msg.empty()) { std::cerr << "[HS] TLS13_WAIT_CLIENT_FINISHED: msg empty, throwing Note\n"; // Must throw Note so event loop waits for more data NetException n; n[NetException::Note] << "TLS1.3: waiting for client Finished"; throw n; } std::cerr << "[HS] TLS13_WAIT_CLIENT_FINISHED: got msg size=" << msg.size() << "\n"; // msg enthält Handshake Msg (type+len24+body) if (msg.size() < 4) throwSSL(NetException::Error, "TLS1.3 client Finished too short"); Loading Loading @@ -2304,8 +2299,6 @@ void netplus::ssl::handshake_after_accept(){ case HsState::TLS13_SEND_ENCRYPTED_FLIGHT: { std::cerr << "[HS] TLS13_SEND_ENCRYPTED_FLIGHT entry, queued=" << _tls13_encflight_queued << "\n"; if (!_tls13_encflight_queued) { if (!_aes13_hs_send || !_aes13_hs_recv) Loading Loading @@ -2336,19 +2329,14 @@ void netplus::ssl::handshake_after_accept(){ try { flush_out(); std::cerr << "[HS] TLS13_SEND_ENCRYPTED_FLIGHT: flush_out done, pending=" << hasPendingWrite() << "\n"; } catch(NetException& e) { if (e.getErrorType() == NetException::Note) { std::cerr << "[HS] TLS13_SEND_ENCRYPTED_FLIGHT: flush_out Note, returning\n"; if (e.getErrorType() == NetException::Note) return; } throw; } if (!hasPendingWrite()) { std::cerr << "[HS] TLS13_SEND_ENCRYPTED_FLIGHT: transition to WAIT_CLIENT_FINISHED\n"; if (!hasPendingWrite()) _hs_state = HsState::TLS13_WAIT_CLIENT_FINISHED; } return; } Loading Loading @@ -3603,12 +3591,8 @@ size_t netplus::ssl::recvData(buffer& data, int flags) // ============================================================ for (int attempts = 0; attempts < 4; ++attempts) { std::cerr << "[recvData] attempt=" << attempts << " calling readTlsRecordAsync\n"; std::vector<uint8_t> rec = readTlsRecordAsync(); // may throw Note std::cerr << "[recvData] got rec size=" << rec.size() << "\n"; if (rec.size() < 5) throwSSL(NetException::Error, "TLS record too short"); Loading Loading @@ -4166,223 +4150,114 @@ std::vector<uint8_t> netplus::ssl::_rsa_pss_sha256_sign(const std::vector<uint8_ #ifdef Windows void netplus::ssl::prime_read(buffer& data) { // Metadaten im Buffer für IOCP vorbereiten std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); data.operation = OP_READ; // WICHTIG: Wir nutzen hier TCP::recvDataWSA, um den ersten Ciphertext // vom Client (Client Hello) zu empfangen. this->tcp::recvDataWSA(data, 0); } void netplus::ssl::onWriteComplete(size_t bytes) { if (_send_record.empty()) return; _send_off += bytes; if (_send_off >= _send_record.size()) { // Whole TLS record sent -> advance seq once _send_seq++; _send_record.clear(); _send_off = 0; } void netplus::ssl::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock, buffer& data){ // This accept is already implemented above at line ~2545 // This stub should not be reached - the other implementation handles it NetException e; e[NetException::Error] << "ssl::accept(LPFN_ACCEPTEX): wrong overload called"; throw e; } void netplus::ssl::sendDataWSA(buffer& data, int flags) { auto throwSSL = [&](int etype, const std::string& msg) -> void { NetException e; e[etype] << "ssl::sendDataWSA: " << msg; throw e; }; // For SSL, we need to encrypt the data before sending // The data in buffer is plaintext - encrypt it and queue for sending // During handshake, just forward raw TCP if (!_handshakeDone) { data.operation = OP_WRITE; std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); return tcp::sendDataWSA(data, flags); // posts WSASend } // During handshake, send raw TLS records from _hs_tx // (handshake data is already queued via queueRaw) } else { // After handshake, encrypt application data from the buffer const char* p = data.data.buf; std::vector<uint8_t> plain(p, p + data.size); // 1) If a TLS record is pending, ONLY flush it (do not consume new plaintext yet) if (!_send_record.empty()) { if (_send_off >= _send_record.size()) { _send_record.clear(); _send_off = 0; return; if (_is_tls13) { // TLS 1.3: Use application keys - this queues to _hs_tx _tls13_send_record(plain, 0x17, false); // 0x17 = application_data } else { // TLS 1.2: Use CBC encryption std::vector<uint8_t> rec = _buildAppDataRecord(plain); queueRaw(rec); } const size_t left = _send_record.size() - _send_off; data.operation = OP_WRITE; std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); data.data.buf = reinterpret_cast<char*>(_send_record.data() + _send_off); data.data.len = static_cast<ULONG>(left); // IMPORTANT: tcp::sendDataWSA must treat WSA_IO_PENDING as success tcp::sendDataWSA(data, flags); return; } // 2) Build a new TLS record from application plaintext const size_t want = static_cast<size_t>(data.data.len); if (want == 0) return; // Now post the actual WSASend with the encrypted data from _hs_tx if (_hs_tx.empty()) return; static constexpr size_t TLS_MAX_PLAINTEXT = 16384; const size_t take = (std::min)(want, TLS_MAX_PLAINTEXT); // Copy encrypted data to the buffer's memory for WSASend // Resize buffer if needed size_t encSize = _hs_tx.size(); if (encSize > data.size) { // Reallocate buffer data delete[] data.data.buf; data.data.buf = new char[encSize]; data.size = encSize; } std::memcpy(data.data.buf, _hs_tx.data(), encSize); data.data.len = static_cast<ULONG>(encSize); const uint8_t recordType = 0x17; // ApplicationData // Clear send queue now that we've copied it _hs_tx.clear(); if (!_aes) throwSSL(NetException::Error, "_aes is null"); if (_mac_key.empty()) throwSSL(NetException::Error, "send MAC key missing"); DWORD bytesSent = 0; DWORD sendFlags = 0; // Copy plaintext (you can avoid this copy if you want later; keep it simple first) std::vector<uint8_t> content( reinterpret_cast<const uint8_t*>(data.data.buf), reinterpret_cast<const uint8_t*>(data.data.buf) + take int result = WSASend( _Socket, &data.data, // WSABUF 1, &bytesSent, sendFlags, &data.overlapped, nullptr ); // MAC over (seq, type, version, length, content) std::vector<uint8_t> mac = _calculateHMAC(content, recordType, _send_seq, _mac_key); // inner = content || mac || padding std::vector<uint8_t> inner; inner.reserve(content.size() + mac.size() + 32); inner.insert(inner.end(), content.begin(), content.end()); inner.insert(inner.end(), mac.begin(), mac.end()); constexpr size_t block = 16; size_t rem = inner.size() % block; size_t padBytes = (rem == 0) ? block : (block - rem); uint8_t padVal = static_cast<uint8_t>(padBytes - 1); inner.insert(inner.end(), padBytes, padVal); // Explicit IV (TLS 1.1+) std::vector<uint8_t> iv(block); { std::random_device rd; for (auto& b : iv) b = static_cast<uint8_t>(rd() & 0xFF); if (result == SOCKET_ERROR) { int err = WSAGetLastError(); if (err != WSA_IO_PENDING) { NetException e; e[NetException::Error] << "ssl::sendDataWSA: WSASend failed: " << err; throw e; } } std::vector<uint8_t> ct = _aes->encryptCBC(inner, iv); const uint16_t fragLen = static_cast<uint16_t>(iv.size() + ct.size()); // Build complete TLS record into persistent storage _send_record.clear(); _send_record.reserve(5 + fragLen); _send_record.push_back(recordType); _send_record.push_back(0x03); _send_record.push_back(0x03); // TLS 1.2 _send_record.push_back(static_cast<uint8_t>((fragLen >> 8) & 0xFF)); _send_record.push_back(static_cast<uint8_t>(fragLen & 0xFF)); _send_record.insert(_send_record.end(), iv.begin(), iv.end()); _send_record.insert(_send_record.end(), ct.begin(), ct.end()); _send_off = 0; // 3) Post async send of the ciphertext using the SAME persistent OVERLAPPED buffer data.operation = OP_WRITE; std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); data.data.buf = reinterpret_cast<char*>(_send_record.data()); data.data.len = static_cast<ULONG>(_send_record.size()); tcp::sendDataWSA(data, flags); return; // consumed plaintext bytes from caller's queue } void netplus::ssl::recvDataWSA(buffer& data, int flags) { // For IOCP the raw bytes are appended to _rx_netbuf by the EventWorker; // this method decodes any complete TLS records present in _rx_netbuf. if (!_handshakeDone) return; // Post an overlapped receive operation // The received data will be ciphertext that needs decryption data.wsaBuf.buf = data.data.buf; data.wsaBuf.len = static_cast<ULONG>(data.size); DWORD bytesRecv = 0; DWORD recvFlags = 0; int result = WSARecv( _Socket, &data.wsaBuf, 1, &bytesRecv, &recvFlags, &data.overlapped, nullptr ); auto throwSSL = [&](int etype, const std::string& msg) -> void { if (result == SOCKET_ERROR) { int err = WSAGetLastError(); if (err != WSA_IO_PENDING) { NetException e; e[etype] << "ssl::recvDataWSA: " << msg; e[NetException::Error] << "ssl::recvDataWSA: WSARecv failed: " << err; throw e; }; // 0) Serve buffered plaintext if (_recv_off < _recv_record.size()) { const size_t avail = _recv_record.size() - _recv_off; const size_t outLen = (std::min)((size_t)data.size, avail); std::memcpy(data.data.buf, _recv_record.data() + _recv_off, outLen); _recv_off += outLen; if (_recv_off == _recv_record.size()) { _recv_record.clear(); _recv_off = 0; } return; } // 1) Process ciphertext already present in _rx_netbuf for (;;) { if (_rx_netbuf.size() < 5) return; // need header const uint8_t type = _rx_netbuf[0]; const uint16_t ver = (uint16_t(_rx_netbuf[1]) << 8) | uint16_t(_rx_netbuf[2]); const uint16_t recLen = (uint16_t(_rx_netbuf[3]) << 8) | uint16_t(_rx_netbuf[4]); static constexpr size_t TLS_MAX_RECORD = 16384 + 2048; if (recLen == 0 || recLen > TLS_MAX_RECORD) { throwSSL(NetException::Error, "invalid TLS record length " + std::to_string((unsigned long long)recLen)); } if (ver != 0x0303) { throwSSL(NetException::Error, "unexpected TLS version 0x" + std::to_string(ver)); } const size_t total = 5 + (size_t)recLen; if (_rx_netbuf.size() < total) return; // incomplete std::vector<uint8_t> frag(_rx_netbuf.begin() + 5, _rx_netbuf.begin() + total); _rx_netbuf.erase(_rx_netbuf.begin(), _rx_netbuf.begin() + total); if (type == 0x16) { _recv_seq++; continue; } if (type != 0x17 && type != 0x15) throwSSL(NetException::Error, "unsupported TLS record type " + std::to_string((int)type)); constexpr size_t block = 16; if (frag.size() < 2 * block || ((frag.size() - block) % block) != 0) { throwSSL(NetException::Error, "invalid CBC fragment size " + std::to_string(frag.size())); } if (!_aes_recv) throwSSL(NetException::Error, "_aes_recv is null"); std::vector<uint8_t> iv(frag.begin(), frag.begin() + block); std::vector<uint8_t> ciphertext(frag.begin() + block, frag.end()); std::vector<uint8_t> plain = _aes_recv->decryptCBC(ciphertext, iv); constexpr size_t macLen = 20; if (plain.size() < macLen + 1) throwSSL(NetException::Error, "bad padding/mac (too short)"); const uint8_t padLen = plain.back(); const size_t padBytes = size_t(padLen) + 1; if (padBytes > plain.size()) throwSSL(NetException::Error, "bad padding/mac (pad too large)"); const size_t noPadLen = plain.size() - padBytes; if (noPadLen < macLen) throwSSL(NetException::Error, "bad padding/mac (noPad < mac)"); for (size_t i = noPadLen; i < plain.size(); ++i) { if (plain[i] != padLen) throwSSL(NetException::Error, "bad padding bytes"); } void netplus::ssl::prime_read(buffer& data) { // Initialize buffer for overlapped read std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); data.operation = OP_READ; const size_t contentLen = noPadLen - macLen; std::vector<uint8_t> content(plain.begin(), plain.begin() + contentLen); std::vector<uint8_t> recvMac(plain.begin() + contentLen, plain.begin() + noPadLen); std::vector<uint8_t> calcMac = _calculateHMAC(content, type, _recv_seq, _client_mac_key); if (calcMac.size() != recvMac.size() || !std::equal(calcMac.begin(), calcMac.end(), recvMac.begin())) { throwSSL(NetException::Error, "bad mac"); // Post the async receive recvDataWSA(data, 0); } _recv_seq++; _recv_record = std::move(content); _recv_off = 0; if (_recv_record.empty()) continue; const size_t outLen = (std::min)((size_t)data.size, _recv_record.size()); std::memcpy(data.data.buf, _recv_record.data(), outLen); _recv_off = outLen; if (_recv_off == _recv_record.size()) { _recv_record.clear(); _recv_off = 0; } return; } } #endif Loading
src/crypto/rsa_pss_sha256.cpp +2 −11 Original line number Diff line number Diff line Loading @@ -95,26 +95,18 @@ std::vector<uint8_t> rsa_pss_sha256::emsa_pss_encode(const std::vector<uint8_t>& std::vector<uint8_t> rsa_pss_sha256::sign(netplus::rsa& key, const std::vector<uint8_t>& message) { std::cerr << "[DEBUG] rsa_pss_sha256::sign: message.size=" << message.size() << std::endl; // hash message std::vector<uint8_t> mHash = sha256(message); std::cerr << "[DEBUG] rsa_pss_sha256::sign: mHash.size=" << mHash.size() << std::endl; if (mHash.size() != 32) { std::cerr << "[DEBUG] rsa_pss_sha256::sign: mHash wrong size" << std::endl; if (mHash.size() != 32) return {}; } // emBits = modBits-1 size_t modBits = key.n.bitLength(); size_t emBits = modBits - 1; std::cerr << "[DEBUG] rsa_pss_sha256::sign: modBits=" << modBits << ", emBits=" << emBits << std::endl; std::vector<uint8_t> EM = emsa_pss_encode(mHash, emBits); std::cerr << "[DEBUG] rsa_pss_sha256::sign: EM.size=" << EM.size() << std::endl; if (EM.empty()) { std::cerr << "[DEBUG] rsa_pss_sha256::sign: EM empty" << std::endl; if (EM.empty()) return {}; } netplus::rsa::bigInt m = rsa::bytesToBigIntBE(EM); Loading @@ -123,7 +115,6 @@ std::vector<uint8_t> rsa_pss_sha256::sign(netplus::rsa& key, size_t k = (modBits + 7) / 8; std::vector<uint8_t> out = rsa::bigIntToBytesBE(s, k); std::cerr << "[DEBUG] rsa_pss_sha256::sign: out.size=" << out.size() << std::endl; return out; } Loading
src/event/iocp.cpp +82 −10 Original line number Diff line number Diff line Loading @@ -398,11 +398,75 @@ namespace netplus { continue; } // append read bytes into con::RecvData owner->RecvData.append(buf->data.buf, (size_t)bytes); // For SSL: need to handle handshake and decryption bool isSSL = (owner->csock->_Type == sockettype::SSL); if (isSSL) { ssl* sslSock = static_cast<ssl*>(owner->csock.get()); // Feed raw bytes into SSL layer's receive buffer sslSock->_rx_tcp_buf.insert( sslSock->_rx_tcp_buf.end(), buf->data.buf, buf->data.buf + bytes ); // Check if handshake is done if (!sslSock->getHandshakeDone()) { try { sslSock->handshake_after_accept(); // let user process it // If there's pending data to send (handshake response) if (sslSock->hasPendingWrite()) { sslSock->flush_out(); } } catch (NetException& e) { if (e.getErrorType() == NetException::Note) { // Need more data - repost recv try { post_recv(st, *owner); } catch (...) {} continue; } // Real error - disconnect ev->DisconnectEvent(*owner, tid, 0); remove_con(st, cs); try { owner->csock->close(); } catch (...) {} continue; } // If handshake still not done, wait for more data if (!sslSock->getHandshakeDone()) { try { post_recv(st, *owner); } catch (...) {} continue; } } // Handshake done - try to decrypt application data try { buffer decrypted(BLOCKSIZE); size_t decLen = sslSock->recvData(decrypted, 0); if (decLen > 0) { owner->RecvData.append(decrypted.data.buf, decLen); ev->RequestEvent(*owner, tid, (ULONG_PTR)decLen); } } catch (NetException& e) { if (e.getErrorType() != NetException::Note) { ev->DisconnectEvent(*owner, tid, 0); remove_con(st, cs); try { owner->csock->close(); } catch (...) {} continue; } // Note = no complete record yet, just continue } } else { // Plain TCP: append directly owner->RecvData.append(buf->data.buf, (size_t)bytes); ev->RequestEvent(*owner, tid, (ULONG_PTR)bytes); } // if user filled SendData -> try send try { Loading @@ -425,7 +489,14 @@ namespace netplus { } } else if (buf->operation == OP_WRITE) { // send complete -> update offset // For SSL, the send encrypts all pending data at once // So we clear SendData entirely on completion if (owner->csock->getSocketType() == sockettype::SSL) { // SSL: all data was encrypted and sent owner->SendData.clear(); owner->SendOff = 0; } else { // Plain TCP: track bytes sent owner->SendOff += (size_t)bytes; // finished sending? clear buffer Loading @@ -433,6 +504,7 @@ namespace netplus { owner->SendData.clear(); owner->SendOff = 0; } } owner->WritePending.store(false); Loading
src/socket.h +0 −2 Original line number Diff line number Diff line Loading @@ -166,7 +166,6 @@ namespace netplus { virtual void sendDataWSA(buffer& data, int flags) = 0; virtual void recvDataWSA(buffer& data, int flags) = 0; virtual void prime_read(buffer& data) = 0; virtual void onWriteComplete(size_t bytes) {}; #endif virtual void bind() = 0; Loading Loading @@ -332,7 +331,6 @@ namespace netplus { void sendDataWSA(buffer& data, int flags); void recvDataWSA(buffer& data, int flags); void prime_read(buffer& data); void onWriteComplete(size_t bytes) override; #endif bool loadServerPrivateKeyDer(const std::string& keyDerPath); Loading
src/ssl.cpp +97 −222 Original line number Diff line number Diff line Loading @@ -2238,8 +2238,6 @@ void netplus::ssl::handshake_after_accept(){ case HsState::TLS13_WAIT_CLIENT_FINISHED: { std::cerr << "[HS] TLS13_WAIT_CLIENT_FINISHED entry\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); Loading @@ -2247,15 +2245,12 @@ void netplus::ssl::handshake_after_accept(){ // wir erwarten encrypted records (outer 0x17) mit handshake keys std::vector<uint8_t> msg = _fetchNextHandshakePlain(); if (msg.empty()) { std::cerr << "[HS] TLS13_WAIT_CLIENT_FINISHED: msg empty, throwing Note\n"; // Must throw Note so event loop waits for more data NetException n; n[NetException::Note] << "TLS1.3: waiting for client Finished"; throw n; } std::cerr << "[HS] TLS13_WAIT_CLIENT_FINISHED: got msg size=" << msg.size() << "\n"; // msg enthält Handshake Msg (type+len24+body) if (msg.size() < 4) throwSSL(NetException::Error, "TLS1.3 client Finished too short"); Loading Loading @@ -2304,8 +2299,6 @@ void netplus::ssl::handshake_after_accept(){ case HsState::TLS13_SEND_ENCRYPTED_FLIGHT: { std::cerr << "[HS] TLS13_SEND_ENCRYPTED_FLIGHT entry, queued=" << _tls13_encflight_queued << "\n"; if (!_tls13_encflight_queued) { if (!_aes13_hs_send || !_aes13_hs_recv) Loading Loading @@ -2336,19 +2329,14 @@ void netplus::ssl::handshake_after_accept(){ try { flush_out(); std::cerr << "[HS] TLS13_SEND_ENCRYPTED_FLIGHT: flush_out done, pending=" << hasPendingWrite() << "\n"; } catch(NetException& e) { if (e.getErrorType() == NetException::Note) { std::cerr << "[HS] TLS13_SEND_ENCRYPTED_FLIGHT: flush_out Note, returning\n"; if (e.getErrorType() == NetException::Note) return; } throw; } if (!hasPendingWrite()) { std::cerr << "[HS] TLS13_SEND_ENCRYPTED_FLIGHT: transition to WAIT_CLIENT_FINISHED\n"; if (!hasPendingWrite()) _hs_state = HsState::TLS13_WAIT_CLIENT_FINISHED; } return; } Loading Loading @@ -3603,12 +3591,8 @@ size_t netplus::ssl::recvData(buffer& data, int flags) // ============================================================ for (int attempts = 0; attempts < 4; ++attempts) { std::cerr << "[recvData] attempt=" << attempts << " calling readTlsRecordAsync\n"; std::vector<uint8_t> rec = readTlsRecordAsync(); // may throw Note std::cerr << "[recvData] got rec size=" << rec.size() << "\n"; if (rec.size() < 5) throwSSL(NetException::Error, "TLS record too short"); Loading Loading @@ -4166,223 +4150,114 @@ std::vector<uint8_t> netplus::ssl::_rsa_pss_sha256_sign(const std::vector<uint8_ #ifdef Windows void netplus::ssl::prime_read(buffer& data) { // Metadaten im Buffer für IOCP vorbereiten std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); data.operation = OP_READ; // WICHTIG: Wir nutzen hier TCP::recvDataWSA, um den ersten Ciphertext // vom Client (Client Hello) zu empfangen. this->tcp::recvDataWSA(data, 0); } void netplus::ssl::onWriteComplete(size_t bytes) { if (_send_record.empty()) return; _send_off += bytes; if (_send_off >= _send_record.size()) { // Whole TLS record sent -> advance seq once _send_seq++; _send_record.clear(); _send_off = 0; } void netplus::ssl::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock, buffer& data){ // This accept is already implemented above at line ~2545 // This stub should not be reached - the other implementation handles it NetException e; e[NetException::Error] << "ssl::accept(LPFN_ACCEPTEX): wrong overload called"; throw e; } void netplus::ssl::sendDataWSA(buffer& data, int flags) { auto throwSSL = [&](int etype, const std::string& msg) -> void { NetException e; e[etype] << "ssl::sendDataWSA: " << msg; throw e; }; // For SSL, we need to encrypt the data before sending // The data in buffer is plaintext - encrypt it and queue for sending // During handshake, just forward raw TCP if (!_handshakeDone) { data.operation = OP_WRITE; std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); return tcp::sendDataWSA(data, flags); // posts WSASend } // During handshake, send raw TLS records from _hs_tx // (handshake data is already queued via queueRaw) } else { // After handshake, encrypt application data from the buffer const char* p = data.data.buf; std::vector<uint8_t> plain(p, p + data.size); // 1) If a TLS record is pending, ONLY flush it (do not consume new plaintext yet) if (!_send_record.empty()) { if (_send_off >= _send_record.size()) { _send_record.clear(); _send_off = 0; return; if (_is_tls13) { // TLS 1.3: Use application keys - this queues to _hs_tx _tls13_send_record(plain, 0x17, false); // 0x17 = application_data } else { // TLS 1.2: Use CBC encryption std::vector<uint8_t> rec = _buildAppDataRecord(plain); queueRaw(rec); } const size_t left = _send_record.size() - _send_off; data.operation = OP_WRITE; std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); data.data.buf = reinterpret_cast<char*>(_send_record.data() + _send_off); data.data.len = static_cast<ULONG>(left); // IMPORTANT: tcp::sendDataWSA must treat WSA_IO_PENDING as success tcp::sendDataWSA(data, flags); return; } // 2) Build a new TLS record from application plaintext const size_t want = static_cast<size_t>(data.data.len); if (want == 0) return; // Now post the actual WSASend with the encrypted data from _hs_tx if (_hs_tx.empty()) return; static constexpr size_t TLS_MAX_PLAINTEXT = 16384; const size_t take = (std::min)(want, TLS_MAX_PLAINTEXT); // Copy encrypted data to the buffer's memory for WSASend // Resize buffer if needed size_t encSize = _hs_tx.size(); if (encSize > data.size) { // Reallocate buffer data delete[] data.data.buf; data.data.buf = new char[encSize]; data.size = encSize; } std::memcpy(data.data.buf, _hs_tx.data(), encSize); data.data.len = static_cast<ULONG>(encSize); const uint8_t recordType = 0x17; // ApplicationData // Clear send queue now that we've copied it _hs_tx.clear(); if (!_aes) throwSSL(NetException::Error, "_aes is null"); if (_mac_key.empty()) throwSSL(NetException::Error, "send MAC key missing"); DWORD bytesSent = 0; DWORD sendFlags = 0; // Copy plaintext (you can avoid this copy if you want later; keep it simple first) std::vector<uint8_t> content( reinterpret_cast<const uint8_t*>(data.data.buf), reinterpret_cast<const uint8_t*>(data.data.buf) + take int result = WSASend( _Socket, &data.data, // WSABUF 1, &bytesSent, sendFlags, &data.overlapped, nullptr ); // MAC over (seq, type, version, length, content) std::vector<uint8_t> mac = _calculateHMAC(content, recordType, _send_seq, _mac_key); // inner = content || mac || padding std::vector<uint8_t> inner; inner.reserve(content.size() + mac.size() + 32); inner.insert(inner.end(), content.begin(), content.end()); inner.insert(inner.end(), mac.begin(), mac.end()); constexpr size_t block = 16; size_t rem = inner.size() % block; size_t padBytes = (rem == 0) ? block : (block - rem); uint8_t padVal = static_cast<uint8_t>(padBytes - 1); inner.insert(inner.end(), padBytes, padVal); // Explicit IV (TLS 1.1+) std::vector<uint8_t> iv(block); { std::random_device rd; for (auto& b : iv) b = static_cast<uint8_t>(rd() & 0xFF); if (result == SOCKET_ERROR) { int err = WSAGetLastError(); if (err != WSA_IO_PENDING) { NetException e; e[NetException::Error] << "ssl::sendDataWSA: WSASend failed: " << err; throw e; } } std::vector<uint8_t> ct = _aes->encryptCBC(inner, iv); const uint16_t fragLen = static_cast<uint16_t>(iv.size() + ct.size()); // Build complete TLS record into persistent storage _send_record.clear(); _send_record.reserve(5 + fragLen); _send_record.push_back(recordType); _send_record.push_back(0x03); _send_record.push_back(0x03); // TLS 1.2 _send_record.push_back(static_cast<uint8_t>((fragLen >> 8) & 0xFF)); _send_record.push_back(static_cast<uint8_t>(fragLen & 0xFF)); _send_record.insert(_send_record.end(), iv.begin(), iv.end()); _send_record.insert(_send_record.end(), ct.begin(), ct.end()); _send_off = 0; // 3) Post async send of the ciphertext using the SAME persistent OVERLAPPED buffer data.operation = OP_WRITE; std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); data.data.buf = reinterpret_cast<char*>(_send_record.data()); data.data.len = static_cast<ULONG>(_send_record.size()); tcp::sendDataWSA(data, flags); return; // consumed plaintext bytes from caller's queue } void netplus::ssl::recvDataWSA(buffer& data, int flags) { // For IOCP the raw bytes are appended to _rx_netbuf by the EventWorker; // this method decodes any complete TLS records present in _rx_netbuf. if (!_handshakeDone) return; // Post an overlapped receive operation // The received data will be ciphertext that needs decryption data.wsaBuf.buf = data.data.buf; data.wsaBuf.len = static_cast<ULONG>(data.size); DWORD bytesRecv = 0; DWORD recvFlags = 0; int result = WSARecv( _Socket, &data.wsaBuf, 1, &bytesRecv, &recvFlags, &data.overlapped, nullptr ); auto throwSSL = [&](int etype, const std::string& msg) -> void { if (result == SOCKET_ERROR) { int err = WSAGetLastError(); if (err != WSA_IO_PENDING) { NetException e; e[etype] << "ssl::recvDataWSA: " << msg; e[NetException::Error] << "ssl::recvDataWSA: WSARecv failed: " << err; throw e; }; // 0) Serve buffered plaintext if (_recv_off < _recv_record.size()) { const size_t avail = _recv_record.size() - _recv_off; const size_t outLen = (std::min)((size_t)data.size, avail); std::memcpy(data.data.buf, _recv_record.data() + _recv_off, outLen); _recv_off += outLen; if (_recv_off == _recv_record.size()) { _recv_record.clear(); _recv_off = 0; } return; } // 1) Process ciphertext already present in _rx_netbuf for (;;) { if (_rx_netbuf.size() < 5) return; // need header const uint8_t type = _rx_netbuf[0]; const uint16_t ver = (uint16_t(_rx_netbuf[1]) << 8) | uint16_t(_rx_netbuf[2]); const uint16_t recLen = (uint16_t(_rx_netbuf[3]) << 8) | uint16_t(_rx_netbuf[4]); static constexpr size_t TLS_MAX_RECORD = 16384 + 2048; if (recLen == 0 || recLen > TLS_MAX_RECORD) { throwSSL(NetException::Error, "invalid TLS record length " + std::to_string((unsigned long long)recLen)); } if (ver != 0x0303) { throwSSL(NetException::Error, "unexpected TLS version 0x" + std::to_string(ver)); } const size_t total = 5 + (size_t)recLen; if (_rx_netbuf.size() < total) return; // incomplete std::vector<uint8_t> frag(_rx_netbuf.begin() + 5, _rx_netbuf.begin() + total); _rx_netbuf.erase(_rx_netbuf.begin(), _rx_netbuf.begin() + total); if (type == 0x16) { _recv_seq++; continue; } if (type != 0x17 && type != 0x15) throwSSL(NetException::Error, "unsupported TLS record type " + std::to_string((int)type)); constexpr size_t block = 16; if (frag.size() < 2 * block || ((frag.size() - block) % block) != 0) { throwSSL(NetException::Error, "invalid CBC fragment size " + std::to_string(frag.size())); } if (!_aes_recv) throwSSL(NetException::Error, "_aes_recv is null"); std::vector<uint8_t> iv(frag.begin(), frag.begin() + block); std::vector<uint8_t> ciphertext(frag.begin() + block, frag.end()); std::vector<uint8_t> plain = _aes_recv->decryptCBC(ciphertext, iv); constexpr size_t macLen = 20; if (plain.size() < macLen + 1) throwSSL(NetException::Error, "bad padding/mac (too short)"); const uint8_t padLen = plain.back(); const size_t padBytes = size_t(padLen) + 1; if (padBytes > plain.size()) throwSSL(NetException::Error, "bad padding/mac (pad too large)"); const size_t noPadLen = plain.size() - padBytes; if (noPadLen < macLen) throwSSL(NetException::Error, "bad padding/mac (noPad < mac)"); for (size_t i = noPadLen; i < plain.size(); ++i) { if (plain[i] != padLen) throwSSL(NetException::Error, "bad padding bytes"); } void netplus::ssl::prime_read(buffer& data) { // Initialize buffer for overlapped read std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED)); data.operation = OP_READ; const size_t contentLen = noPadLen - macLen; std::vector<uint8_t> content(plain.begin(), plain.begin() + contentLen); std::vector<uint8_t> recvMac(plain.begin() + contentLen, plain.begin() + noPadLen); std::vector<uint8_t> calcMac = _calculateHMAC(content, type, _recv_seq, _client_mac_key); if (calcMac.size() != recvMac.size() || !std::equal(calcMac.begin(), calcMac.end(), recvMac.begin())) { throwSSL(NetException::Error, "bad mac"); // Post the async receive recvDataWSA(data, 0); } _recv_seq++; _recv_record = std::move(content); _recv_off = 0; if (_recv_record.empty()) continue; const size_t outLen = (std::min)((size_t)data.size, _recv_record.size()); std::memcpy(data.data.buf, _recv_record.data(), outLen); _recv_off = outLen; if (_recv_off == _recv_record.size()) { _recv_record.clear(); _recv_off = 0; } return; } } #endif