5#include "authenticationrealm.h"
6#include "credentialpassword_p.h"
10#include <QLoggingCategory>
11#include <QMessageAuthenticationCode>
15using namespace Qt::StringLiterals;
17Q_LOGGING_CATEGORY(C_CREDENTIALPASSWORD,
"cutelyst.plugin.credentialpassword", QtWarningMsg)
21 , d_ptr(new CredentialPasswordPrivate)
38 bool validPassword =
false;
42 _user.
checkPassword(c, realm, authinfo.value(d->passwordField), d->passwordField);
44 validPassword = d->checkPassword(_user, authinfo);
50 qCDebug(C_CREDENTIALPASSWORD) <<
"Password didn't match";
53 qCDebug(C_CREDENTIALPASSWORD)
54 <<
"Unable to locate a user matching user info provided in realm";
62 return d->passwordField;
68 d->passwordField = fieldName;
74 return d->passwordType;
80 d->passwordType = type;
86 return d->passwordPreSalt;
98 return d->passwordPostSalt;
108bool slowEquals(
const QByteArray &a,
const QByteArray &b)
110 int diff = a.size() ^ b.size();
111 for (
int i = 0; i < a.size() && i < b.size(); i++) {
118#define HASH_SECTIONS 4
119#define HASH_ALGORITHM_INDEX 0
120#define HASH_ITERATION_INDEX 1
121#define HASH_SALT_INDEX 2
122#define HASH_PBKDF2_INDEX 3
127 QByteArrayList params = correctHash.split(
':');
128 if (params.size() < HASH_SECTIONS) {
132 int method = CredentialPasswordPrivate::cryptoStrToEnum(params.at(HASH_ALGORITHM_INDEX));
137 QByteArray pbkdf2Hash = QByteArray::fromBase64(params.at(HASH_PBKDF2_INDEX));
138 return slowEquals(pbkdf2Hash,
139 pbkdf2(
static_cast<QCryptographicHash::Algorithm
>(method),
141 params.at(HASH_SALT_INDEX),
142 params.at(HASH_ITERATION_INDEX).toInt(),
143 pbkdf2Hash.length()));
147 QCryptographicHash::Algorithm method,
154 QFile random(u
"/dev/urandom"_s);
155 if (random.open(QIODevice::ReadOnly)) {
156 salt = random.read(saltByteSize).toBase64();
159 salt = QUuid::createUuid().toRfc4122().toBase64();
164 const QByteArray methodStr = CredentialPasswordPrivate::cryptoEnumToStr(method);
165 return methodStr +
':' + QByteArray::number(iterations) +
':' + salt +
':' +
166 pbkdf2(method, password, salt, iterations, hashByteSize).toBase64();
171 return createPassword(password, QCryptographicHash::Sha512, 10000, 16, 16);
179 const QByteArray &password,
180 const QByteArray &salt,
186 if (rounds <= 0 || keyLength <= 0) {
187 qCCritical(C_CREDENTIALPASSWORD,
"PBKDF2 ERROR: Invalid parameters.");
191 if (salt.size() == 0 || salt.size() > std::numeric_limits<int>::max() - 4) {
194 key.reserve(keyLength);
196 int saltSize = salt.size();
197 QByteArray asalt = salt;
198 asalt.resize(saltSize + 4);
203 QMessageAuthenticationCode code(method, password);
205 for (
int count = 1, remainingBytes = keyLength; remainingBytes > 0; ++count) {
206 asalt[saltSize + 0] =
static_cast<char>((count >> 24) & 0xff);
207 asalt[saltSize + 1] =
static_cast<char>((count >> 16) & 0xff);
208 asalt[saltSize + 2] =
static_cast<char>((count >> 8) & 0xff);
209 asalt[saltSize + 3] =
static_cast<char>(count & 0xff);
213 obuf = d1 = code.result();
215 for (
int i = 1; i < rounds; ++i) {
219 auto it = obuf.begin();
220 auto d1It = d1.cbegin();
221 while (d1It != d1.cend()) {
229 remainingBytes -= obuf.size();
232 key.truncate(keyLength);
237 const QByteArray &key,
238 const QByteArray &message)
240 return QMessageAuthenticationCode::hash(key, message, method);
246 const QString password = passwordPreSalt + authinfo.value(passwordField) + passwordPostSalt;
247 const QString storedPassword = user.
value(passwordField).toString();
252 return storedPassword == password;
254 qCDebug(C_CREDENTIALPASSWORD) <<
"CredentialPassword is set to ignore password check";
257 qCWarning(C_CREDENTIALPASSWORD) <<
"checkPassword called with SelfCheck type; "
258 "password validation should be delegated to the store";
264QByteArray CredentialPasswordPrivate::cryptoEnumToStr(QCryptographicHash::Algorithm method)
266 QByteArray hashmethod;
268#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
269 if (method == QCryptographicHash::Md4) {
270 hashmethod = QByteArrayLiteral(
"Md4");
271 }
else if (method == QCryptographicHash::Md5) {
272 hashmethod = QByteArrayLiteral(
"Md5");
275 if (method == QCryptographicHash::Sha1) {
276 hashmethod = QByteArrayLiteral(
"Sha1");
278#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
279 if (method == QCryptographicHash::Sha224) {
280 hashmethod = QByteArrayLiteral(
"Sha224");
281 }
else if (method == QCryptographicHash::Sha256) {
282 hashmethod = QByteArrayLiteral(
"Sha256");
283 }
else if (method == QCryptographicHash::Sha384) {
284 hashmethod = QByteArrayLiteral(
"Sha384");
285 }
else if (method == QCryptographicHash::Sha512) {
286 hashmethod = QByteArrayLiteral(
"Sha512");
287 }
else if (method == QCryptographicHash::Sha3_224) {
288 hashmethod = QByteArrayLiteral(
"Sha3_224");
289 }
else if (method == QCryptographicHash::Sha3_256) {
290 hashmethod = QByteArrayLiteral(
"Sha3_256");
291 }
else if (method == QCryptographicHash::Sha3_384) {
292 hashmethod = QByteArrayLiteral(
"Sha3_384");
293 }
else if (method == QCryptographicHash::Sha3_512) {
294 hashmethod = QByteArrayLiteral(
"Sha3_512");
301int CredentialPasswordPrivate::cryptoStrToEnum(
const QByteArray &hashMethod)
303 QByteArray hashmethod = hashMethod;
306#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
307 if (hashmethod ==
"Md4") {
308 method = QCryptographicHash::Md4;
309 }
else if (hashmethod ==
"Md5") {
310 method = QCryptographicHash::Md5;
313 if (hashmethod ==
"Sha1") {
314 method = QCryptographicHash::Sha1;
316#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
317 if (hashmethod ==
"Sha224") {
318 method = QCryptographicHash::Sha224;
319 }
else if (hashmethod ==
"Sha256") {
320 method = QCryptographicHash::Sha256;
321 }
else if (hashmethod ==
"Sha384") {
322 method = QCryptographicHash::Sha384;
323 }
else if (hashmethod ==
"Sha512") {
324 method = QCryptographicHash::Sha512;
325 }
else if (hashmethod ==
"Sha3_224") {
326 method = QCryptographicHash::Sha3_224;
327 }
else if (hashmethod ==
"Sha3_256") {
328 method = QCryptographicHash::Sha3_256;
329 }
else if (hashmethod ==
"Sha3_384") {
330 method = QCryptographicHash::Sha3_384;
331 }
else if (hashmethod ==
"Sha3_512") {
332 method = QCryptographicHash::Sha3_512;
339#include "moc_credentialpassword.cpp"
AuthenticationCredential(QObject *parent=nullptr)
Combines user store and credential validation into a named realm.
virtual AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo)
Container for user data retrieved from an AuthenticationStore.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
bool checkPassword(Context *c, AuthenticationRealm *realm, const QString &password, const QString &passwordField=QStringLiteral("password"))
void setPasswordType(PasswordType type)
QString passwordField() const
void setPasswordPostSalt(const QString &passwordPostSalt)
CredentialPassword(QObject *parent=nullptr)
virtual ~CredentialPassword() override
AuthenticationUser authenticate(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo) final
static QByteArray pbkdf2(QCryptographicHash::Algorithm method, const QByteArray &password, const QByteArray &salt, int rounds, int keyLength)
static bool validatePassword(const QByteArray &password, const QByteArray &correctHash)
QString passwordPreSalt() const
PasswordType passwordType() const
QString passwordPostSalt() const
static QByteArray createPassword(const QByteArray &password, QCryptographicHash::Algorithm method, int iterations, int saltByteSize, int hashByteSize)
void setPasswordField(const QString &fieldName)
static QByteArray hmac(QCryptographicHash::Algorithm method, const QByteArray &key, const QByteArray &message)
void setPasswordPreSalt(const QString &passwordPreSalt)
QMultiMap< QString, QString > ParamsMultiMap
The Cutelyst namespace holds all public Cutelyst API.