diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,13 @@ TYPE OPTIONAL ) +find_package(Qca-qt5 2.1.0 REQUIRED) +set_package_properties(Qca-qt5 PROPERTIES + DESCRIPTION "Support for encryption" + URL "http://download.kde.org/stable/qca-qt5/" + TYPE REQUIRED +) + if(NOT APPLE) find_package(X11) set_package_properties(X11 PROPERTIES TYPE OPTIONAL) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -127,6 +127,7 @@ KF5::DBusAddons KF5::GlobalAccel KF5::NewStuff + qca-qt5 ) if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") diff --git a/src/History.h b/src/History.h --- a/src/History.h +++ b/src/History.h @@ -28,6 +28,10 @@ #include #include #include +#include + +// QCA +#include #include "konsoleprivate_export.h" @@ -73,6 +77,29 @@ static const int MAP_THRESHOLD = -1000; }; +/* + An secure version of HistoryFile that uses an ephemeral key to encrypt the + data before writing to the underlining tmpfile. +*/ +class SecureHistoryFile : public HistoryFile +{ +public: + SecureHistoryFile(); + void add(const char *buffer, qint64 count) Q_DECL_OVERRIDE; + void get(char *buffer, qint64 size, qint64 loc) Q_DECL_OVERRIDE; + +private: + QCA::MemoryRegion decrypt_block(QByteArray &buf, qint64 block_idx); + + QCA::SecureArray _iv_ref; + qint64_be *_iv_counter; + QCA::SymmetricKey _key; + QCA::Cipher _rcipher; + QCA::Cipher _wcipher; + QByteArray _rbuf; +}; + + ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// @@ -140,8 +167,8 @@ private: qint64 startOfLine(int lineno); - HistoryFile _index; // lines Row(qint64) - HistoryFile _cells; // text Row(Character) + SecureHistoryFile _index; // lines Row(qint64) + SecureHistoryFile _cells; // text Row(Character) HistoryFile _lineflags; // flags Row(unsigned char) }; diff --git a/src/History.cpp b/src/History.cpp --- a/src/History.cpp +++ b/src/History.cpp @@ -227,6 +227,54 @@ return _length; } + + +// Secure History File ////////////////////////////////////// +SecureHistoryFile::SecureHistoryFile() : + _rcipher(QString::fromUtf8("aes128"), QCA::Cipher::CTR), + _wcipher(QString::fromUtf8("aes128"), QCA::Cipher::CTR) +{ + _iv_ref = QCA::Random::randomArray(8) + QCA::SecureArray(8, 0); + _iv_counter = (((qint64_be*) _iv_ref.data()) + 1); + _key = QCA::SymmetricKey(16); + _rbuf.reserve(0x400); + + _wcipher.setup(QCA::Encode, _key, _iv_ref); + _rcipher.setup(QCA::Decode, _key, _iv_ref); +} + +void SecureHistoryFile::add(const char *buffer, qint64 count) { + auto encrypted = _wcipher.update(QByteArray(buffer, count)); + Q_ASSERT(_wcipher.ok()); + + HistoryFile::add(encrypted.constData(), count); +} + +void SecureHistoryFile::get(char *buffer, qint64 size, qint64 loc) { + qint64 base = loc & ~0x0F; + qint64 off = loc & 0x0F; + + _rbuf.resize(size + off); + HistoryFile::get(_rbuf.data(), size + off, base); + + auto decrypted = decrypt_block(_rbuf, loc >> 4); + memcpy(buffer, decrypted.constData() + off, size); +} + + +QCA::MemoryRegion SecureHistoryFile::decrypt_block(QByteArray &buf, qint64 block_idx) { + // Set IV counter to the current block index and setup the read cipher + *_iv_counter = block_idx; + _rcipher.setup(QCA::Decode, _key, _iv_ref); + + auto decrypted = _rcipher.update(buf); + Q_ASSERT(_rcipher.ok()); + + return decrypted; +} + + + // History Scroll abstract base class ////////////////////////////////////// HistoryScroll::HistoryScroll(HistoryType *t) : diff --git a/src/main.cpp b/src/main.cpp --- a/src/main.cpp +++ b/src/main.cpp @@ -37,6 +37,9 @@ #include #include +// QCA +#include + using Konsole::Application; // fill the KAboutData structure with information about contributors to Konsole. @@ -84,6 +87,9 @@ qputenv("QT_NO_GLIB", "1"); #endif + // Initialize QCA to enable SecureHistoryFile support + QCA::Initializer init; + auto app = new QApplication(argc, argv); #if defined(Q_OS_LINUX) && (QT_VERSION < QT_VERSION_CHECK(5, 11, 2))