File indexing completed on 2024-05-05 05:53:14
0001 /* 0002 * SPDX-License-Identifier: GPL-3.0-or-later 0003 * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> 0004 */ 0005 #include "secrets.h" 0006 0007 #include "../base32/base32.h" 0008 #include "../logging_p.h" 0009 0010 #include <QCryptographicHash> 0011 0012 #include <limits> 0013 0014 KEYSMITH_LOGGER(logger, ".secrets") 0015 0016 namespace secrets 0017 { 0018 0019 std::optional<EncryptedSecret> EncryptedSecret::from(const QByteArray& cryptText, const QByteArray& nonce) 0020 { 0021 if (cryptText.size() < 0 || ((uint) cryptText.size()) <= crypto_secretbox_MACBYTES) { 0022 qCDebug(logger) << "Invalid ciphertext: too short, expected at least:" << crypto_secretbox_MACBYTES << "bytes but got:" << cryptText.size(); 0023 return std::nullopt; 0024 } 0025 0026 if (nonce.size() != crypto_secretbox_NONCEBYTES) { 0027 qCDebug(logger) << "Invalid nonce: expected exactly:" << crypto_secretbox_NONCEBYTES << "but got:" << nonce.size(); 0028 return std::nullopt; 0029 } 0030 0031 return std::optional<EncryptedSecret>({cryptText, nonce}); 0032 } 0033 0034 unsigned long long EncryptedSecret::messageLength(void) const 0035 { 0036 return m_taggedCryptText.size() - crypto_secretbox_MACBYTES; 0037 } 0038 0039 const QByteArray& EncryptedSecret::cryptText(void) const 0040 { 0041 return m_taggedCryptText; 0042 } 0043 0044 const QByteArray& EncryptedSecret::nonce(void) const 0045 { 0046 return m_nonce; 0047 } 0048 0049 EncryptedSecret::EncryptedSecret(const QByteArray &taggedCryptText, const QByteArray &nonce) : m_taggedCryptText(taggedCryptText), m_nonce(nonce) 0050 { 0051 } 0052 0053 std::optional<KeyDerivationParameters> KeyDerivationParameters::create(unsigned long long keyLength, int algorithm, size_t memoryCost, unsigned long long cpuCost) 0054 { 0055 switch (algorithm) 0056 { 0057 case crypto_pwhash_ALG_ARGON2I13: 0058 case crypto_pwhash_ALG_ARGON2ID13: 0059 break; 0060 default: 0061 qCDebug(logger) << "Unable to construct key derivation parameters: unkown algorithm:" << algorithm; 0062 return std::nullopt; 0063 } 0064 0065 if (keyLength < crypto_pwhash_BYTES_MIN || keyLength > crypto_pwhash_BYTES_MAX || 0066 cpuCost < crypto_pwhash_OPSLIMIT_MIN || cpuCost > crypto_pwhash_OPSLIMIT_MAX || 0067 memoryCost < crypto_pwhash_MEMLIMIT_MIN || memoryCost > crypto_pwhash_MEMLIMIT_MAX) { 0068 return std::nullopt; 0069 } 0070 0071 return std::optional<KeyDerivationParameters>({algorithm, keyLength, memoryCost, cpuCost}); 0072 } 0073 0074 int KeyDerivationParameters::algorithm(void) const 0075 { 0076 return m_algorithm; 0077 } 0078 0079 unsigned long long KeyDerivationParameters::keyLength(void) const 0080 { 0081 return m_keyLength; 0082 } 0083 0084 size_t KeyDerivationParameters::memoryCost(void) const 0085 { 0086 return m_memoryCost; 0087 } 0088 0089 unsigned long long KeyDerivationParameters::cpuCost(void) const 0090 { 0091 return m_cpuCost; 0092 } 0093 0094 KeyDerivationParameters::KeyDerivationParameters(int algorithm, unsigned long long keyLength, size_t memoryCost, unsigned long long cpuCost) : m_algorithm(algorithm), m_keyLength(keyLength), m_memoryCost(memoryCost), m_cpuCost(cpuCost) 0095 { 0096 } 0097 0098 SecureMemory * SecureMemory::allocate(size_t size) 0099 { 0100 if (sodium_init() < 0) { 0101 qCDebug(logger) << "Unable to allocate secure memory region: libsodium failed to initialise"; 0102 return nullptr; 0103 } 0104 0105 if (size == 0ULL) { 0106 qCDebug(logger) << "Rejecting attempt to allocate empty secure memory region"; 0107 return nullptr; 0108 } 0109 0110 void * memory = sodium_malloc(size); 0111 if (memory) { 0112 return new SecureMemory(memory, size); 0113 } 0114 0115 qCDebug(logger) << "Failed to allocate secure memory region"; 0116 return nullptr; 0117 } 0118 0119 size_t SecureMemory::size(void) const 0120 { 0121 return m_size; 0122 } 0123 0124 const unsigned char * SecureMemory::constData(void) const 0125 { 0126 return m_secureMemory; 0127 } 0128 0129 unsigned char * SecureMemory::data(void) 0130 { 0131 return m_secureMemory; 0132 } 0133 0134 SecureMemory::~SecureMemory() 0135 { 0136 sodium_free(m_secureMemory); 0137 } 0138 0139 SecureMemory::SecureMemory(void *memory, size_t size) : m_secureMemory(reinterpret_cast<unsigned char*>(memory)), m_size(size) 0140 { 0141 } 0142 0143 bool SecureMasterKey::validate(const SecureMemory *secret) 0144 { 0145 static const int max = std::numeric_limits<int>::max() - crypto_secretbox_MACBYTES; 0146 return max > 0 && secret->size() > 0ULL && secret->size() <= max; 0147 } 0148 0149 bool SecureMasterKey::validate(const KeyDerivationParameters ¶ms) 0150 { 0151 return params.keyLength() == crypto_secretbox_KEYBYTES; 0152 } 0153 0154 bool SecureMasterKey::validate(const QByteArray &salt) 0155 { 0156 return salt.size() == crypto_pwhash_SALTBYTES; 0157 } 0158 0159 SecureMasterKey * SecureMasterKey::derive(const SecureMemory *password, const KeyDerivationParameters ¶ms, const SecureRandom &random) 0160 { 0161 QByteArray salt; 0162 0163 if (!password) { 0164 qCDebug(logger) << "Unable to derive a key: no password given"; 0165 return nullptr; 0166 } 0167 if (sodium_init() < 0) { 0168 qCDebug(logger) << "Unable to derive a key: libsodium failed to initialise"; 0169 return nullptr; 0170 } 0171 0172 salt.reserve(crypto_pwhash_SALTBYTES); 0173 salt.resize(crypto_pwhash_SALTBYTES); 0174 0175 if (!random(salt.data(), crypto_pwhash_SALTBYTES)) { 0176 qCDebug(logger) << "Unable to derive a key: failed to generate the random nonce"; 0177 return nullptr; 0178 } 0179 0180 return derive(password, params, salt, random); 0181 } 0182 0183 SecureMasterKey * SecureMasterKey::derive(const SecureMemory *password, const KeyDerivationParameters ¶ms, const QByteArray &salt, const SecureRandom &random) 0184 { 0185 if (!password) { 0186 qCDebug(logger) << "Unable to derive a key: no password given"; 0187 return nullptr; 0188 } 0189 if (sodium_init() < 0) { 0190 qCDebug(logger) << "Unable to derive a key: libsodium failed to initialise"; 0191 return nullptr; 0192 } 0193 if (!validate(params)) { 0194 qCDebug(logger) << "Unable to derive a key: invalid key derivation parameters"; 0195 return nullptr; 0196 } 0197 if (!validate(salt)) { 0198 qCDebug(logger) << "Unable to derive a key: invalid salt"; 0199 return nullptr; 0200 } 0201 0202 void * memory = sodium_malloc((size_t) params.keyLength()); 0203 if (!memory) { 0204 qCDebug(logger) << "Unable to derive a key: failed to allocate secure memory region"; 0205 return nullptr; 0206 } 0207 0208 unsigned char * derived = reinterpret_cast<unsigned char*>(memory); 0209 const unsigned char *saltData = reinterpret_cast<const unsigned char*>(salt.constData()); 0210 const char *passwordData = reinterpret_cast<const char*>(password->constData()); 0211 if (crypto_pwhash(derived, params.keyLength(), passwordData, password->size(), saltData, params.cpuCost(), params.memoryCost(), params.algorithm()) == 0) { 0212 return new SecureMasterKey(derived, params, salt, random); 0213 } 0214 0215 qCDebug(logger) << "Failed to derive a key: password hashing failed"; 0216 sodium_free(memory); 0217 return nullptr; 0218 } 0219 0220 const KeyDerivationParameters SecureMasterKey::params(void) const 0221 { 0222 return m_params; 0223 } 0224 0225 const QByteArray SecureMasterKey::salt(void) const 0226 { 0227 return m_salt; 0228 } 0229 0230 std::optional<EncryptedSecret> SecureMasterKey::encrypt(const SecureMemory *secret) const 0231 { 0232 QByteArray cryptText; 0233 QByteArray nonce; 0234 0235 if (!validate(secret)) { 0236 qCDebug(logger) << "Unable to encrypt secret: invalid input"; 0237 return std::nullopt; 0238 } 0239 0240 cryptText.reserve(((int) secret->size()) + crypto_secretbox_MACBYTES); 0241 cryptText.resize(((int) secret->size()) + crypto_secretbox_MACBYTES); 0242 nonce.reserve(crypto_secretbox_NONCEBYTES); 0243 nonce.resize(crypto_secretbox_NONCEBYTES); 0244 0245 if (!m_secureRandom(nonce.data(), crypto_secretbox_NONCEBYTES)) { 0246 qCDebug(logger) << "Unable to encrypt secret: failed to generate the random nonce"; 0247 return std::nullopt; 0248 } 0249 0250 unsigned char *encrypted = reinterpret_cast<unsigned char*>(cryptText.data()); 0251 const unsigned char *nonceData = reinterpret_cast<const unsigned char*>(nonce.constData()); 0252 if (crypto_secretbox_easy(encrypted, secret->constData(), (unsigned long long) secret->size(), nonceData, m_secureKeyMemory) == 0) { 0253 return EncryptedSecret::from(cryptText, nonce); 0254 } 0255 0256 qCDebug(logger) << "Failed to encrypt secret"; 0257 return std::nullopt; 0258 } 0259 0260 SecureMemory * SecureMasterKey::decrypt(const EncryptedSecret &secret) const 0261 { 0262 SecureMemory *result = SecureMemory::allocate(secret.messageLength()); 0263 if (!result) { 0264 qCDebug(logger) << "Unable to decrypt secret: failed to allocate secure memory region"; 0265 return nullptr; 0266 } 0267 0268 unsigned char *decrypted = reinterpret_cast<unsigned char*>(result->data()); 0269 const unsigned char *cryptText = reinterpret_cast<const unsigned char*>(secret.cryptText().constData()); 0270 const unsigned char *nonce = reinterpret_cast<const unsigned char*>(secret.nonce().constData()); 0271 if (crypto_secretbox_open_easy(decrypted, cryptText, (unsigned long long) secret.cryptText().size(), nonce, m_secureKeyMemory) == 0) { 0272 return result; 0273 } 0274 0275 qCDebug(logger) << "Failed to decrypt secret"; 0276 delete result; 0277 return nullptr; 0278 } 0279 0280 SecureMasterKey::~SecureMasterKey() 0281 { 0282 sodium_free(m_secureKeyMemory); 0283 } 0284 0285 SecureMasterKey::SecureMasterKey(unsigned char *keyMemory, const KeyDerivationParameters ¶ms, const QByteArray &salt, const SecureRandom &random) : 0286 m_secureKeyMemory(keyMemory), m_params(params), m_salt(salt), m_secureRandom(random) 0287 { 0288 } 0289 0290 bool defaultSecureRandom(void *data, size_t size) 0291 { 0292 if (sodium_init() < 0) { 0293 qCDebug(logger) << "Unable to fill buffer with random bytes: libsodium failed to initialise"; 0294 return false; 0295 } 0296 0297 randombytes_buf(data, size); 0298 return true; 0299 } 0300 0301 SecureMemory * decodeBase32(const QString &encoded, int from, int until) 0302 { 0303 std::optional<size_t> size = base32::validate(encoded, from, until); 0304 0305 if (!size) { 0306 return nullptr; 0307 } 0308 0309 SecureMemory *result = SecureMemory::allocate(*size); 0310 if (!result) { 0311 qCDebug(logger) << "Unable to decoded base32 secrets: failed to allocate secure memory"; 0312 return nullptr; 0313 } 0314 0315 std::optional<size_t> decoded = base32::decode(encoded, reinterpret_cast<char*>(result->data()), result->size(), from, until); 0316 if (decoded && *decoded == *size) { 0317 return result; 0318 } 0319 0320 Q_ASSERT_X(decoded, Q_FUNC_INFO, "invalid base32 should have been caught by prior validation"); 0321 Q_ASSERT_X(*decoded == *size, Q_FUNC_INFO, "the entire base32 input should have been decoded"); 0322 delete result; 0323 return nullptr; 0324 } 0325 }