--- plasma-desktop~/kcms/users/src/user.cpp +++ plasma-desktop/kcms/users/src/user.cpp @@ -221,13 +221,70 @@ static QString saltPassword(const QStrin return QString::fromUtf8(salted); } +static QByteArray +generateSalt(const char* prefix, int byte_len) +{ + /* + * Generate valid salt for crypt + * The last hexad must contain no more than (6 - bits_diff) bits or + * crypt will return *0 + * + * For example, for 16 byte salt, hexads count = 22 + * 22 * 6 = 132 bits, but last 4 bits can't be used + * so last hexad must contain no more than 2 bits + */ + + if (byte_len < 16) + byte_len = 16; + + int hexads_count = 1 + ((byte_len * 8 - 1) / 6); + int bits_diff = hexads_count * 6 - byte_len * 8; + int last_hexad_bound = (64U >> bits_diff); + + QRandomGenerator* gen = QRandomGenerator::system(); + QByteArray salt(prefix); + salt.reserve(hexads_count); + + // Alphabet from crypt.c + char alpha[65] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + for (int i = 0; i < hexads_count - 1; ++i) { + salt.append(alpha[gen->bounded(64)]); + } + salt.append(alpha[gen->bounded(last_hexad_bound)]); + + return salt; +} + +static QString +saltPasswordGostYescrypt(const QString &plain) +{ + // Try gost-yescrypt, CPU time = 8, 16 byte salt + char* crypted = crypt(plain.toUtf8().constData(), + generateSalt("$gy$jCT$", 16).constData()); + + // Use yescrypt (CPU time = 8, 16 byte salt) if gost-yescrypt failed + if (!crypted || crypted[0] == '*') { + crypted = crypt(plain.toUtf8().constData(), + generateSalt("$2y$jCT$", 16).constData()); + } + + // If yescrypt failed too - use the upstream method + if (!crypted || crypted[0] == '*') { + return saltPassword(plain); + } + + return QString(crypted); +} + void User::setPassword(const QString &password) { // Blocking because we need to wait for the password to be changed before we // can ask the user about also possibly changing their KWallet password auto mc = QDBusMessage::createMethodCall(m_dbusIface->service(), m_dbusIface->path(), m_dbusIface->interface(), "SetPassword"); - mc.setArguments({saltPassword(password), QString()}); + mc.setArguments({saltPasswordGostYescrypt(password), QString()}); mc.setInteractiveAuthorizationAllowed(true); auto message = QDBusConnection::systemBus().call(mc);