5 #include "credentialpassword_p.h"
6 #include "authenticationrealm.h"
8 #include <QLoggingCategory>
9 #include <QMessageAuthenticationCode>
15 Q_LOGGING_CATEGORY(C_CREDENTIALPASSWORD,
"cutelyst.plugin.credentialpassword", QtWarningMsg)
18 , d_ptr(new CredentialPasswordPrivate)
23 CredentialPassword::~CredentialPassword()
34 if (d->checkPassword(_user, authinfo)) {
37 qCDebug(C_CREDENTIALPASSWORD) <<
"Password didn't match";
40 qCDebug(C_CREDENTIALPASSWORD) <<
"Unable to locate a user matching user info provided in realm";
48 return d->passwordField;
54 d->passwordField = fieldName;
60 return d->passwordType;
66 d->passwordType = type;
72 return d->passwordPreSalt;
84 return d->passwordPostSalt;
94 bool slowEquals(
const QByteArray &a,
const QByteArray &b)
96 int diff = a.size() ^ b.size();
97 for(
int i = 0; i < a.size() && i < b.size(); i++) {
103 #define HASH_SECTIONS 4
104 #define HASH_ALGORITHM_INDEX 0
105 #define HASH_ITERATION_INDEX 1
106 #define HASH_SALT_INDEX 2
107 #define HASH_PBKDF2_INDEX 3
110 QByteArrayList params = correctHash.split(
':');
111 if (params.size() < HASH_SECTIONS) {
115 int method = CredentialPasswordPrivate::cryptoStrToEnum(params.at(HASH_ALGORITHM_INDEX));
120 QByteArray pbkdf2Hash = QByteArray::fromBase64(params.at(HASH_PBKDF2_INDEX));
124 static_cast<QCryptographicHash::Algorithm
>(method),
126 params.at(HASH_SALT_INDEX),
127 params.at(HASH_ITERATION_INDEX).toInt(),
137 QFile random(QStringLiteral(
"/dev/urandom"));
138 if (random.open(QIODevice::ReadOnly)) {
139 salt = random.read(saltByteSize).toBase64();
142 salt = QUuid::createUuid().toRfc4122().toBase64();
147 const QByteArray methodStr = CredentialPasswordPrivate::cryptoEnumToStr(method);
148 return methodStr +
':' + QByteArray::number(iterations) +
':' + salt +
':' +
160 return createPassword(password, QCryptographicHash::Sha512, 10000, 16, 16);
167 QByteArray
CredentialPassword::pbkdf2(QCryptographicHash::Algorithm method,
const QByteArray &password,
const QByteArray &salt,
int rounds,
int keyLength)
171 if (rounds <= 0 || keyLength <= 0) {
172 qCCritical(C_CREDENTIALPASSWORD,
"PBKDF2 ERROR: Invalid parameters.");
176 if (salt.size() == 0 || salt.size() > std::numeric_limits<int>::max() - 4) {
179 key.reserve(keyLength);
181 int saltSize = salt.size();
182 QByteArray asalt = salt;
183 asalt.resize(saltSize + 4);
187 QMessageAuthenticationCode code(method, password);
189 for (
int count = 1, remainingBytes = keyLength; remainingBytes > 0; ++count) {
190 asalt[saltSize + 0] =
static_cast<char>((count >> 24) & 0xff);
191 asalt[saltSize + 1] =
static_cast<char>((count >> 16) & 0xff);
192 asalt[saltSize + 2] =
static_cast<char>((count >> 8) & 0xff);
193 asalt[saltSize + 3] =
static_cast<char>(count & 0xff);
197 obuf = d1 = code.result();
199 for (
int i = 1; i < rounds; ++i) {
203 auto it = obuf.begin();
204 auto d1It = d1.cbegin();
205 while (d1It != d1.cend()) {
213 remainingBytes -= obuf.size();
216 key.truncate(keyLength);
222 return QMessageAuthenticationCode::hash(key, message, method);
227 QString password = authinfo.value(passwordField);
228 const QString storedPassword = user.value(passwordField).toString();
230 if (Q_LIKELY(passwordType == CredentialPassword::Hashed)) {
231 if (!passwordPreSalt.isEmpty()) {
232 password.prepend(password);
235 if (!passwordPostSalt.isEmpty()) {
236 password.append(password);
240 }
else if (passwordType == CredentialPassword::Clear) {
241 return storedPassword == password;
242 }
else if (passwordType == CredentialPassword::None) {
243 qCDebug(C_CREDENTIALPASSWORD) <<
"CredentialPassword is set to ignore password check";
250 QByteArray CredentialPasswordPrivate::cryptoEnumToStr(QCryptographicHash::Algorithm method)
252 QByteArray hashmethod;
254 #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
255 if (method == QCryptographicHash::Md4) {
256 hashmethod = QByteArrayLiteral(
"Md4");
257 }
else if (method == QCryptographicHash::Md5) {
258 hashmethod = QByteArrayLiteral(
"Md5");
261 if (method == QCryptographicHash::Sha1) {
262 hashmethod = QByteArrayLiteral(
"Sha1");
264 #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
265 if (method == QCryptographicHash::Sha224) {
266 hashmethod = QByteArrayLiteral(
"Sha224");
267 }
else if (method == QCryptographicHash::Sha256) {
268 hashmethod = QByteArrayLiteral(
"Sha256");
269 }
else if (method == QCryptographicHash::Sha384) {
270 hashmethod = QByteArrayLiteral(
"Sha384");
271 }
else if (method == QCryptographicHash::Sha512) {
272 hashmethod = QByteArrayLiteral(
"Sha512");
273 }
else if (method == QCryptographicHash::Sha3_224) {
274 hashmethod = QByteArrayLiteral(
"Sha3_224");
275 }
else if (method == QCryptographicHash::Sha3_256) {
276 hashmethod = QByteArrayLiteral(
"Sha3_256");
277 }
else if (method == QCryptographicHash::Sha3_384) {
278 hashmethod = QByteArrayLiteral(
"Sha3_384");
279 }
else if (method == QCryptographicHash::Sha3_512) {
280 hashmethod = QByteArrayLiteral(
"Sha3_512");
287 int CredentialPasswordPrivate::cryptoStrToEnum(
const QByteArray &hashMethod)
289 QByteArray hashmethod = hashMethod;
292 #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
293 if (hashmethod ==
"Md4") {
294 method = QCryptographicHash::Md4;
295 }
else if (hashmethod ==
"Md5") {
296 method = QCryptographicHash::Md5;
299 if (hashmethod ==
"Sha1") {
300 method = QCryptographicHash::Sha1;
302 #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
303 if (hashmethod ==
"Sha224") {
304 method = QCryptographicHash::Sha224;
305 }
else if (hashmethod ==
"Sha256") {
306 method = QCryptographicHash::Sha256;
307 }
else if (hashmethod ==
"Sha384") {
308 method = QCryptographicHash::Sha384;
309 }
else if (hashmethod ==
"Sha512") {
310 method = QCryptographicHash::Sha512;
311 }
else if (hashmethod ==
"Sha3_224") {
312 method = QCryptographicHash::Sha3_224;
313 }
else if (hashmethod ==
"Sha3_256") {
314 method = QCryptographicHash::Sha3_256;
315 }
else if (hashmethod ==
"Sha3_384") {
316 method = QCryptographicHash::Sha3_384;
317 }
else if (hashmethod ==
"Sha3_512") {
318 method = QCryptographicHash::Sha3_512;
325 #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