/* * Copyright (C) 2004 Justin Karneges * Copyright (C) 2004-2006 Brad Hards * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * */ #include #include #include #include #include #include namespace gcryptQCAPlugin { #include "hkdf.c" #include "pkcs5.c" void check_error(const char *label, gcry_error_t err) { // we ignore the case where it is not an error, and // we also don't flag weak keys. if ((GPG_ERR_NO_ERROR != err) && (GPG_ERR_WEAK_KEY != gpg_err_code(err))) { std::cout << "Failure (" << label << "): "; std::cout << gcry_strsource(err) << "/"; std::cout << gcry_strerror(err) << std::endl; } } class gcryHashContext : public QCA::HashContext { Q_OBJECT public: gcryHashContext(int hashAlgorithm, QCA::Provider *p, const QString &type) : QCA::HashContext(p, type) { m_hashAlgorithm = hashAlgorithm; err = gcry_md_open(&context, m_hashAlgorithm, 0); if (GPG_ERR_NO_ERROR != err) { std::cout << "Failure: "; std::cout << gcry_strsource(err) << "/"; std::cout << gcry_strerror(err) << std::endl; } } ~gcryHashContext() override { gcry_md_close(context); } Context *clone() const override { return new gcryHashContext(m_hashAlgorithm, provider(), type()); } void clear() override { gcry_md_reset(context); } void update(const QCA::MemoryRegion &a) override { gcry_md_write(context, a.data(), a.size()); } QCA::MemoryRegion final() override { unsigned char *md; QCA::SecureArray a(gcry_md_get_algo_dlen(m_hashAlgorithm)); md = gcry_md_read(context, m_hashAlgorithm); memcpy(a.data(), md, a.size()); return a; } protected: gcry_md_hd_t context; gcry_error_t err; int m_hashAlgorithm; }; class gcryHMACContext : public QCA::MACContext { Q_OBJECT public: gcryHMACContext(int hashAlgorithm, QCA::Provider *p, const QString &type) : QCA::MACContext(p, type) { m_hashAlgorithm = hashAlgorithm; err = gcry_md_open(&context, m_hashAlgorithm, GCRY_MD_FLAG_HMAC); if (GPG_ERR_NO_ERROR != err) { std::cout << "Failure: "; std::cout << gcry_strsource(err) << "/"; std::cout << gcry_strerror(err) << std::endl; } } ~gcryHMACContext() override { gcry_md_close(context); } void setup(const QCA::SymmetricKey &key) override { gcry_md_setkey(context, key.data(), key.size()); } Context *clone() const override { return new gcryHMACContext(m_hashAlgorithm, provider(), type()); } void clear() { gcry_md_reset(context); } QCA::KeyLength keyLength() const override { return anyKeyLength(); } void update(const QCA::MemoryRegion &a) override { gcry_md_write(context, a.data(), a.size()); } void final(QCA::MemoryRegion *out) override { QCA::SecureArray sa(gcry_md_get_algo_dlen(m_hashAlgorithm), 0); unsigned char *md; md = gcry_md_read(context, m_hashAlgorithm); memcpy(sa.data(), md, sa.size()); *out = sa; } protected: gcry_md_hd_t context; gcry_error_t err; int m_hashAlgorithm; }; class gcryCipherContext : public QCA::CipherContext { Q_OBJECT public: gcryCipherContext(int algorithm, int mode, bool pad, QCA::Provider *p, const QString &type) : QCA::CipherContext(p, type) { m_cryptoAlgorithm = algorithm; m_mode = mode; m_pad = pad; } void setup(QCA::Direction dir, const QCA::SymmetricKey &key, const QCA::InitializationVector &iv, const QCA::AuthTag &tag) override { Q_UNUSED(tag); m_direction = dir; err = gcry_cipher_open(&context, m_cryptoAlgorithm, m_mode, 0); check_error("gcry_cipher_open", err); if ((GCRY_CIPHER_3DES == m_cryptoAlgorithm) && (key.size() == 16)) { // this is triple DES with two keys, and gcrypt wants three QCA::SymmetricKey keyCopy(key); QCA::SecureArray thirdKey(key); thirdKey.resize(8); keyCopy += thirdKey; err = gcry_cipher_setkey(context, keyCopy.data(), keyCopy.size()); } else { err = gcry_cipher_setkey(context, key.data(), key.size()); } check_error("gcry_cipher_setkey", err); err = gcry_cipher_setiv(context, iv.data(), iv.size()); check_error("gcry_cipher_setiv", err); } Context *clone() const override { return new gcryCipherContext(*this); } int blockSize() const override { size_t blockSize; gcry_cipher_algo_info(m_cryptoAlgorithm, GCRYCTL_GET_BLKLEN, nullptr, &blockSize); return blockSize; } QCA::AuthTag tag() const override { // For future implementation return QCA::AuthTag(); } bool update(const QCA::SecureArray &in, QCA::SecureArray *out) override { QCA::SecureArray result(in.size()); if (QCA::Encode == m_direction) { err = gcry_cipher_encrypt( context, (unsigned char *)result.data(), result.size(), (unsigned char *)in.data(), in.size()); } else { err = gcry_cipher_decrypt( context, (unsigned char *)result.data(), result.size(), (unsigned char *)in.data(), in.size()); } check_error("update cipher encrypt/decrypt", err); result.resize(in.size()); *out = result; return true; } bool final(QCA::SecureArray *out) override { QCA::SecureArray result; if (m_pad) { result.resize(blockSize()); if (QCA::Encode == m_direction) { err = gcry_cipher_encrypt(context, (unsigned char *)result.data(), result.size(), nullptr, 0); } else { err = gcry_cipher_decrypt(context, (unsigned char *)result.data(), result.size(), nullptr, 0); } check_error("final cipher encrypt/decrypt", err); } else { // just return null } *out = result; return true; } QCA::KeyLength keyLength() const override { switch (m_cryptoAlgorithm) { case GCRY_CIPHER_DES: return QCA::KeyLength(8, 8, 1); case GCRY_CIPHER_AES128: return QCA::KeyLength(16, 16, 1); case GCRY_CIPHER_AES192: return QCA::KeyLength(24, 24, 1); case GCRY_CIPHER_3DES: // we do two and three key versions return QCA::KeyLength(16, 24, 8); case GCRY_CIPHER_AES256: return QCA::KeyLength(32, 32, 1); case GCRY_CIPHER_BLOWFISH: // Don't know - TODO return QCA::KeyLength(1, 32, 1); default: return QCA::KeyLength(0, 1, 1); } } protected: gcry_cipher_hd_t context; gcry_error_t err; int m_cryptoAlgorithm; QCA::Direction m_direction; int m_mode; bool m_pad; }; class pbkdf1Context : public QCA::KDFContext { Q_OBJECT public: pbkdf1Context(int algorithm, QCA::Provider *p, const QString &type) : QCA::KDFContext(p, type) { m_hashAlgorithm = algorithm; err = gcry_md_open(&context, m_hashAlgorithm, 0); if (GPG_ERR_NO_ERROR != err) { std::cout << "Failure: "; std::cout << gcry_strsource(err) << "/"; std::cout << gcry_strerror(err) << std::endl; } } ~pbkdf1Context() override { gcry_md_close(context); } Context *clone() const override { return new pbkdf1Context(m_hashAlgorithm, provider(), type()); } QCA::SymmetricKey makeKey(const QCA::SecureArray &secret, const QCA::InitializationVector &salt, unsigned int keyLength, unsigned int iterationCount) override { /* from RFC2898: Steps: 1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output "derived key too long" and stop. */ if (keyLength > gcry_md_get_algo_dlen(m_hashAlgorithm)) { std::cout << "derived key too long" << std::endl; return QCA::SymmetricKey(); } /* 2. Apply the underlying hash function Hash for c iterations to the concatenation of the password P and the salt S, then extract the first dkLen octets to produce a derived key DK: T_1 = Hash (P || S) , T_2 = Hash (T_1) , ... T_c = Hash (T_{c-1}) , DK = Tc<0..dkLen-1> */ // calculate T_1 gcry_md_write(context, secret.data(), secret.size()); gcry_md_write(context, salt.data(), salt.size()); unsigned char *md; md = gcry_md_read(context, m_hashAlgorithm); QCA::SecureArray a(gcry_md_get_algo_dlen(m_hashAlgorithm)); memcpy(a.data(), md, a.size()); // calculate T_2 up to T_c for (unsigned int i = 2; i <= iterationCount; ++i) { gcry_md_reset(context); gcry_md_write(context, a.data(), a.size()); md = gcry_md_read(context, m_hashAlgorithm); memcpy(a.data(), md, a.size()); } // shrink a to become DK, of the required length a.resize(keyLength); /* 3. Output the derived key DK. */ return a; } QCA::SymmetricKey makeKey(const QCA::SecureArray &secret, const QCA::InitializationVector &salt, unsigned int keyLength, int msecInterval, unsigned int *iterationCount) override { Q_ASSERT(iterationCount != nullptr); QElapsedTimer timer; /* from RFC2898: Steps: 1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output "derived key too long" and stop. */ if (keyLength > gcry_md_get_algo_dlen(m_hashAlgorithm)) { std::cout << "derived key too long" << std::endl; return QCA::SymmetricKey(); } /* 2. Apply the underlying hash function Hash for M milliseconds to the concatenation of the password P and the salt S, incrementing c, then extract the first dkLen octets to produce a derived key DK: time from 0 to M T_1 = Hash (P || S) , T_2 = Hash (T_1) , ... T_c = Hash (T_{c-1}) , when time = 0: stop, DK = Tc<0..dkLen-1> */ // calculate T_1 gcry_md_write(context, secret.data(), secret.size()); gcry_md_write(context, salt.data(), salt.size()); unsigned char *md; md = gcry_md_read(context, m_hashAlgorithm); QCA::SecureArray a(gcry_md_get_algo_dlen(m_hashAlgorithm)); memcpy(a.data(), md, a.size()); // calculate T_2 up to T_c *iterationCount = 2 - 1; // <- Have to remove 1, unless it computes one timer.start(); // ^ time more than the base function // ^ with the same iterationCount while (timer.elapsed() < msecInterval) { gcry_md_reset(context); gcry_md_write(context, a.data(), a.size()); md = gcry_md_read(context, m_hashAlgorithm); memcpy(a.data(), md, a.size()); ++(*iterationCount); } // shrink a to become DK, of the required length a.resize(keyLength); /* 3. Output the derived key DK. */ return a; } protected: gcry_md_hd_t context; gcry_error_t err; int m_hashAlgorithm; }; class pbkdf2Context : public QCA::KDFContext { Q_OBJECT public: pbkdf2Context(int algorithm, QCA::Provider *p, const QString &type) : QCA::KDFContext(p, type) { m_algorithm = algorithm; } Context *clone() const override { return new pbkdf2Context(*this); } QCA::SymmetricKey makeKey(const QCA::SecureArray &secret, const QCA::InitializationVector &salt, unsigned int keyLength, unsigned int iterationCount) override { QCA::SymmetricKey result(keyLength); gcry_error_t retval = gcry_pbkdf2(m_algorithm, secret.data(), secret.size(), salt.data(), salt.size(), iterationCount, keyLength, result.data()); if (retval == GPG_ERR_NO_ERROR) { return result; } else { // std::cout << "got: " << retval << std::endl; return QCA::SymmetricKey(); } } QCA::SymmetricKey makeKey(const QCA::SecureArray &secret, const QCA::InitializationVector &salt, unsigned int keyLength, int msecInterval, unsigned int *iterationCount) override { Q_ASSERT(iterationCount != nullptr); QCA::SymmetricKey result(keyLength); QElapsedTimer timer; *iterationCount = 0; timer.start(); while (timer.elapsed() < msecInterval) { gcry_pbkdf2( m_algorithm, secret.data(), secret.size(), salt.data(), salt.size(), 1, keyLength, result.data()); ++(*iterationCount); } return makeKey(secret, salt, keyLength, *iterationCount); } protected: int m_algorithm; }; class hkdfContext : public QCA::HKDFContext { Q_OBJECT public: hkdfContext(int algorithm, QCA::Provider *p, const QString &type) : QCA::HKDFContext(p, type) { m_algorithm = algorithm; } Context *clone() const override { return new hkdfContext(*this); } QCA::SymmetricKey makeKey(const QCA::SecureArray &secret, const QCA::InitializationVector &salt, const QCA::InitializationVector &info, unsigned int keyLength) override { QCA::SymmetricKey result(keyLength); gcry_error_t retval = gcry_hkdf(m_algorithm, secret.data(), secret.size(), salt.data(), salt.size(), info.data(), info.size(), result.data(), result.size()); if (retval == GPG_ERR_NO_ERROR) { return result; } else { return QCA::SymmetricKey(); } } protected: int m_algorithm; }; } extern "C" { static void *qca_func_malloc(size_t n) { return qca_secure_alloc(n); } static void *qca_func_secure_malloc(size_t n) { return qca_secure_alloc(n); } static void *qca_func_realloc(void *oldBlock, size_t newBlockSize) { return qca_secure_realloc(oldBlock, newBlockSize); } static void qca_func_free(void *mem) { qca_secure_free(mem); } int qca_func_secure_check(const void *) { return (int)QCA::haveSecureMemory(); } } // extern "C" class gcryptProvider : public QCA::Provider { public: void init() override { if (!gcry_control(GCRYCTL_ANY_INITIALIZATION_P)) { /* No other library has already initialized libgcrypt. */ if (!gcry_check_version(GCRYPT_VERSION)) { std::cout << "libgcrypt is too old (need " << GCRYPT_VERSION; std::cout << ", have " << gcry_check_version(nullptr) << ")" << std::endl; } gcry_set_allocation_handler( qca_func_malloc, qca_func_secure_malloc, qca_func_secure_check, qca_func_realloc, qca_func_free); gcry_control(GCRYCTL_INITIALIZATION_FINISHED); } } int qcaVersion() const override { return QCA_VERSION; } QString name() const override { return QStringLiteral("qca-gcrypt"); } QStringList features() const override { QStringList list; list += QStringLiteral("sha1"); list += QStringLiteral("md4"); list += QStringLiteral("md5"); list += QStringLiteral("ripemd160"); #ifdef GCRY_MD_SHA224 list += QStringLiteral("sha224"); #endif list += QStringLiteral("sha256"); list += QStringLiteral("sha384"); list += QStringLiteral("sha512"); list += QStringLiteral("hmac(md5)"); list += QStringLiteral("hmac(sha1)"); #ifdef GCRY_MD_SHA224 list += QStringLiteral("hmac(sha224)"); #endif list += QStringLiteral("hmac(sha256)"); if (!(nullptr == gcry_check_version("1.3.0"))) { // 1.2 and earlier have broken implementation list += QStringLiteral("hmac(sha384)"); list += QStringLiteral("hmac(sha512)"); } list += QStringLiteral("hmac(ripemd160)"); list += QStringLiteral("aes128-ecb"); list += QStringLiteral("aes128-cfb"); list += QStringLiteral("aes128-cbc"); list += QStringLiteral("aes192-ecb"); list += QStringLiteral("aes192-cfb"); list += QStringLiteral("aes192-cbc"); list += QStringLiteral("aes256-ecb"); list += QStringLiteral("aes256-cfb"); list += QStringLiteral("aes256-cbc"); list += QStringLiteral("blowfish-ecb"); list += QStringLiteral("blowfish-cbc"); list += QStringLiteral("blowfish-cfb"); list += QStringLiteral("tripledes-ecb"); // list += QStringLiteral("des-ecb"); list += QStringLiteral("des-cbc"); list += QStringLiteral("des-cfb"); if (!(nullptr == gcry_check_version("1.3.0"))) { // 1.2 branch and earlier doesn't support OFB mode list += QStringLiteral("aes128-ofb"); list += QStringLiteral("aes192-ofb"); list += QStringLiteral("aes256-ofb"); list += QStringLiteral("des-ofb"); list += QStringLiteral("tripledes-ofb"); list += QStringLiteral("blowfish-ofb"); } list += QStringLiteral("pbkdf1(sha1)"); list += QStringLiteral("pbkdf2(sha1)"); list += QStringLiteral("hkdf(sha256)"); return list; } Context *createContext(const QString &type) override { // std::cout << "type: " << qPrintable(type) << std::endl; if (type == QLatin1String("sha1")) return new gcryptQCAPlugin::gcryHashContext(GCRY_MD_SHA1, this, type); else if (type == QLatin1String("md4")) return new gcryptQCAPlugin::gcryHashContext(GCRY_MD_MD4, this, type); else if (type == QLatin1String("md5")) return new gcryptQCAPlugin::gcryHashContext(GCRY_MD_MD5, this, type); else if (type == QLatin1String("ripemd160")) return new gcryptQCAPlugin::gcryHashContext(GCRY_MD_RMD160, this, type); #ifdef GCRY_MD_SHA224 else if (type == QLatin1String("sha224")) return new gcryptQCAPlugin::gcryHashContext(GCRY_MD_SHA224, this, type); #endif else if (type == QLatin1String("sha256")) return new gcryptQCAPlugin::gcryHashContext(GCRY_MD_SHA256, this, type); else if (type == QLatin1String("sha384")) return new gcryptQCAPlugin::gcryHashContext(GCRY_MD_SHA384, this, type); else if (type == QLatin1String("sha512")) return new gcryptQCAPlugin::gcryHashContext(GCRY_MD_SHA512, this, type); else if (type == QLatin1String("hmac(md5)")) return new gcryptQCAPlugin::gcryHMACContext(GCRY_MD_MD5, this, type); else if (type == QLatin1String("hmac(sha1)")) return new gcryptQCAPlugin::gcryHMACContext(GCRY_MD_SHA1, this, type); #ifdef GCRY_MD_SHA224 else if (type == QLatin1String("hmac(sha224)")) return new gcryptQCAPlugin::gcryHMACContext(GCRY_MD_SHA224, this, type); #endif else if (type == QLatin1String("hmac(sha256)")) return new gcryptQCAPlugin::gcryHMACContext(GCRY_MD_SHA256, this, type); else if (type == QLatin1String("hmac(sha384)")) return new gcryptQCAPlugin::gcryHMACContext(GCRY_MD_SHA384, this, type); else if (type == QLatin1String("hmac(sha512)")) return new gcryptQCAPlugin::gcryHMACContext(GCRY_MD_SHA512, this, type); else if (type == QLatin1String("hmac(ripemd160)")) return new gcryptQCAPlugin::gcryHMACContext(GCRY_MD_RMD160, this, type); else if (type == QLatin1String("aes128-ecb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, false, this, type); else if (type == QLatin1String("aes128-cfb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB, false, this, type); else if (type == QLatin1String("aes128-ofb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_OFB, false, this, type); else if (type == QLatin1String("aes128-cbc")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, false, this, type); else if (type == QLatin1String("aes192-ecb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB, false, this, type); else if (type == QLatin1String("aes192-cfb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB, false, this, type); else if (type == QLatin1String("aes192-ofb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB, false, this, type); else if (type == QLatin1String("aes192-cbc")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC, false, this, type); else if (type == QLatin1String("aes256-ecb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, false, this, type); else if (type == QLatin1String("aes256-cfb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, false, this, type); else if (type == QLatin1String("aes256-ofb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, false, this, type); else if (type == QLatin1String("aes256-cbc")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, false, this, type); else if (type == QLatin1String("blowfish-ecb")) return new gcryptQCAPlugin::gcryCipherContext( GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB, false, this, type); else if (type == QLatin1String("blowfish-cbc")) return new gcryptQCAPlugin::gcryCipherContext( GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC, false, this, type); else if (type == QLatin1String("blowfish-cfb")) return new gcryptQCAPlugin::gcryCipherContext( GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB, false, this, type); else if (type == QLatin1String("blowfish-ofb")) return new gcryptQCAPlugin::gcryCipherContext( GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB, false, this, type); else if (type == QLatin1String("tripledes-ecb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, false, this, type); else if (type == QLatin1String("tripledes-ofb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_OFB, false, this, type); else if (type == QLatin1String("des-ecb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, false, this, type); else if (type == QLatin1String("des-cbc")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC, false, this, type); else if (type == QLatin1String("des-cfb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CFB, false, this, type); else if (type == QLatin1String("des-ofb")) return new gcryptQCAPlugin::gcryCipherContext(GCRY_CIPHER_DES, GCRY_CIPHER_MODE_OFB, false, this, type); else if (type == QLatin1String("pbkdf1(sha1)")) return new gcryptQCAPlugin::pbkdf1Context(GCRY_MD_SHA1, this, type); else if (type == QLatin1String("pbkdf2(sha1)")) return new gcryptQCAPlugin::pbkdf2Context(GCRY_MD_SHA1, this, type); else if (type == QLatin1String("hkdf(sha256)")) return new gcryptQCAPlugin::hkdfContext(GCRY_MD_SHA256, this, type); else return nullptr; } }; class gcryptPlugin : public QObject, public QCAPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0") Q_INTERFACES(QCAPlugin) public: QCA::Provider *createProvider() override { return new gcryptProvider; } }; #include "qca-gcrypt.moc"