Commit 137c9224 authored by jan.koester's avatar jan.koester
Browse files

test

parent aa4db7ed
Loading
Loading
Loading
Loading
+2 −11
Original line number Diff line number Diff line
@@ -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);

@@ -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;
}

+82 −10
Original line number Diff line number Diff line
@@ -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 {
@@ -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
@@ -433,6 +504,7 @@ namespace netplus {
                        owner->SendData.clear();
                        owner->SendOff = 0;
                    }
                }

                owner->WritePending.store(false);

+0 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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);
+97 −222
Original line number Diff line number Diff line
@@ -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);
@@ -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");
@@ -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)
@@ -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;
        }
@@ -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");

@@ -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