mirror of
https://github.com/QuasarApp/qca.git
synced 2025-04-26 11:34:32 +00:00
Introduce HKDF
Summary: It's needed for implementation of Secret Service: https://specifications.freedesktop.org/secret-service/ch07s03.html Reviewers: iromanov, sitter, #frameworks, dfaure Reviewed By: dfaure Subscribers: dfaure Differential Revision: https://phabricator.kde.org/D15510
This commit is contained in:
parent
98eead0058
commit
f57d661416
@ -1078,6 +1078,63 @@ public:
|
||||
: KeyDerivationFunction(withAlgorithm(QStringLiteral("pbkdf2"), algorithm), provider) {}
|
||||
};
|
||||
|
||||
/**
|
||||
\class HKDF qca_basic.h QtCrypto
|
||||
\since 2.3
|
||||
|
||||
HMAC-based extract-and-expand key derivation function
|
||||
|
||||
This class implements HMAC-based Extract-and-Expand Key Derivation Function,
|
||||
as specified in RFC5869.
|
||||
|
||||
\ingroup UserAPI
|
||||
*/
|
||||
class QCA_EXPORT HKDF : public Algorithm
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Standard constructor
|
||||
|
||||
\param algorithm the name of the hashing algorithm to use
|
||||
\param provider the name of the provider to use, if available
|
||||
*/
|
||||
explicit HKDF(const QString &algorithm = QStringLiteral("sha256"), const QString &provider = QString());
|
||||
|
||||
/**
|
||||
Standard copy constructor
|
||||
|
||||
\param from the KeyDerivationFunction to copy from
|
||||
*/
|
||||
HKDF(const HKDF &from);
|
||||
|
||||
~HKDF();
|
||||
|
||||
/**
|
||||
Assignment operator
|
||||
|
||||
Copies the state (including key) from one HKDF
|
||||
to another
|
||||
|
||||
\param from the HKDF to assign from
|
||||
*/
|
||||
HKDF & operator=(const HKDF &from);
|
||||
|
||||
/**
|
||||
Generate the key from a specified secret, salt value, and an additional info
|
||||
|
||||
\note key length is ignored for some functions
|
||||
|
||||
\param secret the secret (password or passphrase)
|
||||
\param salt the salt to use
|
||||
\param info the info to use
|
||||
\param keyLength the length of key to return
|
||||
|
||||
\return the derived key
|
||||
*/
|
||||
SymmetricKey makeKey(const SecureArray &secret, const InitializationVector &salt,
|
||||
const InitializationVector &info, unsigned int keyLength);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -379,6 +379,40 @@ public:
|
||||
unsigned int *iterationCount) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
\class HKDFContext qcaprovider.h QtCrypto
|
||||
|
||||
HKDF provider
|
||||
|
||||
\note This class is part of the provider plugin interface and should not
|
||||
be used directly by applications. You probably want HKDF instead.
|
||||
|
||||
\ingroup ProviderAPI
|
||||
*/
|
||||
class QCA_EXPORT HKDFContext : public BasicContext
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
Standard constructor
|
||||
|
||||
\param p the provider associated with this context
|
||||
\param type the name of the HKDF provided by this context (including algorithm)
|
||||
*/
|
||||
HKDFContext(Provider *p, const QString &type) : BasicContext(p, type) {}
|
||||
|
||||
/**
|
||||
Create a key and return it
|
||||
|
||||
\param secret the secret part (typically password)
|
||||
\param salt the salt / initialization vector
|
||||
\param info the info / initialization vector
|
||||
\param keyLength the length of the key to be produced
|
||||
*/
|
||||
virtual SymmetricKey makeKey(const SecureArray &secret, const InitializationVector &salt,
|
||||
const InitializationVector &info, unsigned int keyLength) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
\class DLGroupContext qcaprovider.h QtCrypto
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <botan/filters.h>
|
||||
#include <botan/hash.h>
|
||||
#include <botan/pbkdf.h>
|
||||
#include <botan/hkdf.h>
|
||||
#include <botan/stream_cipher.h>
|
||||
#endif
|
||||
|
||||
@ -226,6 +227,48 @@ protected:
|
||||
Botan::S2K* m_s2k;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------
|
||||
class BotanHKDFContext: public QCA::HKDFContext
|
||||
{
|
||||
public:
|
||||
BotanHKDFContext(const QString &hashName, QCA::Provider *p, const QString &type) : QCA::HKDFContext(p, type)
|
||||
{
|
||||
Botan::HMAC *hashObj;
|
||||
#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(2,0,0)
|
||||
hashObj = new Botan::HMAC(Botan::global_state().algorithm_factory().make_hash_function(hashName.toStdString()));
|
||||
#else
|
||||
hashObj = new Botan::HMAC(Botan::HashFunction::create_or_throw(hashName.toStdString()).release());
|
||||
#endif
|
||||
m_hkdf = new Botan::HKDF(hashObj);
|
||||
}
|
||||
|
||||
~BotanHKDFContext()
|
||||
{
|
||||
delete m_hkdf;
|
||||
}
|
||||
|
||||
Context *clone() const
|
||||
{
|
||||
return new BotanHKDFContext( *this );
|
||||
}
|
||||
|
||||
QCA::SymmetricKey makeKey(const QCA::SecureArray &secret, const QCA::InitializationVector &salt,
|
||||
const QCA::InitializationVector &info, unsigned int keyLength)
|
||||
{
|
||||
std::string secretString(secret.data(), secret.size());
|
||||
Botan::secure_vector<uint8_t> key(keyLength);
|
||||
m_hkdf->kdf(key.data(), keyLength,
|
||||
reinterpret_cast<const Botan::byte*>(secret.data()), secret.size(),
|
||||
reinterpret_cast<const Botan::byte*>(salt.data()), salt.size(),
|
||||
reinterpret_cast<const Botan::byte*>(info.data()), info.size());
|
||||
QCA::SecureArray retval(QByteArray::fromRawData(reinterpret_cast<const char*>(key.data()), key.size()));
|
||||
return QCA::SymmetricKey(retval);
|
||||
}
|
||||
|
||||
protected:
|
||||
Botan::HKDF* m_hkdf;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------
|
||||
class BotanCipherContext : public QCA::CipherContext
|
||||
@ -416,6 +459,7 @@ public:
|
||||
list += "pbkdf1(sha1)";
|
||||
list += "pbkdf1(md2)";
|
||||
list += "pbkdf2(sha1)";
|
||||
list += "hkdf(sha256)";
|
||||
list += "aes128-ecb";
|
||||
list += "aes128-cbc";
|
||||
list += "aes128-cfb";
|
||||
@ -481,6 +525,8 @@ public:
|
||||
return new BotanPBKDFContext( QString("PBKDF1(MD2)"), this, type );
|
||||
else if ( type == "pbkdf2(sha1)" )
|
||||
return new BotanPBKDFContext( QString("PBKDF2(SHA-1)"), this, type );
|
||||
else if ( type == "hkdf(sha256)" )
|
||||
return new BotanHKDFContext( QString("SHA-256"), this, type );
|
||||
else if ( type == "aes128-ecb" )
|
||||
return new BotanCipherContext( QString("AES-128"), QString("ECB"), QString("NoPadding"), this, type );
|
||||
else if ( type == "aes128-cbc" )
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/kdf.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -1276,6 +1277,35 @@ public:
|
||||
protected:
|
||||
};
|
||||
|
||||
class opensslHkdfContext : public HKDFContext
|
||||
{
|
||||
public:
|
||||
opensslHkdfContext(Provider *p, const QString &type) : HKDFContext(p, type)
|
||||
{
|
||||
}
|
||||
|
||||
Provider::Context *clone() const
|
||||
{
|
||||
return new opensslHkdfContext( *this );
|
||||
}
|
||||
|
||||
SymmetricKey makeKey(const SecureArray &secret, const InitializationVector &salt,
|
||||
const InitializationVector &info, unsigned int keyLength)
|
||||
{
|
||||
SecureArray out(keyLength);
|
||||
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
|
||||
EVP_PKEY_derive_init(pctx);
|
||||
EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256());
|
||||
EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt.data(), int(salt.size()));
|
||||
EVP_PKEY_CTX_set1_hkdf_key(pctx, secret.data(), int(secret.size()));
|
||||
EVP_PKEY_CTX_add1_hkdf_info(pctx, info.data(), int(info.size()));
|
||||
size_t outlen = out.size();
|
||||
EVP_PKEY_derive(pctx, reinterpret_cast<unsigned char*>(out.data()), &outlen);
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
class opensslHMACContext : public MACContext
|
||||
{
|
||||
public:
|
||||
@ -7381,6 +7411,7 @@ public:
|
||||
#endif
|
||||
list += "pbkdf1(sha1)";
|
||||
list += "pbkdf2(sha1)";
|
||||
list += "hkdf(sha256)";
|
||||
list += "pkey";
|
||||
list += "dlgroup";
|
||||
list += "rsa";
|
||||
@ -7451,6 +7482,8 @@ public:
|
||||
#endif
|
||||
else if ( type == "pbkdf2(sha1)" )
|
||||
return new opensslPbkdf2Context( this, type );
|
||||
else if ( type == "hkdf(sha256)" )
|
||||
return new opensslHkdfContext( this, type );
|
||||
else if ( type == "hmac(md5)" )
|
||||
return new opensslHMACContext( EVP_md5(), this, type );
|
||||
else if ( type == "hmac(sha1)" )
|
||||
|
@ -591,4 +591,35 @@ QString KeyDerivationFunction::withAlgorithm(const QString &kdfType, const QStri
|
||||
return (kdfType + '(' + algType + ')');
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// HKDF
|
||||
//----------------------------------------------------------------------------
|
||||
HKDF::HKDF(const QString &algorithm, const QString &provider)
|
||||
: Algorithm(QStringLiteral("hkdf(") + algorithm + ')', provider)
|
||||
{
|
||||
}
|
||||
|
||||
HKDF::HKDF(const HKDF &from)
|
||||
: Algorithm(from)
|
||||
{
|
||||
}
|
||||
|
||||
HKDF::~HKDF()
|
||||
{
|
||||
}
|
||||
|
||||
HKDF & HKDF::operator=(const HKDF &from)
|
||||
{
|
||||
Algorithm::operator=(from);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SymmetricKey HKDF::makeKey(const SecureArray &secret, const InitializationVector &salt, const InitializationVector &info, unsigned int keyLength)
|
||||
{
|
||||
return static_cast<HKDFContext *>(context())->makeKey(secret,
|
||||
salt,
|
||||
info,
|
||||
keyLength);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ private slots:
|
||||
void pbkdf2Tests();
|
||||
void pbkdf2TimeTest();
|
||||
void pbkdf2extraTests();
|
||||
void hkdfTests_data();
|
||||
void hkdfTests();
|
||||
private:
|
||||
QCA::Initializer* m_init;
|
||||
};
|
||||
@ -475,6 +477,59 @@ void KDFUnitTest::pbkdf2extraTests()
|
||||
}
|
||||
}
|
||||
|
||||
void KDFUnitTest::hkdfTests_data()
|
||||
{
|
||||
QTest::addColumn<QString>("secret"); // usually a password or passphrase
|
||||
QTest::addColumn<QString>("salt"); // a salt or initialisation vector
|
||||
QTest::addColumn<QString>("info"); // an additional info
|
||||
QTest::addColumn<QString>("output"); // the key you get back
|
||||
|
||||
// RFC 5869, Appendix A
|
||||
QTest::newRow("1") << QString("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
|
||||
<< QString("000102030405060708090a0b0c")
|
||||
<< QString("f0f1f2f3f4f5f6f7f8f9")
|
||||
<< QString("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865");
|
||||
|
||||
QTest::newRow("2") << QString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f")
|
||||
<< QString("606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf")
|
||||
<< QString("b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")
|
||||
<< QString("b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87");
|
||||
|
||||
QTest::newRow("3") << QString("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
|
||||
<< QString()
|
||||
<< QString()
|
||||
<< QString("8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8");
|
||||
}
|
||||
|
||||
void KDFUnitTest::hkdfTests()
|
||||
{
|
||||
QStringList providersToTest;
|
||||
providersToTest.append("qca-ossl");
|
||||
//providersToTest.append("qca-gcrypt");
|
||||
providersToTest.append("qca-botan");
|
||||
|
||||
QFETCH(QString, secret);
|
||||
QFETCH(QString, salt);
|
||||
QFETCH(QString, info);
|
||||
QFETCH(QString, output);
|
||||
|
||||
foreach(QString provider, providersToTest) {
|
||||
if(!QCA::isSupported("hkdf(sha256)", provider))
|
||||
QWARN(QString("HKDF with SHA256 not supported for "+provider).toLocal8Bit());
|
||||
else {
|
||||
QCA::SecureArray password = QCA::hexToArray( secret );
|
||||
QCA::InitializationVector saltv( QCA::hexToArray( salt ) );
|
||||
QCA::InitializationVector infov( QCA::hexToArray( info ) );
|
||||
QCA::SymmetricKey key = QCA::HKDF("sha256", provider).makeKey( password,
|
||||
saltv,
|
||||
infov,
|
||||
output.size() / 2 );
|
||||
QCOMPARE( QCA::arrayToHex( key.toByteArray() ), output );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(KDFUnitTest)
|
||||
|
||||
#include "kdfunittest.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user