kwalletd: extend Blowfish source to handle SHA512 keys

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2021-03-29 18:14:42 +03:00
parent 91780aff84
commit 3865870b97
7 changed files with 26 additions and 142 deletions

View file

@ -14,14 +14,6 @@ set_package_properties(ZLIB PROPERTIES
TYPE REQUIRED
)
find_package(LibGcrypt 1.5.0)
set_package_properties(LibGcrypt PROPERTIES
DESCRIPTION "general purpose cryptographic library based on the code from GnuPG"
URL "https://www.gnu.org/software/libgcrypt/"
PURPOSE "kwalletd needs libgcrypt to perform PBKDF2-SHA512 hashing"
TYPE REQUIRED
)
find_package(JPEG)
set_package_properties(JPEG PROPERTIES
DESCRIPTION "Accelerated JPEG image codec"

View file

@ -18,7 +18,7 @@ set(kwalletbackend_LIB_SRCS
add_library(kwalletbackend SHARED ${kwalletbackend_LIB_SRCS})
target_link_libraries(kwalletbackend ${KDE4_KDEUI_LIBS} ${LIBGCRYPT_LIBRARIES})
target_link_libraries(kwalletbackend ${KDE4_KDEUI_LIBS})
set_target_properties(kwalletbackend PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
install(TARGETS kwalletbackend ${INSTALL_TARGETS_DEFAULT_ARGS})

View file

@ -31,7 +31,6 @@
#include "blowfish.h"
#include "cbc.h"
#include <gcrypt.h>
#include <assert.h>
#define KWALLET_CIPHER_BLOWFISH_ECB 0 // this was the old KWALLET_CIPHER_BLOWFISH_CBC
@ -42,6 +41,7 @@
#define KWALLET_HASH_SHA1 0 // fallback
#define KWALLET_HASH_MD5 1 // unsupported
#define KWALLET_HASH_PBKDF2_SHA512 2 // used since 4.13 version
#define KWALLET_HASH_SHA512 3 // used since 4.13 version
namespace KWallet {
@ -67,8 +67,7 @@ BackendPersistHandler *BackendPersistHandler::getPersistHandler(BackendCipherTyp
BackendPersistHandler *BackendPersistHandler::getPersistHandler(char magicBuf[KWMAGIC_LEN])
{
if (magicBuf[2] == KWALLET_CIPHER_BLOWFISH_CBC &&
(magicBuf[3] == KWALLET_HASH_SHA1 || magicBuf[3] == KWALLET_HASH_PBKDF2_SHA512)) {
if (magicBuf[2] == KWALLET_CIPHER_BLOWFISH_CBC && magicBuf[3] == KWALLET_HASH_SHA512) {
if (blowfishHandler == 0) {
blowfishHandler = new BlowfishPersistHandler();
}
@ -82,11 +81,7 @@ int BlowfishPersistHandler::write(Backend* wb, KSaveFile& sf, QByteArray& versio
assert(wb->_cipherType == BACKEND_CIPHER_BLOWFISH);
version[2] = KWALLET_CIPHER_BLOWFISH_CBC;
if(!wb->_useNewHash) {
version[3] = KWALLET_HASH_SHA1;
} else {
version[3] = KWALLET_HASH_PBKDF2_SHA512; // Since 4.13 we always use PBKDF2_SHA512
}
version[3] = KWALLET_HASH_SHA512; // Since 4.20 we always use SHA512
if (sf.write(version, 4) != 4) {
sf.abort();
@ -153,9 +148,12 @@ int BlowfishPersistHandler::write(Backend* wb, KSaveFile& sf, QByteArray& versio
wholeFile.resize(newsize);
const int randBlockSize = blksz+delta;
char *randomData = (char*) gcry_random_bytes(randBlockSize, GCRY_STRONG_RANDOM);
char randomData[randBlockSize];
::memset(randomData, 0, randBlockSize * sizeof(char));
for (int i = 0; i < randBlockSize; i++) {
randomData[i] = char(qrand() % (sizeof(char) * sizeof(char)));
}
QByteArray randBlock(randomData, randBlockSize);
::free(randomData);
for (int i = 0; i < blksz; i++) {
wholeFile[i] = randBlock[i];

View file

@ -107,7 +107,7 @@ BlowFish::~BlowFish() {
int BlowFish::keyLen() const {
return 448;
return 512;
}
@ -117,7 +117,7 @@ bool BlowFish::readyToGo() const {
bool BlowFish::setKey(void *key, int bitlength) {
if (bitlength <= 0 || bitlength > 448 || bitlength % 8 != 0) {
if (bitlength <= 0 || bitlength > 512 || bitlength % 8 != 0) {
return false;
}

View file

@ -37,13 +37,10 @@
#include "blowfish.h"
#include "cbc.h"
#include <gcrypt.h>
#include <assert.h>
// quick fix to get random numbers on win32
#define KWALLET_VERSION_MAJOR 0
#define KWALLET_VERSION_MINOR 1
#define KWALLET_VERSION_MAJOR 0
#define KWALLET_VERSION_MINOR 2
using namespace KWallet;
@ -61,7 +58,6 @@ static void initKWalletDir()
Backend::Backend(const QString& name, bool isPath)
: d(0)
, _name(name)
, _useNewHash(false)
, _ref(0)
, _cipherType(KWallet::BACKEND_CIPHER_UNKNOWN)
{
@ -90,34 +86,6 @@ void Backend::setCipherType(BackendCipherType ct)
_cipherType = ct;
}
static int password2PBKDF2_SHA512(const QByteArray &password, QByteArray& hash, const QByteArray &salt)
{
if (!gcry_check_version("1.5.0")) {
printf("libcrypt version is too old \n");
return GPG_ERR_USER_2;
}
gcry_error_t error;
bool static gcry_secmem_init = false;
if (!gcry_secmem_init) {
error = gcry_control(GCRYCTL_INIT_SECMEM, 32768, 0);
if (error != 0) {
kWarning() << "Can't get secure memory:" << error;
return error;
}
gcry_secmem_init = true;
}
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
error = gcry_kdf_derive(password.constData(), password.size(),
GCRY_KDF_PBKDF2, GCRY_MD_SHA512,
salt.data(), salt.size(),
PBKDF2_SHA512_ITERATIONS, PBKDF2_SHA512_KEYSIZE, hash.data());
return error;
}
int Backend::deref() {
if (--_ref < 0) {
kDebug() << "refCount negative!";
@ -179,14 +147,11 @@ int Backend::openPreHashed(const QByteArray &passwordHash)
}
// check the password hash for correct size (currently fixed)
if (passwordHash.size() != 20 && passwordHash.size() != 40 &&
passwordHash.size() != 56) {
if (passwordHash.size() != 20 && passwordHash.size() != 128) {
return -42; // unsupported encryption scheme
}
_passhash = passwordHash;
_newPassHash = passwordHash;
_useNewHash = true;//Only new hash is supported
return openInternal();
}
@ -227,10 +192,10 @@ int Backend::openInternal(WId w)
return -4; // unknown version
}
// 0 has been the MINOR version until 4.13, from that point we use it to upgrade the hash
if (magicBuf[1] == 1) {
// 0 has been the MINOR version until 4.13
// 1 has been the MINOR version until 4.20
if (magicBuf[1] == 2) {
kDebug() << "Wallet new enough, using new hash";
swapToNewHash();
} else if (magicBuf[1] != 0){
kDebug() << "Wallet is old, sad panda :(";
return -4; // unknown version
@ -243,40 +208,6 @@ int Backend::openInternal(WId w)
return phandler->read(this, db, w);
}
void Backend::swapToNewHash()
{
//Runtime error happened and we can't use the new hash
if (!_useNewHash) {
kDebug() << "Runtime error on the new hash";
return;
}
_passhash.fill(0); // Making sure the old passhash is not around in memory
_passhash = _newPassHash; // Use the new hash, means the wallet is modern enough
}
QByteArray Backend::createAndSaveSalt(const QString& path) const
{
QFile saltFile(path);
saltFile.remove();
if (!saltFile.open(QIODevice::WriteOnly)) {
return QByteArray();
}
char *randomData = (char*) gcry_random_bytes(PBKDF2_SHA512_SALTSIZE, GCRY_STRONG_RANDOM);
QByteArray salt(randomData, PBKDF2_SHA512_SALTSIZE);
::free(randomData);
if (saltFile.write(salt) != PBKDF2_SHA512_SALTSIZE) {
kWarning() << "Could not save salt to" << path;
return QByteArray();
}
saltFile.close();
return salt;
}
int Backend::sync(WId w) {
if (!_open) {
return -255; // not open yet
@ -297,13 +228,7 @@ int Backend::sync(WId w) {
// Write the version number
QByteArray version(4, 0);
version[0] = KWALLET_VERSION_MAJOR;
if (_useNewHash) {
version[1] = KWALLET_VERSION_MINOR;
// Use the sync to update the hash to PBKDF2_SHA512
swapToNewHash();
} else {
version[1] = 0; //was KWALLET_VERSION_MINOR before the new hash
}
version[1] = KWALLET_VERSION_MINOR;
BackendPersistHandler *phandler = BackendPersistHandler::getPersistHandler(_cipherType);
if (0 == phandler) {
@ -340,7 +265,6 @@ int Backend::close(bool save) {
// empty the password hash
_passhash.fill(0);
_newPassHash.fill(0);
_open = false;
@ -542,29 +466,5 @@ bool Backend::entryDoesNotExist(const QString& folder, const QString& entry) con
}
void Backend::setPassword(const QByteArray &password) {
_passhash.fill(0); // empty just in case
BlowFish _bf;
CipherBlockChain bf(&_bf);
_newPassHash.resize(bf.keyLen()/8);
_newPassHash.fill(0);
// this should be SHA-512 for release probably
_passhash = QCryptographicHash::hash(password, QCryptographicHash::Sha1);
QByteArray salt;
QFile saltFile(KGlobal::dirs()->saveLocation("kwallet") + _name + ".salt");
if (!saltFile.exists() || saltFile.size() == 0) {
salt = createAndSaveSalt(saltFile.fileName());
} else {
if (!saltFile.open(QIODevice::ReadOnly)) {
salt = createAndSaveSalt(saltFile.fileName());
} else {
salt = saltFile.readAll();
}
}
if (!salt.isEmpty() && password2PBKDF2_SHA512(password, _newPassHash, salt) == 0) {
kDebug() << "Setting useNewHash to true";
_useNewHash = true;
}
_passhash = QCryptographicHash::hash(password, QCryptographicHash::Sha512);
}

View file

@ -28,10 +28,6 @@
#include "kwalletentry.h"
#include "backendpersisthandler.h"
#define PBKDF2_SHA512_KEYSIZE 56
#define PBKDF2_SHA512_SALTSIZE 56
#define PBKDF2_SHA512_ITERATIONS 50000
namespace KWallet {
/**
@ -160,7 +156,6 @@ private:
QString _name;
QString _path;
bool _open;
bool _useNewHash;
QString _folder;
int _ref;
// Map Folder->Entries
@ -170,7 +165,6 @@ private:
typedef QMap<MD5Digest, QList<MD5Digest> > HashMap;
HashMap _hashes;
QByteArray _passhash; // password hash used for saving the wallet
QByteArray _newPassHash; //Modern hash using KWALLET_HASH_PBKDF2_SHA512
BackendCipherType _cipherType; // the kind of encryption used for this wallet
friend class BlowfishPersistHandler;
@ -178,8 +172,6 @@ private:
// open the wallet with the password already set. This is
// called internally by both open and openPreHashed.
int openInternal(WId w=0);
void swapToNewHash();
QByteArray createAndSaveSalt(const QString &path) const;
};

View file

@ -35,6 +35,8 @@
#include "backend/kwalletbackend.h" //For the hash size
#define BSIZE 1000
#define KWALLET_SHA512_KEYSIZE 128
static int pipefd = 0;
static int socketfd = 0;
static bool isWalletEnabled()
@ -51,10 +53,10 @@ static char *waitForHash()
int totalRead = 0;
int readBytes = 0;
int attemps = 0;
char *buf = (char*)malloc(sizeof(char) * PBKDF2_SHA512_KEYSIZE);
memset(buf, '\0', PBKDF2_SHA512_KEYSIZE);
while(totalRead != PBKDF2_SHA512_KEYSIZE) {
readBytes = read(pipefd, buf + totalRead, PBKDF2_SHA512_KEYSIZE - totalRead);
char *buf = (char*)malloc(sizeof(char) * KWALLET_SHA512_KEYSIZE);
memset(buf, '\0', KWALLET_SHA512_KEYSIZE);
while(totalRead != KWALLET_SHA512_KEYSIZE) {
readBytes = read(pipefd, buf + totalRead, KWALLET_SHA512_KEYSIZE - totalRead);
if (readBytes == -1 || attemps > 5) {
free(buf);
return NULL;
@ -180,7 +182,7 @@ int main(int argc, char **argv)
KWalletD walletd;
if (hash) {
kDebug() << "LOGIN INSIDE!";
QByteArray passHash(hash, PBKDF2_SHA512_KEYSIZE);
QByteArray passHash(hash, KWALLET_SHA512_KEYSIZE);
int wallet = walletd.pamOpen(KWallet::Wallet::LocalWallet(), passHash, 0);
kDebug() << "Wallet handler: " << wallet;
free(hash);