5#include "authenticationrealm.h"
6#include "credentialpassword_p.h"
9#include <QLoggingCategory>
10#include <QMessageAuthenticationCode>
15Q_LOGGING_CATEGORY(C_CREDENTIALPASSWORD,
"cutelyst.plugin.credentialpassword", QtWarningMsg)
19 , d_ptr(new CredentialPasswordPrivate)
23CredentialPassword::~CredentialPassword()
36 if (d->checkPassword(_user, authinfo)) {
39 qCDebug(C_CREDENTIALPASSWORD) <<
"Password didn't match";
42 qCDebug(C_CREDENTIALPASSWORD)
43 <<
"Unable to locate a user matching user info provided in realm";
51 return d->passwordField;
57 d->passwordField = fieldName;
63 return d->passwordType;
69 d->passwordType = type;
75 return d->passwordPreSalt;
87 return d->passwordPostSalt;
97bool slowEquals(
const QByteArray &a,
const QByteArray &b)
99 int diff = a.size() ^ b.size();
100 for (
int i = 0; i < a.size() && i < b.size(); i++) {
106#define HASH_SECTIONS 4
107#define HASH_ALGORITHM_INDEX 0
108#define HASH_ITERATION_INDEX 1
109#define HASH_SALT_INDEX 2
110#define HASH_PBKDF2_INDEX 3
113 QByteArrayList params = correctHash.split(
':');
114 if (params.size() < HASH_SECTIONS) {
118 int method = CredentialPasswordPrivate::cryptoStrToEnum(params.at(HASH_ALGORITHM_INDEX));
123 QByteArray pbkdf2Hash = QByteArray::fromBase64(params.at(HASH_PBKDF2_INDEX));
124 return slowEquals(pbkdf2Hash,
125 pbkdf2(
static_cast<QCryptographicHash::Algorithm
>(method),
127 params.at(HASH_SALT_INDEX),
128 params.at(HASH_ITERATION_INDEX).toInt(),
129 pbkdf2Hash.length()));
133 QCryptographicHash::Algorithm method,
140 QFile random(QStringLiteral(
"/dev/urandom"));
141 if (random.open(QIODevice::ReadOnly)) {
142 salt = random.read(saltByteSize).toBase64();
145 salt = QUuid::createUuid().toRfc4122().toBase64();
150 const QByteArray methodStr = CredentialPasswordPrivate::cryptoEnumToStr(method);
151 return methodStr +
':' + QByteArray::number(iterations) +
':' + salt +
':' +
152 pbkdf2(method, password, salt, iterations, hashByteSize).toBase64();
157 return createPassword(password, QCryptographicHash::Sha512, 10000, 16, 16);
165 const QByteArray &password,
166 const QByteArray &salt,
172 if (rounds <= 0 || keyLength <= 0) {
173 qCCritical(C_CREDENTIALPASSWORD,
"PBKDF2 ERROR: Invalid parameters.");
177 if (salt.size() == 0 || salt.size() > std::numeric_limits<int>::max() - 4) {
180 key.reserve(keyLength);
182 int saltSize = salt.size();
183 QByteArray asalt = salt;
184 asalt.resize(saltSize + 4);
188 QMessageAuthenticationCode code(method, password);
190 for (
int count = 1, remainingBytes = keyLength; remainingBytes > 0; ++count) {
191 asalt[saltSize + 0] =
static_cast<char>((count >> 24) & 0xff);
192 asalt[saltSize + 1] =
static_cast<char>((count >> 16) & 0xff);
193 asalt[saltSize + 2] =
static_cast<char>((count >> 8) & 0xff);
194 asalt[saltSize + 3] =
static_cast<char>(count & 0xff);
198 obuf = d1 = code.result();
200 for (
int i = 1; i < rounds; ++i) {
204 auto it = obuf.begin();
205 auto d1It = d1.cbegin();
206 while (d1It != d1.cend()) {
214 remainingBytes -= obuf.size();
217 key.truncate(keyLength);
222 const QByteArray &key,
223 const QByteArray &message)
225 return QMessageAuthenticationCode::hash(key, message, method);
231 const QString password = passwordPreSalt + authinfo.value(passwordField) + passwordPostSalt;
232 const QString storedPassword = user.value(passwordField).toString();
234 if (Q_LIKELY(passwordType == CredentialPassword::Hashed)) {
236 }
else if (passwordType == CredentialPassword::Clear) {
237 return storedPassword == password;
238 }
else if (passwordType == CredentialPassword::None) {
239 qCDebug(C_CREDENTIALPASSWORD) <<
"CredentialPassword is set to ignore password check";
246QByteArray CredentialPasswordPrivate::cryptoEnumToStr(QCryptographicHash::Algorithm method)
248 QByteArray hashmethod;
250#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
251 if (method == QCryptographicHash::Md4) {
252 hashmethod = QByteArrayLiteral(
"Md4");
253 }
else if (method == QCryptographicHash::Md5) {
254 hashmethod = QByteArrayLiteral(
"Md5");
257 if (method == QCryptographicHash::Sha1) {
258 hashmethod = QByteArrayLiteral(
"Sha1");
260#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
261 if (method == QCryptographicHash::Sha224) {
262 hashmethod = QByteArrayLiteral(
"Sha224");
263 }
else if (method == QCryptographicHash::Sha256) {
264 hashmethod = QByteArrayLiteral(
"Sha256");
265 }
else if (method == QCryptographicHash::Sha384) {
266 hashmethod = QByteArrayLiteral(
"Sha384");
267 }
else if (method == QCryptographicHash::Sha512) {
268 hashmethod = QByteArrayLiteral(
"Sha512");
269 }
else if (method == QCryptographicHash::Sha3_224) {
270 hashmethod = QByteArrayLiteral(
"Sha3_224");
271 }
else if (method == QCryptographicHash::Sha3_256) {
272 hashmethod = QByteArrayLiteral(
"Sha3_256");
273 }
else if (method == QCryptographicHash::Sha3_384) {
274 hashmethod = QByteArrayLiteral(
"Sha3_384");
275 }
else if (method == QCryptographicHash::Sha3_512) {
276 hashmethod = QByteArrayLiteral(
"Sha3_512");
283int CredentialPasswordPrivate::cryptoStrToEnum(
const QByteArray &hashMethod)
285 QByteArray hashmethod = hashMethod;
288#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
289 if (hashmethod ==
"Md4") {
290 method = QCryptographicHash::Md4;
291 }
else if (hashmethod ==
"Md5") {
292 method = QCryptographicHash::Md5;
295 if (hashmethod ==
"Sha1") {
296 method = QCryptographicHash::Sha1;
298#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
299 if (hashmethod ==
"Sha224") {
300 method = QCryptographicHash::Sha224;
301 }
else if (hashmethod ==
"Sha256") {
302 method = QCryptographicHash::Sha256;
303 }
else if (hashmethod ==
"Sha384") {
304 method = QCryptographicHash::Sha384;
305 }
else if (hashmethod ==
"Sha512") {
306 method = QCryptographicHash::Sha512;
307 }
else if (hashmethod ==
"Sha3_224") {
308 method = QCryptographicHash::Sha3_224;
309 }
else if (hashmethod ==
"Sha3_256") {
310 method = QCryptographicHash::Sha3_256;
311 }
else if (hashmethod ==
"Sha3_384") {
312 method = QCryptographicHash::Sha3_384;
313 }
else if (hashmethod ==
"Sha3_512") {
314 method = QCryptographicHash::Sha3_512;
321#include "moc_credentialpassword.cpp"
virtual AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo)
Tries to find the user with authinfo returning a non null AuthenticationUser on success.
bool isNull() const
Returns true if the object is null.
void setPasswordType(PasswordType type)
Sets the type of password this class will be dealing with.
QString passwordField() const
Returns the field to look for when authenticating the user.
void setPasswordPostSalt(const QString &passwordPostSalt)
Sets the salt string to be appended to the password.
AuthenticationUser authenticate(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo) final
Tries to authenticate the authinfo using the give realm.
static QByteArray pbkdf2(QCryptographicHash::Algorithm method, const QByteArray &password, const QByteArray &salt, int rounds, int keyLength)
Generates a pbkdf2 string for the given password.
static bool validatePassword(const QByteArray &password, const QByteArray &correctHash)
Validates the given password against the correct hash.
QString passwordPreSalt() const
Returns the salt string to be prepended to the password.
PasswordType passwordType() const
Returns the type of password this class will be dealing with.
QString passwordPostSalt() const
Returns the salt string to be appended to the password.
static QByteArray createPassword(const QByteArray &password, QCryptographicHash::Algorithm method, int iterations, int saltByteSize, int hashByteSize)
Creates a password hash string.
void setPasswordField(const QString &fieldName)
Sets the field to look for when authenticating the user.
static QByteArray hmac(QCryptographicHash::Algorithm method, const QByteArray &key, const QByteArray &message)
Generates the Hash-based message authentication code.
void setPasswordPreSalt(const QString &passwordPreSalt)
Sets the salt string to be prepended to the password.
The Cutelyst namespace holds all public Cutelyst API.
QMultiMap< QString, QString > ParamsMultiMap