Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Qt + CryptoPP : Asymmetric encryption Problem: the string is either too long or too short for the public key



  • I have a Problem with CryptoPP. I generated a Private Key of size 4096 Bit = 512 Bytes.

    Now when I send over TCP I encrypt the Message everything under 512 will be filled with 0. If the message is bigger than 512 Bytes it will be splitted and sent to chunks.

    I fill up because if it is below 512 the Receiver throws an Exception that the text does not match with the key. I tried with 480 Bytes Chunks

    But if I send a message file over 6MB in 512 Bytes it will throw a Message that 512 Bytes exceeds the the key size

    I tried to send in 510 Bytes. In this case both sides threw an error that 510 exceeds the size of public key and the over said 510 does not match with key
    Anyone sees a solution or a Asymmetric Encryption giving you the choice to encrypt smaller messages than the FIXED Messages?

    buffer2k48 is 512. The name was because it was a 2048 bit key before.

    Encryption: (Server Generates Key)

    void KSecureDesktopQT::generateRSAKeys()
    {
        this->privateKey = std::make_unique<CryptoPP::InvertibleRSAFunction>();
        this->privateKey->GenerateRandomWithKeySize(rng, 4096);
        this->publicKey = std::make_unique<CryptoPP::RSAFunction>(*this->privateKey);
    }
    

    If the Client wants to send a File he starts making a request over DTLS

    void KChatDialog::sendfile()
    {
    	//this->generateKeyPair();
    	this->temp_file_path_for_request = QFileDialog::getOpenFileName(nullptr, "Select Files", QString(), "*.*");
    	if (this->temp_file_path_for_request.isEmpty())
    		return;
    	QFile file(this->temp_file_path_for_request);
    	nlohmann::json request;
    	request["action"] = static_cast<uint16_t>(ActionTypes::FileSendRequest);
    	request["from"] = this->user;
    	request["cookie"] = this->cookie;
    	request["path"] = this->temp_file_path_for_request.toStdU16String();
    	request["size"] = file.size();
    	auto r = request.dump();
    	this->s->writeDtlsDatagram(r.c_str());
    	//this->sendFilesFoo(this->targets, str.toStdU16String());
    }
    

    The Server receives the Request, the User can choose to deny the file

    void KSecureDesktopQT::receiveFiles(const QHostAddress& address, uint16_t port, const nlohmann::json& data) {
        auto from = QString::fromStdString(data["from"].get<std::string>());
        auto size = data["size"].get<uint64_t>();
        auto pvec = data["path"];
        std::u16string str = u"";
        for (nlohmann::json::iterator iter = pvec.begin(); iter != pvec.end(); ++iter) {
            char16_t c = iter.value().get<uint16_t>();
            str += c;                                 
        }
        auto caption = QString::fromStdU32String(this->language["MainForm"]["FileReceiveRequest"]["caption"]);
        auto sfrom = QString::fromStdU32String(this->language["MainForm"]["FileReceiveRequest"]["from"]);
        auto spath = QString::fromStdU32String(this->language["MainForm"]["FileReceiveRequest"]["path"]);
        auto ssize = QString::fromStdU32String(this->language["MainForm"]["FileReceiveRequest"]["size"]);
        auto body = sfrom + ':' + from + '\n' + ssize + ':' + QString::number(size) + '\n' + spath + ':' + QString::fromStdU16String(str);
        //auto res = QMessageBox::question(nullptr, caption, body, QMessageBox::StandardButton::Yes, QMessageBox::StandardButton::No);
        std::promise<bool> transfer;
        auto fut = transfer.get_future();
            emit this->askForReceiveFileAllowance(caption, body,(transfer));
            fut.wait();
        if (fut.get() == false) {
            this->denyFileRequest(address, port, from.toStdString(), this->user);
            return;
        }
        std::promise<QString> target;
        auto futuris = target.get_future();
        emit this->saveFileSignal(QString("Receiving"), target);
        futuris.wait();
        auto path_str = futuris.get();
        //auto targetpath = QFileDialog::getSaveFileName(nullptr, "Receiving", QString(), QString("*.*"));
        if (path_str.isEmpty()) {
            this->denyFileRequest(address, port, from.toStdString(), this->user);
            return;
        }
        
        this->acceptFileRequest(address,port,from.toStdString(),this->user, path_str,str,size);
    }
    

    The Filepath is U16String since Windows is using wchar and with Unicode wchar_t (windows) is char16_t.

    If the server accepts it goes to this Function

    void KSecureDesktopQT::acceptFileRequest(const QHostAddress& address, uint16_t port, std::string& source, std::string& receiver, const QString& target, const std::u16string& original, uint64_t filesize)
    {
        nlohmann::json response;
        response["action"] = static_cast<uint16_t>(ActionTypes::FileRequestAccept);
        response["source"] = source;
        response["from"] = receiver;
        response["path"] = original;
        response["port"] = (*this->tbl)["communication"]["chat"].value_or<uint16_t>(899);
        std::string strPubKey;
        CryptoPP::Base64Encoder pubkey(new CryptoPP::StringSink(strPubKey));
        this->publicKey->DEREncode(pubkey);
        pubkey.MessageEnd();
        response["key"] = strPubKey;
        FileInformation fi;
        fi.from = QString::fromStdString(source);
        fi.size = filesize;
        fi.source = QString::fromStdU16String(original);
        fi.target = target;
        std::promise<KTransferProgress*> prom;
        auto cont = prom.get_future();
        emit this->openTransferDialog(fi, prom);
        cont.wait();
        KTransferProgress* dlg_ptr = cont.get();
        auto str = response.dump();
        this->server->sendEncryptedDatagram(address, port, QString::fromStdString(str));
        //this->chatSocket.writeDatagram(str.c_str(), str.size(), address, port);
        //sockpp::tcp_acceptor acc((*this->tbl)["communication"]["chat"].value_or<uint16_t>(899));
    
        
    
        sockpp::tcp_acceptor acc((*this->tbl)["communication"]["chat"]["port"].value_or<uint16_t>(899));
        sockpp::inet_address peer;
        auto sock = acc.accept(&peer);
        sock.read_timeout(std::chrono::seconds(30));
        std::vector<uint8_t> bytes;
        char* buffer = new char[buffer2k48];
        int64_t len = 0;
        CryptoPP::RSAES_PKCS1v15_Decryptor dec(*this->privateKey);
        try {
            do {
                memset(buffer, 0, buffer2k48);
                len = sock.read(buffer, buffer2k48);
                std::string encrypted(buffer, len);
                std::string plain;
                CryptoPP::StringSource ss1(encrypted, true, new CryptoPP::PK_DecryptorFilter(this->rng, dec, new CryptoPP::StringSink(plain)));
                if (len > 0)
                    bytes.insert(bytes.end(), plain.begin(), plain.end());
                dlg_ptr->update(len);
            } while (len > 0);
        }
        catch (const CryptoPP::Exception& ex) {
            std::string e = ex.what();
            int i = 0;
        }
        //std::string encrypted(bytes.begin(), bytes.end());
        
        QSaveFile file(target);
        file.open(QIODevice::WriteOnly);
        file.write(reinterpret_cast<char*>(&bytes.at(0)),bytes.size());
        if (file.commit()) {
            emit this->informationSignal(1, "Transfer Completed", "Transfer successfully completed");
            //QMessageBox::information(nullptr, "Transfer completed", "Transfer successfully completed");
            return;
        }
        emit this->informationSignal(2, "Transfer failed", "transfer failed");
        //QMessageBox::warning(nullptr, "Transfer incomplete", "Transfer failed");
    }
    

    and the Client receives message that the file was accepted:

    void KChatDialog::acceptedFileRequest(const nlohmann::json& json)
    {
    	std::unique_ptr<CryptoPP::RSAFunction> pubkey = std::make_unique<CryptoPP::RSAFunction>();
    	std::string spubkey = json["key"].get<std::string>();
    	CryptoPP::StringSource src(spubkey, true, new CryptoPP::Base64Decoder);
    	CryptoPP::ByteQueue bytes;
    	src.TransferTo(bytes);
    	pubkey->Load(bytes);
    	QFile file(this->temp_file_path_for_request);
    	file.open(QIODevice::ReadOnly);
    	auto fdata = file.readAll();
    	std::string raw(fdata.begin(), fdata.end());
    	//std::string encrypted;
    	CryptoPP::RSAES_PKCS1v15_Encryptor enc(*pubkey);
    	//
    	auto addr = this->s->peerAddress();
    	auto ip = quint32IPv4ToString(addr.toIPv4Address());
    	auto port = json["port"].get<uint16_t>();
    	sockpp::tcp_connector conn;
    	conn.connect(sockpp::inet_address(ip.toStdString(), port));
    	const char* const data = raw.c_str();
    	uint32_t indexer = buffer2k48;
    	uint64_t alreadyWritten = 0;
    	//auto* ptr = data;
    	bool quitafter = false;
    	for (uint64_t i = 0, end = raw.size(); i < end; ) {
    		uint64_t diff = raw.size() - alreadyWritten;
    		auto plain = [&](){
    			if (diff > buffer2k48)
    				return raw.substr(alreadyWritten, buffer2k48);
    			return raw.substr(i, diff);
    		}();
    		std::string encrypted;
    		
    		if ((end - alreadyWritten) < buffer2k48) {
    			fillToLimit(plain);
    			quitafter = true;
    		}
    			//indexer = end - alreadyWritten;
    		CryptoPP::StringSource ss1(plain.c_str(), true, new CryptoPP::PK_EncryptorFilter(this->rng, enc, new CryptoPP::StringSink(encrypted)));
    		auto len = conn.write(encrypted);
    		if (len < 1 || quitafter == true)
    		{
    			//ERROR
    			break;
    		}
    		alreadyWritten += len;
    		//ptr += len;
    		i += len;
    	}
    	file.close();
    	conn.close();
    }
    


  • Hi, don't see the source code for function fillToLimit()?
    Also, instead of sending the file through port 899, could you simplify your example, for example use a CryptoPP::FileSink instead to transfer a file?



  • @hskoglund

    void fillToLimit(std::string& str, uint32_t futureSize = buffer2k48) {
    	
    	for (uint32_t i = 0, end = futureSize - str.size(); i < end; ++i) {
    		str += char(0);
    	}
    	
    }
    

    it fills the string with 0s.

    you can send a file through network with FileSink?
    I am not sending through tcp just for encrypting a file. Since the receiver is decrypting it again. I just want to encrypted data transfer with Public / Privatekey.



  • OK I will try to encrypt the File with AES and encrypt the key with RSA and send the encrypted AES Key to client.



  • Hi, basically you're trying to solve 2 problems at the same time: encrypting a file and sending the encrypted file to another computer.
    What I mean, encrypting/decrypting in the same loop that you're receiving or transmitting is not a good idea. Better to do it in separate steps: 1) encrypt to a file 2) send the encrypted file on port 899 3) receive it on the server and save it as a file 4) decrypt it. (Steps 2 and 3 can then be done without involving CryptoPP).

    Also, the fillToLimit() function is very dangerous, although std::string is perfectly happy with embedded/extra null bytes, other parts of the string functions in c++ are not, for example: c_str() will not include them and a std::string constructor ((const char* s, size_t n) will not include them, for example in your KSecureDesktopQT::acceptFileRequest() function this constructor will disregard the efforts of fillToLimit():

    ...
    std::string encrypted(buffer, len);
    ...
    

    Consider changing fillToLimit() to add extra spaces instead of null bytes :-)


Log in to reply