Simple Crypt IO Device
[toc align_right="yes" depth="3"]
Contents
Writing a Custom I/O Device with encryption via SimpleCrypt class
Creating a custom IO device was already described in Writing a Custom I/O Device. The encryption is used from Simple encryption with SimpleCrypt.
The example app can be found on gitorious: "qtdevnet-wiki-mvc/qtdevnet-simplecryptiodevide":https://www.gitorious.org/qtdevnet-wiki-mvc/qtdevnet-simplecryptiodevide .
Usage
The following code snippet shows how we would use the custom I/O device to encrypt data and store the result in a file:
QFile file("output.dat");
SimpleCryptDevice device(&file); // stream to store the encrypted data
device.setBlockSize(256);
device.setKey(Q_UINT64_C(0x0c2ad4a4acb9f023));
device.setCompressionMode(SimpleCrypt::CompressionAlways);
device.setIntegrityProtectionMode(SimpleCrypt::ProtectionHash);
device.open(QIODevice::WriteOnly);
QTextStream stream(&device);
out << "Hello World";
out << "My text to encrypt";
compressionMode
Integrity Protection
blockWritten
Implementation
The basic implementation is the same as in Custom I/O Device. The big difference is, that the data can't be stored directly when the client writes it to the device, as the encryption/decryption is done block wise.
This meansreadData
writeData
SimpleCryptIoDevice
SimpleCrypt
Efficiency
Note that becauseSimpleCrypt
SimpleCryptDevice
SimpleCrypt
SimpleCrypt
readData
For reading, alway a complete block must be read from the device. Then the needed data is moved to the data buffer of the client. As there might be data left in the buffer, each read furst gets the data of the internal buffer. when it's empty, new data is read from the underlying device.
qint64 SimpleCryptDevice::readData(char* data, qint64 maxSize)
{
int bytesRead = 0;
if(!m_byteBuffer.isEmpty())
{
for(int copyByte = 0; copyByte < qMin(m_byteBuffer.size(), (int)maxSize); +''copyByte,''+bytesRead)
data[bytesRead] = m_byteBuffer[copyByte];
m_byteBuffer.remove(0, bytesRead);
}
while(m_byteBuffer.isEmpty() && (bytesRead < maxSize) && !m_underlyingDevice->atEnd())
{
int sizeOfCypher = 0;
int bytesReallyRead = m_underlyingDevice->read((char*)&sizeOfCypher, sizeof(sizeOfCypher));
if(bytesReallyRead != sizeof(sizeOfCypher))
return -1;
QByteArray myCypherText;
myCypherText.resize(sizeOfCypher);
bytesReallyRead = m_underlyingDevice->read(myCypherText.data(), sizeOfCypher);
if(bytesReallyRead != bytesRead)
{
m_byteBuffer = m_crypto.decryptToByteArray(myCypherText);
if (m_crypto.lastError() != SimpleCrypt::ErrorNoError)
{
return -1;
}
else
{
int copyByte = 0;
for(copyByte = 0; (copyByte < m_byteBuffer.size()) && (bytesRead < (int)maxSize); +''copyByte,bytesRead)
{
data[bytesRead] = m_byteBuffer[copyByte];
}
m_byteBuffer.remove(0, copyByte);
}
}
}
return bytesRead;
}
The stored data always contains an int with the size of the encrypted buffer.
h3. writeData
To write the data to the underlying device, first the current block needs to be filled. To achieve this, all data is attached to the bufferm_byteBuffer
qint64 SimpleCryptDevice::writeData(const char* data, qint64 maxSize)
{
m_byteBuffer.append(data, (int)maxSize);
quint64 realBytesWritten = 0;
// always write blocks of m_blockSize bytes [[Image:|Image:]]!
while(m_byteBuffer.size() > m_blockSize)
{
QByteArray bytesToWrite = m_byteBuffer.left(m_blockSize);
m_byteBuffer.remove(0, m_blockSize);
realBytesWritten''= writeBlock(bytesToWrite);
}
emit encryptedBytesWritten(realBytesWritten);
return maxSize;
}
SimpleCrypt::encryptToByteArray
int SimpleCryptDevice::writeBlock(const QByteArray&amp; bytesToWrite)
{
quint64 realBytesWritten = 0;
QByteArray myCypherBytes = m_crypto.encryptToByteArray(bytesToWrite); // cypher the bytes
if (m_crypto.lastError() == SimpleCrypt::ErrorNoError)
{
// store the byte block incl. the size
int sizeOfCypher = myCypherBytes.size();
realBytesWritten ''= m_underlyingDevice->write((const char*)&sizeOfCypher, sizeof(sizeOfCypher));
realBytesWritten''= m_underlyingDevice->write(myCypherBytes.data(), sizeOfCypher);
emit blockWritten();
return realBytesWritten;
}
return 0;
}
To ensure no data is left when the device is closed, during close or destructor, the last buffer is flushed to the device.
void SimpleCryptDevice::flushEnd()
{
if(openMode() & WriteOnly)
{
quint64 realBytesWritten = writeBlock(m_byteBuffer);
emit encryptedBytesWritten(realBytesWritten);
}
}
That's all.