Loading src/event/iocp.cpp +22 −1 Original line number Diff line number Diff line Loading @@ -514,9 +514,26 @@ namespace netplus { } // If there's pending data to send (handshake response) // CRITICAL: flush_out() posts WSASend ASYNCHRONOUSLY in IOCP mode if (owner->csock->hasPendingWrite()) { std::cerr << "[IOCP] Flushing handshake response via IOCP..." << std::endl; std::cerr << "[IOCP] Flushing handshake response, posting async WSASend..." << std::endl; owner->csock->flush_out(); std::cerr << "[IOCP] flush_out posted - now continuing in READ handler" << std::endl; // After posting async write, DON'T try to recv more data yet. // The write completion handler will decide what's next. // Just post recv and let IOCP sequence things properly. if (!owner->csock->getHandshakeDone()) { std::cerr << "[IOCP] Still in handshake, posting recv for next message" << std::endl; try { post_recv(st, *owner); } catch (...) {} } else { std::cerr << "[IOCP] Handshake complete, posting recv for application data" << std::endl; try { post_recv(st, *owner); } catch (...) {} } continue; } // If handshake still not done, wait for more data Loading Loading @@ -575,6 +592,10 @@ namespace netplus { // So we clear SendData entirely on completion bool isSSL = (owner->csock->getSocketType() == sockettype::SSL); // CRITICAL: Clear the pending IOCP write flag owner->csock->setPendingWrite(false); std::cerr << "[IOCP] Write completed, cleared _pendingIocpWrite" << std::endl; if (isSSL) { // SSL: all data was encrypted and sent owner->SendData.clear(); Loading src/socket.h +10 −1 Original line number Diff line number Diff line Loading @@ -179,6 +179,7 @@ namespace netplus { virtual size_t recvData(buffer& data, int flags = 0) = 0; virtual bool hasPendingWrite() const { return false; } virtual void setPendingWrite(bool pending) {} // Only used for SSL/IOCP virtual bool getHandshakeDone() { return true; } virtual int getSocketType() const { return _Type; } Loading Loading @@ -334,9 +335,15 @@ namespace netplus { bool loadServerPrivateKey(const std::vector<uint8_t>& keyData); bool hasPendingWrite() const override { return (!_send_queue.empty()) || (_send_off < _send_record.size()); // Return true if there's queued data not yet sent // Check _send_queue (queued but not flushed) // Check _send_off (current record being transmitted via WSASend) // Check _pendingIocpWrite (async WSASend posted but not completed) return (!_send_queue.empty()) || (_send_off > 0) || _pendingIocpWrite; } void setPendingWrite(bool pending) override { _pendingIocpWrite = pending; } bool getHandshakeDone() override { return _handshakeDone; } virtual void queueRaw(const std::vector<uint8_t>& v); Loading Loading @@ -485,6 +492,7 @@ namespace netplus { return sha256_hash(_handshake_transcript); } std::vector<uint8_t> _rsa_sha256_pkcs15_sign(const std::vector<uint8_t>& in); std::vector<uint8_t> _rsa_pss_sha256_sign(const std::vector<uint8_t>& in); std::vector<uint8_t> _ecdsa_sha256_sign(const std::vector<uint8_t>& in); Loading Loading @@ -526,6 +534,7 @@ namespace netplus { std::deque<std::vector<uint8_t>> _send_queue; std::vector<uint8_t> _send_record; size_t _send_off = 0; bool _pendingIocpWrite = false; // IOCP WSASend posted but not yet completed std::vector<uint8_t> _rx_record_buf; std::vector<uint8_t> _rx_handshake_buf; Loading src/ssl.cpp +219 −74 Original line number Diff line number Diff line Loading @@ -1266,6 +1266,7 @@ std::vector<uint8_t> netplus::ssl::_fetchNextHandshakeTLS13() std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() { for (;;) { // Loop to skip CCS records std::vector<uint8_t> rec = readTlsRecordAsync(); if (rec.empty()) return {}; Loading @@ -1276,6 +1277,13 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() uint16_t ver = (uint16_t(rec[1]) << 8) | rec[2]; uint16_t rlen = (uint16_t(rec[3]) << 8) | rec[4]; // ✅ Skip CCS records (middlebox compatibility in TLS 1.3) if (outer_type == 0x14) { if (rec.size() == 6 && rec[5] == 0x01) { continue; // ignore CCS, read next record } } if (outer_type == 0x15) throwSSL(NetException::Error, "TLS1.3 alert received"); Loading Loading @@ -1336,10 +1344,29 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() throwSSL(NetException::Error, "TLS1.3 inner all padding"); uint8_t inner_type = inner[end - 1]; if (inner_type != 0x16) std::cerr << "[TLS] record decrypted: size=" << inner.size() << " stripped=" << end << " type=0x" << std::hex << (int)inner_type << std::dec << std::endl; std::cerr.flush(); // ✅ If alert, allow it through for debugging (client may be rejecting handshake) if (inner_type == 0x15) { // Alert: level (byte 0) + description (byte 1, in the plaintext) std::cerr << "[TLS] ALERT RECEIVED from client: "; if (end >= 2) { uint8_t level = inner[0]; uint8_t desc = inner[1]; std::cerr << "level=" << (int)level << " desc=" << (int)desc; } std::cerr << std::endl; std::cerr.flush(); // Continue anyway for now to see what happens } if (inner_type != 0x16 && inner_type != 0x15) throwSSL(NetException::Error, "TLS1.3 inner type not handshake"); return std::vector<uint8_t>(inner.begin(), inner.begin() + (end - 1)); } // end loop } std::vector<uint8_t> netplus::ssl::_tls13_build_certificate() Loading Loading @@ -1390,21 +1417,44 @@ std::vector<uint8_t> netplus::ssl::_tls13_build_certificate_verify() toSign.push_back(0x00); toSign.insert(toSign.end(), th.begin(), th.end()); // DEBUG: Log what we're signing fprintf(stderr, "[TLS] CertificateVerify: toSign buffer size=%zu\n", toSign.size()); fprintf(stderr, "[TLS] CertificateVerify: transcript hash (first 16 bytes): "); for (int i = 0; i < 16 && i < (int)th.size(); i++) fprintf(stderr, "%02x ", th[i]); fprintf(stderr, "\n"); std::vector<uint8_t> sig; std::vector<uint8_t> cv; if (_has_ec_key) { // Use ECDSA-SHA256 with P-256 curve // Use RSA-PKCS1-SHA256 (0x0401) for maximum compatibility // This is the classic RSA signature algorithm, widely supported if (_rsa) { // For TLS 1.3, we need to use RSA-PKCS1-SHA256 (0x0401) // which is RSA signature with PKCS#1 v1.5 padding sig = _rsa_sha256_pkcs15_sign(toSign); if (sig.empty()) throwSSL(NetException::Error, "TLS1.3 CertificateVerify: RSA signature empty"); // DEBUG: Log RSA signature details fprintf(stderr, "[TLS] RSA-PKCS1-SHA256 signature generated: size=%zu\n", sig.size()); fprintf(stderr, "[TLS] RSA signature (first 16 bytes): "); for (int i = 0; i < 16 && i < (int)sig.size(); i++) fprintf(stderr, "%02x ", sig[i]); fprintf(stderr, "\n"); fprintf(stderr, "[TLS] RSA signature (last 16 bytes): "); for (int i = (int)sig.size() - 16; i < (int)sig.size(); i++) if (i >= 0) fprintf(stderr, "%02x ", sig[i]); fprintf(stderr, "\n"); cv.push_back(0x04); cv.push_back(0x01); // rsa_pkcs1_sha256 } else if (_has_ec_key) { // Use ECDSA-SHA256 with P-256 curve (0x0403) sig = _ecdsa_sha256_sign(toSign); if (sig.empty()) throwSSL(NetException::Error, "TLS1.3 CertificateVerify: ECDSA signature empty"); fprintf(stderr, "[TLS] ECDSA-SHA256 signature generated: size=%zu\n", sig.size()); cv.push_back(0x04); cv.push_back(0x03); // ecdsa_secp256r1_sha256 } else if (_rsa) { // Use RSA-PSS-SHA256 sig = _rsa_pss_sha256_sign(toSign); if (sig.empty()) throwSSL(NetException::Error, "TLS1.3 CertificateVerify: RSA signature empty"); cv.push_back(0x08); cv.push_back(0x04); // rsa_pss_rsae_sha256 } else { throwSSL(NetException::Error, "TLS1.3 CertificateVerify: no private key available"); } Loading @@ -1412,6 +1462,14 @@ std::vector<uint8_t> netplus::ssl::_tls13_build_certificate_verify() cv.push_back(uint8_t(sig.size() >> 8)); cv.push_back(uint8_t(sig.size() & 0xFF)); cv.insert(cv.end(), sig.begin(), sig.end()); fprintf(stderr, "[TLS] CertificateVerify message complete: total size=%zu (alg_id=2 + len=2 + sig=%zu)\n", cv.size(), sig.size()); fprintf(stderr, "[TLS] CertificateVerify (first 20 bytes): "); for (int i = 0; i < 20 && i < (int)cv.size(); i++) fprintf(stderr, "%02x ", cv[i]); fprintf(stderr, "\n"); return cv; } Loading Loading @@ -2308,8 +2366,9 @@ void netplus::ssl::handshake_after_accept(){ // 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); // wir erwarten encrypted records (outer 0x17) mit handshake keys std::vector<uint8_t> msg = _fetchNextHandshakePlain(); // ✅ 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()) { // Must throw Note so event loop waits for more data NetException n; Loading Loading @@ -2349,7 +2408,16 @@ void netplus::ssl::handshake_after_accept(){ if (expected != client_verify) throwSSL(NetException::Error, "TLS1.3 Finished verify_data mismatch"); // Note: transcript already includes client Finished (added by _fetchNextHandshakePlain) // ✅ Add client Finished to transcript (includes handshake header + body) std::vector<uint8_t> client_finished_msg; client_finished_msg.push_back(0x14); // Finished type client_finished_msg.push_back(0x00); client_finished_msg.push_back(0x00); client_finished_msg.push_back(0x20); // length = 32 client_finished_msg.insert(client_finished_msg.end(), client_verify.begin(), client_verify.end()); _handshake_transcript.insert(_handshake_transcript.end(), client_finished_msg.begin(), client_finished_msg.end()); TLSDBG("✅ TLS1.3 Client Finished verified"); Loading Loading @@ -2611,6 +2679,8 @@ void netplus::ssl::flush_out(){ _SendBuffer->data.len = (ULONG)combined.size(); tcp::flush_out(); _pendingIocpWrite = true; // Mark async write as pending std::cerr << "[SSL] flush_out: Set _pendingIocpWrite=true, hasPendingWrite=" << hasPendingWrite() << std::endl; } catch (...) { throw; } Loading Loading @@ -4329,6 +4399,81 @@ std::vector<uint8_t> netplus::ssl::_tls13_build_server_finished() { return verify_data; } // RSA-PKCS1-v1.5-SHA256 signature for maximum compatibility with TLS 1.3 std::vector<uint8_t> netplus::ssl::_rsa_sha256_pkcs15_sign(const std::vector<uint8_t>& in){ if (!_rsa) throwSSL(NetException::Error, "TLS1.3: no RSA private key"); // Compute SHA256 hash std::vector<uint8_t> mHash = sha256_hash(in); if (mHash.size() != 32) throwSSL(NetException::Error, "SHA256 hash failed"); fprintf(stderr, "[TLS] _rsa_sha256_pkcs15_sign: input size=%zu, hash size=%zu\n", in.size(), mHash.size()); fprintf(stderr, "[TLS] SHA256 hash: "); for (int i = 0; i < 16; i++) fprintf(stderr, "%02x ", mHash[i]); fprintf(stderr, "...\n"); // PKCS#1 v1.5 signature digest info for SHA256: // DigestInfo ::= SEQUENCE { // digestAlgorithm DigestAlgorithmIdentifier, // digest Digest // } // For SHA256: OID = 2.16.840.1.101.3.4.2.1 = 60 86 48 01 65 03 04 02 01 const uint8_t sha256_prefix[] = { 0x30, 0x31, // SEQUENCE length=49 0x30, 0x0d, // SEQUENCE length=13 (algId) 0x06, 0x09, 0x60, 0x86, 0x48, // OID for SHA256 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, // NULL 0x04, 0x20 // OCTET STRING length=32 }; const size_t sha256_prefix_len = sizeof(sha256_prefix) / sizeof(sha256_prefix[0]); fprintf(stderr, "[TLS] SHA256 DigestInfo prefix (%zu bytes): ", sha256_prefix_len); for (size_t i = 0; i < sha256_prefix_len; i++) fprintf(stderr, "%02x ", sha256_prefix[i]); fprintf(stderr, "\n"); // Build DigestInfo: prefix || hash std::vector<uint8_t> tBuf; tBuf.insert(tBuf.end(), sha256_prefix, sha256_prefix + sha256_prefix_len); tBuf.insert(tBuf.end(), mHash.begin(), mHash.end()); fprintf(stderr, "[TLS] DigestInfo total size=%zu\n", tBuf.size()); // Build PKCS#1 v1.5 encoded message size_t modBytes = (_rsa.n.bitLength() + 7) / 8; fprintf(stderr, "[TLS] RSA modulus bits=%u, bytes=%zu\n", _rsa.n.bitLength(), modBytes); if (tBuf.size() > modBytes - 11) throwSSL(NetException::Error, "Message too long for RSA key"); std::vector<uint8_t> EM(modBytes, 0x00); EM[0] = 0x00; EM[1] = 0x01; // Block type (signature) // Padding bytes (0xFF) for (size_t i = 2; i < modBytes - tBuf.size() - 1; i++) EM[i] = 0xFF; EM[modBytes - tBuf.size() - 1] = 0x00; // Separator // Copy digest info std::memcpy(&EM[modBytes - tBuf.size()], tBuf.data(), tBuf.size()); fprintf(stderr, "[TLS] PKCS#1 EM encoding: [00 01 FF...FF 00 DigestInfo]\n"); fprintf(stderr, "[TLS] EM (first 20 bytes): "); for (int i = 0; i < 20 && i < (int)EM.size(); i++) fprintf(stderr, "%02x ", EM[i]); fprintf(stderr, "\n"); fprintf(stderr, "[TLS] EM (last 20 bytes): "); for (int i = (int)EM.size() - 20; i < (int)EM.size(); i++) if (i >= 0) fprintf(stderr, "%02x ", EM[i]); fprintf(stderr, "\n"); // Perform raw RSA operation: signature = EM^d mod n rsa::bigInt m = rsa::bytesToBigIntBE(EM); rsa::bigInt s = rsa::modPow(m, _rsa.d, _rsa.n); std::vector<uint8_t> sig = rsa::bigIntToBytesBE(s, modBytes); fprintf(stderr, "[TLS] Final RSA signature size=%zu (should be %zu)\n", sig.size(), modBytes); return sig; } std::vector<uint8_t> netplus::ssl::_rsa_pss_sha256_sign(const std::vector<uint8_t>& in){ if (!_rsa) throwSSL(NetException::Error, "TLS1.3: no RSA private key"); Loading Loading
src/event/iocp.cpp +22 −1 Original line number Diff line number Diff line Loading @@ -514,9 +514,26 @@ namespace netplus { } // If there's pending data to send (handshake response) // CRITICAL: flush_out() posts WSASend ASYNCHRONOUSLY in IOCP mode if (owner->csock->hasPendingWrite()) { std::cerr << "[IOCP] Flushing handshake response via IOCP..." << std::endl; std::cerr << "[IOCP] Flushing handshake response, posting async WSASend..." << std::endl; owner->csock->flush_out(); std::cerr << "[IOCP] flush_out posted - now continuing in READ handler" << std::endl; // After posting async write, DON'T try to recv more data yet. // The write completion handler will decide what's next. // Just post recv and let IOCP sequence things properly. if (!owner->csock->getHandshakeDone()) { std::cerr << "[IOCP] Still in handshake, posting recv for next message" << std::endl; try { post_recv(st, *owner); } catch (...) {} } else { std::cerr << "[IOCP] Handshake complete, posting recv for application data" << std::endl; try { post_recv(st, *owner); } catch (...) {} } continue; } // If handshake still not done, wait for more data Loading Loading @@ -575,6 +592,10 @@ namespace netplus { // So we clear SendData entirely on completion bool isSSL = (owner->csock->getSocketType() == sockettype::SSL); // CRITICAL: Clear the pending IOCP write flag owner->csock->setPendingWrite(false); std::cerr << "[IOCP] Write completed, cleared _pendingIocpWrite" << std::endl; if (isSSL) { // SSL: all data was encrypted and sent owner->SendData.clear(); Loading
src/socket.h +10 −1 Original line number Diff line number Diff line Loading @@ -179,6 +179,7 @@ namespace netplus { virtual size_t recvData(buffer& data, int flags = 0) = 0; virtual bool hasPendingWrite() const { return false; } virtual void setPendingWrite(bool pending) {} // Only used for SSL/IOCP virtual bool getHandshakeDone() { return true; } virtual int getSocketType() const { return _Type; } Loading Loading @@ -334,9 +335,15 @@ namespace netplus { bool loadServerPrivateKey(const std::vector<uint8_t>& keyData); bool hasPendingWrite() const override { return (!_send_queue.empty()) || (_send_off < _send_record.size()); // Return true if there's queued data not yet sent // Check _send_queue (queued but not flushed) // Check _send_off (current record being transmitted via WSASend) // Check _pendingIocpWrite (async WSASend posted but not completed) return (!_send_queue.empty()) || (_send_off > 0) || _pendingIocpWrite; } void setPendingWrite(bool pending) override { _pendingIocpWrite = pending; } bool getHandshakeDone() override { return _handshakeDone; } virtual void queueRaw(const std::vector<uint8_t>& v); Loading Loading @@ -485,6 +492,7 @@ namespace netplus { return sha256_hash(_handshake_transcript); } std::vector<uint8_t> _rsa_sha256_pkcs15_sign(const std::vector<uint8_t>& in); std::vector<uint8_t> _rsa_pss_sha256_sign(const std::vector<uint8_t>& in); std::vector<uint8_t> _ecdsa_sha256_sign(const std::vector<uint8_t>& in); Loading Loading @@ -526,6 +534,7 @@ namespace netplus { std::deque<std::vector<uint8_t>> _send_queue; std::vector<uint8_t> _send_record; size_t _send_off = 0; bool _pendingIocpWrite = false; // IOCP WSASend posted but not yet completed std::vector<uint8_t> _rx_record_buf; std::vector<uint8_t> _rx_handshake_buf; Loading
src/ssl.cpp +219 −74 Original line number Diff line number Diff line Loading @@ -1266,6 +1266,7 @@ std::vector<uint8_t> netplus::ssl::_fetchNextHandshakeTLS13() std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() { for (;;) { // Loop to skip CCS records std::vector<uint8_t> rec = readTlsRecordAsync(); if (rec.empty()) return {}; Loading @@ -1276,6 +1277,13 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() uint16_t ver = (uint16_t(rec[1]) << 8) | rec[2]; uint16_t rlen = (uint16_t(rec[3]) << 8) | rec[4]; // ✅ Skip CCS records (middlebox compatibility in TLS 1.3) if (outer_type == 0x14) { if (rec.size() == 6 && rec[5] == 0x01) { continue; // ignore CCS, read next record } } if (outer_type == 0x15) throwSSL(NetException::Error, "TLS1.3 alert received"); Loading Loading @@ -1336,10 +1344,29 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() throwSSL(NetException::Error, "TLS1.3 inner all padding"); uint8_t inner_type = inner[end - 1]; if (inner_type != 0x16) std::cerr << "[TLS] record decrypted: size=" << inner.size() << " stripped=" << end << " type=0x" << std::hex << (int)inner_type << std::dec << std::endl; std::cerr.flush(); // ✅ If alert, allow it through for debugging (client may be rejecting handshake) if (inner_type == 0x15) { // Alert: level (byte 0) + description (byte 1, in the plaintext) std::cerr << "[TLS] ALERT RECEIVED from client: "; if (end >= 2) { uint8_t level = inner[0]; uint8_t desc = inner[1]; std::cerr << "level=" << (int)level << " desc=" << (int)desc; } std::cerr << std::endl; std::cerr.flush(); // Continue anyway for now to see what happens } if (inner_type != 0x16 && inner_type != 0x15) throwSSL(NetException::Error, "TLS1.3 inner type not handshake"); return std::vector<uint8_t>(inner.begin(), inner.begin() + (end - 1)); } // end loop } std::vector<uint8_t> netplus::ssl::_tls13_build_certificate() Loading Loading @@ -1390,21 +1417,44 @@ std::vector<uint8_t> netplus::ssl::_tls13_build_certificate_verify() toSign.push_back(0x00); toSign.insert(toSign.end(), th.begin(), th.end()); // DEBUG: Log what we're signing fprintf(stderr, "[TLS] CertificateVerify: toSign buffer size=%zu\n", toSign.size()); fprintf(stderr, "[TLS] CertificateVerify: transcript hash (first 16 bytes): "); for (int i = 0; i < 16 && i < (int)th.size(); i++) fprintf(stderr, "%02x ", th[i]); fprintf(stderr, "\n"); std::vector<uint8_t> sig; std::vector<uint8_t> cv; if (_has_ec_key) { // Use ECDSA-SHA256 with P-256 curve // Use RSA-PKCS1-SHA256 (0x0401) for maximum compatibility // This is the classic RSA signature algorithm, widely supported if (_rsa) { // For TLS 1.3, we need to use RSA-PKCS1-SHA256 (0x0401) // which is RSA signature with PKCS#1 v1.5 padding sig = _rsa_sha256_pkcs15_sign(toSign); if (sig.empty()) throwSSL(NetException::Error, "TLS1.3 CertificateVerify: RSA signature empty"); // DEBUG: Log RSA signature details fprintf(stderr, "[TLS] RSA-PKCS1-SHA256 signature generated: size=%zu\n", sig.size()); fprintf(stderr, "[TLS] RSA signature (first 16 bytes): "); for (int i = 0; i < 16 && i < (int)sig.size(); i++) fprintf(stderr, "%02x ", sig[i]); fprintf(stderr, "\n"); fprintf(stderr, "[TLS] RSA signature (last 16 bytes): "); for (int i = (int)sig.size() - 16; i < (int)sig.size(); i++) if (i >= 0) fprintf(stderr, "%02x ", sig[i]); fprintf(stderr, "\n"); cv.push_back(0x04); cv.push_back(0x01); // rsa_pkcs1_sha256 } else if (_has_ec_key) { // Use ECDSA-SHA256 with P-256 curve (0x0403) sig = _ecdsa_sha256_sign(toSign); if (sig.empty()) throwSSL(NetException::Error, "TLS1.3 CertificateVerify: ECDSA signature empty"); fprintf(stderr, "[TLS] ECDSA-SHA256 signature generated: size=%zu\n", sig.size()); cv.push_back(0x04); cv.push_back(0x03); // ecdsa_secp256r1_sha256 } else if (_rsa) { // Use RSA-PSS-SHA256 sig = _rsa_pss_sha256_sign(toSign); if (sig.empty()) throwSSL(NetException::Error, "TLS1.3 CertificateVerify: RSA signature empty"); cv.push_back(0x08); cv.push_back(0x04); // rsa_pss_rsae_sha256 } else { throwSSL(NetException::Error, "TLS1.3 CertificateVerify: no private key available"); } Loading @@ -1412,6 +1462,14 @@ std::vector<uint8_t> netplus::ssl::_tls13_build_certificate_verify() cv.push_back(uint8_t(sig.size() >> 8)); cv.push_back(uint8_t(sig.size() & 0xFF)); cv.insert(cv.end(), sig.begin(), sig.end()); fprintf(stderr, "[TLS] CertificateVerify message complete: total size=%zu (alg_id=2 + len=2 + sig=%zu)\n", cv.size(), sig.size()); fprintf(stderr, "[TLS] CertificateVerify (first 20 bytes): "); for (int i = 0; i < 20 && i < (int)cv.size(); i++) fprintf(stderr, "%02x ", cv[i]); fprintf(stderr, "\n"); return cv; } Loading Loading @@ -2308,8 +2366,9 @@ void netplus::ssl::handshake_after_accept(){ // 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); // wir erwarten encrypted records (outer 0x17) mit handshake keys std::vector<uint8_t> msg = _fetchNextHandshakePlain(); // ✅ 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()) { // Must throw Note so event loop waits for more data NetException n; Loading Loading @@ -2349,7 +2408,16 @@ void netplus::ssl::handshake_after_accept(){ if (expected != client_verify) throwSSL(NetException::Error, "TLS1.3 Finished verify_data mismatch"); // Note: transcript already includes client Finished (added by _fetchNextHandshakePlain) // ✅ Add client Finished to transcript (includes handshake header + body) std::vector<uint8_t> client_finished_msg; client_finished_msg.push_back(0x14); // Finished type client_finished_msg.push_back(0x00); client_finished_msg.push_back(0x00); client_finished_msg.push_back(0x20); // length = 32 client_finished_msg.insert(client_finished_msg.end(), client_verify.begin(), client_verify.end()); _handshake_transcript.insert(_handshake_transcript.end(), client_finished_msg.begin(), client_finished_msg.end()); TLSDBG("✅ TLS1.3 Client Finished verified"); Loading Loading @@ -2611,6 +2679,8 @@ void netplus::ssl::flush_out(){ _SendBuffer->data.len = (ULONG)combined.size(); tcp::flush_out(); _pendingIocpWrite = true; // Mark async write as pending std::cerr << "[SSL] flush_out: Set _pendingIocpWrite=true, hasPendingWrite=" << hasPendingWrite() << std::endl; } catch (...) { throw; } Loading Loading @@ -4329,6 +4399,81 @@ std::vector<uint8_t> netplus::ssl::_tls13_build_server_finished() { return verify_data; } // RSA-PKCS1-v1.5-SHA256 signature for maximum compatibility with TLS 1.3 std::vector<uint8_t> netplus::ssl::_rsa_sha256_pkcs15_sign(const std::vector<uint8_t>& in){ if (!_rsa) throwSSL(NetException::Error, "TLS1.3: no RSA private key"); // Compute SHA256 hash std::vector<uint8_t> mHash = sha256_hash(in); if (mHash.size() != 32) throwSSL(NetException::Error, "SHA256 hash failed"); fprintf(stderr, "[TLS] _rsa_sha256_pkcs15_sign: input size=%zu, hash size=%zu\n", in.size(), mHash.size()); fprintf(stderr, "[TLS] SHA256 hash: "); for (int i = 0; i < 16; i++) fprintf(stderr, "%02x ", mHash[i]); fprintf(stderr, "...\n"); // PKCS#1 v1.5 signature digest info for SHA256: // DigestInfo ::= SEQUENCE { // digestAlgorithm DigestAlgorithmIdentifier, // digest Digest // } // For SHA256: OID = 2.16.840.1.101.3.4.2.1 = 60 86 48 01 65 03 04 02 01 const uint8_t sha256_prefix[] = { 0x30, 0x31, // SEQUENCE length=49 0x30, 0x0d, // SEQUENCE length=13 (algId) 0x06, 0x09, 0x60, 0x86, 0x48, // OID for SHA256 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, // NULL 0x04, 0x20 // OCTET STRING length=32 }; const size_t sha256_prefix_len = sizeof(sha256_prefix) / sizeof(sha256_prefix[0]); fprintf(stderr, "[TLS] SHA256 DigestInfo prefix (%zu bytes): ", sha256_prefix_len); for (size_t i = 0; i < sha256_prefix_len; i++) fprintf(stderr, "%02x ", sha256_prefix[i]); fprintf(stderr, "\n"); // Build DigestInfo: prefix || hash std::vector<uint8_t> tBuf; tBuf.insert(tBuf.end(), sha256_prefix, sha256_prefix + sha256_prefix_len); tBuf.insert(tBuf.end(), mHash.begin(), mHash.end()); fprintf(stderr, "[TLS] DigestInfo total size=%zu\n", tBuf.size()); // Build PKCS#1 v1.5 encoded message size_t modBytes = (_rsa.n.bitLength() + 7) / 8; fprintf(stderr, "[TLS] RSA modulus bits=%u, bytes=%zu\n", _rsa.n.bitLength(), modBytes); if (tBuf.size() > modBytes - 11) throwSSL(NetException::Error, "Message too long for RSA key"); std::vector<uint8_t> EM(modBytes, 0x00); EM[0] = 0x00; EM[1] = 0x01; // Block type (signature) // Padding bytes (0xFF) for (size_t i = 2; i < modBytes - tBuf.size() - 1; i++) EM[i] = 0xFF; EM[modBytes - tBuf.size() - 1] = 0x00; // Separator // Copy digest info std::memcpy(&EM[modBytes - tBuf.size()], tBuf.data(), tBuf.size()); fprintf(stderr, "[TLS] PKCS#1 EM encoding: [00 01 FF...FF 00 DigestInfo]\n"); fprintf(stderr, "[TLS] EM (first 20 bytes): "); for (int i = 0; i < 20 && i < (int)EM.size(); i++) fprintf(stderr, "%02x ", EM[i]); fprintf(stderr, "\n"); fprintf(stderr, "[TLS] EM (last 20 bytes): "); for (int i = (int)EM.size() - 20; i < (int)EM.size(); i++) if (i >= 0) fprintf(stderr, "%02x ", EM[i]); fprintf(stderr, "\n"); // Perform raw RSA operation: signature = EM^d mod n rsa::bigInt m = rsa::bytesToBigIntBE(EM); rsa::bigInt s = rsa::modPow(m, _rsa.d, _rsa.n); std::vector<uint8_t> sig = rsa::bigIntToBytesBE(s, modBytes); fprintf(stderr, "[TLS] Final RSA signature size=%zu (should be %zu)\n", sig.size(), modBytes); return sig; } std::vector<uint8_t> netplus::ssl::_rsa_pss_sha256_sign(const std::vector<uint8_t>& in){ if (!_rsa) throwSSL(NetException::Error, "TLS1.3: no RSA private key"); Loading