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 }