File indexing completed on 2024-04-28 05:50:07

0001 /*
0002  * SPDX-License-Identifier: GPL-3.0-or-later
0003  * SPDX-FileCopyrightText: 2020-2021 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
0004  */
0005 #include "keys.h"
0006 
0007 #include "../logging_p.h"
0008 
0009 #include <cstring>
0010 
0011 KEYSMITH_LOGGER(logger, ".accounts.keys")
0012 
0013 namespace accounts
0014 {
0015     AccountSecret::AccountSecret(const secrets::SecureRandom &random, QObject *parent) :
0016         QObject(parent), m_stillAlive(true), m_newPassword(false), m_passwordRequested(false),
0017         m_hackWithoutChallenge(false), // HACK: disables challenge verification, remove at some point!
0018         m_random(random), m_salt(std::nullopt), m_challenge(std::nullopt), m_key(nullptr), m_password(nullptr),
0019         m_keyParams(std::nullopt)
0020     {
0021     }
0022 
0023     void AccountSecret::cancelRequests(void)
0024     {
0025         if (!m_stillAlive) {
0026             qCDebug(logger) << "Ignoring cancellation request: account secret is marked for death";
0027             return;
0028         }
0029 
0030         m_stillAlive = false;
0031         Q_EMIT requestsCancelled();
0032     }
0033 
0034     bool AccountSecret::requestNewPassword(void)
0035     {
0036         if (!m_stillAlive) {
0037             qCDebug(logger) << "Ignoring request for 'new' password: account secret is marked for death";
0038             return false;
0039         }
0040 
0041         if (m_passwordRequested) {
0042             qCDebug(logger) << "Ignoring request for 'new' password: conflicting or duplicate request";
0043             return false;
0044         }
0045 
0046         qCDebug(logger) << "Emitting request for 'new' password";
0047         m_passwordRequested = true;
0048         m_newPassword = true;
0049         Q_EMIT newPasswordNeeded();
0050         return true;
0051     }
0052 
0053     bool AccountSecret::requestExistingPassword(const secrets::EncryptedSecret &challenge,
0054                                                 const QByteArray& salt, const secrets::KeyDerivationParameters &keyParams)
0055     {
0056         if (!m_stillAlive) {
0057             qCDebug(logger) << "Ignoring request for 'existing' password: account secret is marked for death";
0058             return false;
0059         }
0060 
0061         if (m_passwordRequested) {
0062             qCDebug(logger) << "Ignoring request for 'existing' password: conflicting or duplicate request";
0063             return false;
0064         }
0065 
0066         if (!secrets::SecureMasterKey::validate(keyParams)) {
0067             qCDebug(logger) << "Unable to request 'existing' password: invalid key derivation parameters";
0068             return false;
0069         }
0070 
0071         if (!secrets::SecureMasterKey::validate(salt)) {
0072             qCDebug(logger) << "Unable to request 'existing' password: invalid salt";
0073             return false;
0074         }
0075 
0076         qCDebug(logger) << "Emitting request for 'existing' password";
0077         m_passwordRequested = true;
0078         m_newPassword = false;
0079         m_keyParams.emplace(keyParams);
0080         m_salt.emplace(salt);
0081         m_challenge.emplace(challenge);
0082         Q_EMIT existingPasswordNeeded();
0083         return true;
0084     }
0085 
0086     // HACK: disables challenge verification, remove at some point!
0087     bool AccountSecret::requestExistingPassword(const QByteArray& salt, const secrets::KeyDerivationParameters &keyParams)
0088     {
0089         qCWarning(logger) << "HACK: running a 'silent' migration for legacy account setups without password challenge";
0090 
0091         if (!m_stillAlive) {
0092             qCDebug(logger) << "Ignoring request for 'existing' password: account secret is marked for death";
0093             return false;
0094         }
0095 
0096         if (m_passwordRequested) {
0097             qCDebug(logger) << "Ignoring request for 'existing' password: conflicting or duplicate request";
0098             return false;
0099         }
0100 
0101         if (!secrets::SecureMasterKey::validate(keyParams)) {
0102             qCDebug(logger) << "Unable to request 'existing' password: invalid key derivation parameters";
0103             return false;
0104         }
0105 
0106         if (!secrets::SecureMasterKey::validate(salt)) {
0107             qCDebug(logger) << "Unable to request 'existing' password: invalid salt";
0108             return false;
0109         }
0110 
0111         qCDebug(logger) << "Emitting request for 'existing' password";
0112         m_passwordRequested = true;
0113         m_newPassword = false;
0114         m_keyParams.emplace(keyParams);
0115         m_salt.emplace(salt);
0116         m_hackWithoutChallenge = true;
0117         Q_EMIT existingPasswordNeeded();
0118         return true;
0119     }
0120 
0121     bool AccountSecret::acceptPassword(QString &password, bool answerMatchesRequest)
0122     {
0123         QByteArray passwordBytes;
0124         if (!m_stillAlive) {
0125             qCDebug(logger) << "Ignoring password: account secret is marked for death";
0126             password.fill(QLatin1Char('*'), -1);
0127             return false;
0128         }
0129 
0130         if (!m_passwordRequested) {
0131             qCDebug(logger) << "Ignoring password: was not requested";
0132             password.fill(QLatin1Char('*'), -1);
0133             return false;
0134         }
0135 
0136         if (m_key || (m_password && !m_challenge)) {
0137             qCDebug(logger) << "Ignoring password: duplicate/conflicting password";
0138             password.fill(QLatin1Char('*'), -1);
0139             return false;
0140         }
0141 
0142         if (!answerMatchesRequest) {
0143             qCDebug(logger) << "Ignoring password: wrong answer function used for the request";
0144             password.fill(QLatin1Char('*'), -1);
0145             return false;
0146         }
0147 
0148         /*
0149          * This is still unfortunate: no idea how many (partial) copies toUtf8() makes.
0150          * I.e. no idea how many (partial) copies of the secret wind up floating around in memory.
0151          */
0152         passwordBytes = password.toUtf8();
0153         m_password.reset(secrets::SecureMemory::allocate((size_t) passwordBytes.size()));
0154 
0155         if (m_password) {
0156             qCDebug(logger) << "Accepted password for account secrets";
0157             std::memcpy(m_password->data(), passwordBytes.constData(), m_password->size());
0158         } else {
0159             qCDebug(logger) << "Failed to accept password for account secrets";
0160         }
0161 
0162         /*
0163          * Try and overwrite known copies of the password/secret (these are redundant now...)
0164          */
0165         passwordBytes.fill('\0', -1);
0166         password.fill(QLatin1Char('*'), -1);
0167         return !m_password.isNull();
0168     }
0169 
0170     bool AccountSecret::answerExistingPassword(QString &password)
0171     {
0172         // HACK: disables challenge verification, remove at some point!
0173         bool challengeOk = (m_challenge || m_hackWithoutChallenge);
0174         bool result = acceptPassword(password, m_keyParams && m_salt && challengeOk);
0175         if (result) {
0176             Q_EMIT passwordAvailable();
0177         }
0178         return result;
0179     }
0180 
0181     bool AccountSecret::answerNewPassword(QString &password, const secrets::KeyDerivationParameters &keyParams)
0182     {
0183         if (!secrets::SecureMasterKey::validate(keyParams)) {
0184             qCDebug(logger) << "Unable to accept 'existing' password: invalid key derivation parameters";
0185             password.fill(QLatin1Char('*'), -1);
0186             return false;
0187         }
0188 
0189         bool result = acceptPassword(password, !m_keyParams && !m_salt && !m_challenge);
0190         if (result) {
0191             m_keyParams.emplace(keyParams);
0192             Q_EMIT passwordAvailable();
0193         }
0194         return result;
0195     }
0196 
0197     bool AccountSecret::isStillAlive(void) const
0198     {
0199         return m_stillAlive;
0200     }
0201 
0202     bool AccountSecret::isNewPasswordRequested(void) const
0203     {
0204         return m_passwordRequested && m_newPassword;
0205     }
0206 
0207     bool AccountSecret::isExistingPasswordRequested(void) const
0208     {
0209         return m_passwordRequested && !m_newPassword;
0210     }
0211 
0212     bool AccountSecret::isKeyAvailable(void) const
0213     {
0214         return m_stillAlive && m_key;
0215     }
0216 
0217     bool AccountSecret::isPasswordAvailable(void) const
0218     {
0219         return m_stillAlive && m_password;
0220     }
0221 
0222     bool AccountSecret::isChallengeAvailable(void) const
0223     {
0224         return m_stillAlive && m_challenge;
0225     }
0226 
0227     secrets::SecureMasterKey * AccountSecret::deriveKey(void)
0228     {
0229         if (!m_stillAlive) {
0230             qCDebug(logger) << "Ignoring request to derive encryption/decryption key: account secret is marked for death";
0231             m_password.reset(nullptr);
0232             return nullptr;
0233         }
0234 
0235         if (m_key) {
0236             qCDebug(logger) << "Ignoring request to derive encryption/decryption key: duplicate request";
0237             m_password.reset(nullptr);
0238             return nullptr;
0239         }
0240 
0241         if (!m_passwordRequested || !m_keyParams || !m_password) {
0242             qCDebug(logger) << "Ignoring request to derive encryption/decryption key: passwor or key derivation parameters not available";
0243             m_password.reset(nullptr);
0244             return nullptr;
0245         }
0246 
0247         secrets::SecureMasterKey * derived = m_salt
0248             ? secrets::SecureMasterKey::derive(m_password.data(), *m_keyParams, *m_salt, m_random)
0249             : secrets::SecureMasterKey::derive(m_password.data(), *m_keyParams, m_random);
0250 
0251         if (!derived) {
0252             qCDebug(logger) << "Failed to derive encryption/decryption key for account secrets";
0253             m_password.reset(nullptr);
0254             Q_EMIT keyFailed();
0255             return nullptr;
0256         }
0257 
0258         if (m_challenge) {
0259             QScopedPointer<secrets::SecureMemory> result(derived->decrypt(*m_challenge));
0260             if (!result) {
0261                 qCDebug(logger) << "Failed to derive encryption/decryption key for account secrets: challenge failed";
0262                 m_password.reset(nullptr);
0263                 delete derived;
0264                 Q_EMIT keyFailed();
0265                 return nullptr;
0266             }
0267 
0268             bool sizeMismatch = result->size() != m_password->size();
0269             const unsigned char * const other = sizeMismatch ? m_password->constData() : result->constData();
0270             if (std::memcmp(m_password->constData(), other, m_password->size()) != 0 || sizeMismatch) {
0271                 qCDebug(logger) << "Failed to derive encryption/decryption key for account secrets: challenge failed";
0272                 m_password.reset(nullptr);
0273                 delete derived;
0274                 Q_EMIT keyFailed();
0275                 return nullptr;
0276             }
0277         } else {
0278             std::optional<secrets::EncryptedSecret> challenge = derived->encrypt(m_password.data());
0279             if (!challenge) {
0280                 qCDebug(logger) << "Failed to derive encryption/decryption key for account secrets: unable to generate challenge";
0281                 m_password.reset(nullptr);
0282                 delete derived;
0283                 Q_EMIT keyFailed();
0284                 return nullptr;
0285             }
0286             m_challenge.emplace(*challenge);
0287         }
0288 
0289         qCDebug(logger) << "Successfully derived encryption/decryption key for account secrets";
0290         m_key.reset(derived);
0291         m_salt.emplace(m_key->salt());
0292         m_password.reset(nullptr);
0293         Q_EMIT keyAvailable();
0294         return m_key.data();
0295     }
0296 
0297     secrets::SecureMasterKey * AccountSecret::key(void) const
0298     {
0299         return isKeyAvailable() ? m_key.data() : nullptr;
0300     }
0301 
0302     std::optional<secrets::EncryptedSecret> AccountSecret::challenge(void) const
0303     {
0304         return isChallengeAvailable() ? m_challenge : std::nullopt;
0305     }
0306 
0307     std::optional<secrets::EncryptedSecret> AccountSecret::encrypt(const secrets::SecureMemory *secret) const
0308     {
0309         secrets::SecureMasterKey *k = key();
0310         if (!k) {
0311             qCDebug(logger) << "Unable to encrypt secret: encryption key not available";
0312             return std::nullopt;
0313         }
0314 
0315         return k->encrypt(secret);
0316     }
0317 
0318     secrets::SecureMemory * AccountSecret::decrypt(const secrets::EncryptedSecret &secret) const
0319     {
0320         secrets::SecureMasterKey *k = key();
0321         if (!k) {
0322             qCDebug(logger) << "Unable to decrypt secret: decryption key not available";
0323             return  nullptr;
0324         }
0325 
0326         return k->decrypt(secret);
0327     }
0328 }