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 &params)
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 &params, 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 &params, 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 &params, 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 }