mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-24 02:13:02 -07:00
Challenge-response authentication: the client derives a scrypt verifier (RFC 7914, EVP_PBE_scrypt, N=32768, r=8, p=1) and authenticates with HMAC-SHA256(key, nonce), so neither the password nor its hash is transmitted. The stored format becomes "$scrypt$<n>$<r>$<p>$<salt>$<verifier>" and Response_PasswordSalt now carries the cost parameters. Strict servers only accept scrypt verifiers; legacy accounts are migrated after a successful login. Fix #344 for challenge-response servers: a saved profile stores the derived verifier under the password key instead of the plaintext password. The connect dialog loads it without revealing it, autoconnect passes it through, the change-password dialog no longer prefills the old password field with it, and the client only persists the verifier when "Save password" is checked. Took 3 minutes Took 1 minute Took 10 seconds Took 7 minutes
53 lines
1.8 KiB
C++
53 lines
1.8 KiB
C++
#ifndef PASSWORDHASHER_H
|
|
#define PASSWORDHASHER_H
|
|
|
|
#include <QByteArray>
|
|
#include <QObject>
|
|
|
|
// scrypt cost parameters used for newly created password verifiers. These match
|
|
// the RFC 7914 recommended parameters for interactive use.
|
|
constexpr int SCRYPT_N = 32768;
|
|
constexpr int SCRYPT_R = 8;
|
|
constexpr int SCRYPT_P = 1;
|
|
constexpr int SCRYPT_SALT_LENGTH = 16;
|
|
constexpr int SCRYPT_VERIFIER_LENGTH = 64;
|
|
|
|
enum class PasswordFormat
|
|
{
|
|
None = 0,
|
|
Scrypt
|
|
};
|
|
|
|
struct PasswordVerifier
|
|
{
|
|
PasswordFormat format = PasswordFormat::None;
|
|
int n = 0;
|
|
int r = 0;
|
|
int p = 0;
|
|
QByteArray salt;
|
|
QByteArray verifier;
|
|
bool isValid = false;
|
|
};
|
|
|
|
class PasswordHasher
|
|
{
|
|
public:
|
|
static QString computeHash(const QString &password, const QString &salt);
|
|
static QString generateRandomSalt(const int len = 16);
|
|
static QString generateActivationToken();
|
|
|
|
/** @brief Derive the scrypt verifier for the given password, salt and cost parameters. */
|
|
static QByteArray deriveKey(const QString &password, const QByteArray &salt, int n, int r, int p);
|
|
/** @brief Build a "$scrypt$<n>$<r>$<p>$<salt>$<verifier>" string with a fresh random salt. */
|
|
static QString generatePasswordVerifier(const QString &password);
|
|
/** @brief Parse a stored "$scrypt$..." string into its components. */
|
|
static PasswordVerifier parsePasswordVerifier(const QString &stored);
|
|
/** @brief True if the stored value is not in the scrypt format (legacy salt+hash). */
|
|
static bool isLegacyFormat(const QString &stored);
|
|
/** @brief HMAC-SHA256 of nonce keyed with the password verifier, used for challenge-response logins. */
|
|
static QByteArray computeResponse(const QByteArray &key, const QByteArray &nonce);
|
|
/** @brief Constant-time byte comparison. */
|
|
static bool constantTimeEquals(const QByteArray &a, const QByteArray &b);
|
|
};
|
|
|
|
#endif
|