File indexing completed on 2024-05-05 05:53:13

0001 /*
0002  * SPDX-License-Identifier: GPL-3.0-or-later
0003  * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
0004  */
0005 #include "hmac.h"
0006 #include "../logging_p.h"
0007 
0008 KEYSMITH_LOGGER(logger, ".hmac")
0009 
0010 static QByteArray hmac_stage(QCryptographicHash::Algorithm algorithm, char * const keyBuf, int ksize, int blockSize, const char fillPad, const char xorKey, const QByteArray &message)
0011 {
0012     QCryptographicHash hash(algorithm);
0013     int count;
0014     for (count = 0; count < ksize; ++count) {
0015         keyBuf[count] ^= xorKey;
0016     }
0017 
0018     hash.addData(keyBuf, count);
0019 
0020     if (ksize < blockSize) {
0021         QByteArray pad(blockSize - ksize, fillPad);
0022         hash.addData(pad);
0023     }
0024 
0025     hash.addData(message);
0026 
0027     return hash.result();
0028 }
0029 
0030 namespace hmac
0031 {
0032     std::optional<int> blockSize(QCryptographicHash::Algorithm algorithm)
0033     {
0034         switch (algorithm) {
0035         case QCryptographicHash::Md4:
0036         case QCryptographicHash::Md5:
0037         case QCryptographicHash::Sha1:
0038         case QCryptographicHash::Sha224:
0039         case QCryptographicHash::Sha256:
0040             return std::optional<int>(64ULL);
0041         case QCryptographicHash::Sha384:
0042         case QCryptographicHash::Sha512:
0043             return std::optional<int>(128ULL);
0044         case QCryptographicHash::RealSha3_224:
0045         case QCryptographicHash::Keccak_224:
0046             return std::optional<int>(144ULL);
0047         case QCryptographicHash::RealSha3_256:
0048         case QCryptographicHash::Keccak_256:
0049             return std::optional<int>(136ULL);
0050         case QCryptographicHash::RealSha3_384:
0051         case QCryptographicHash::Keccak_384:
0052             return std::optional<int>(104ULL);
0053         case QCryptographicHash::RealSha3_512:
0054         case QCryptographicHash::Keccak_512:
0055             return std::optional<int>(72ULL);
0056         default:
0057             return std::nullopt;
0058         }
0059     }
0060 
0061     std::optional<uint> outputSize(QCryptographicHash::Algorithm algorithm)
0062     {
0063         switch (algorithm) {
0064         case QCryptographicHash::Md4:
0065         case QCryptographicHash::Md5:
0066             return std::optional<uint>(16ULL);
0067         case QCryptographicHash::Sha1:
0068             return std::optional<uint>(20ULL);
0069         case QCryptographicHash::Sha224:
0070         case QCryptographicHash::RealSha3_224:
0071         case QCryptographicHash::Keccak_224:
0072             return std::optional<int>(28UL);
0073         case QCryptographicHash::Sha256:
0074         case QCryptographicHash::RealSha3_256:
0075         case QCryptographicHash::Keccak_256:
0076             return std::optional<int>(32UL);
0077         case QCryptographicHash::Sha384:
0078         case QCryptographicHash::RealSha3_384:
0079         case QCryptographicHash::Keccak_384:
0080             return std::optional<int>(48UL);
0081         case QCryptographicHash::Sha512:
0082         case QCryptographicHash::RealSha3_512:
0083         case QCryptographicHash::Keccak_512:
0084             return std::optional<uint>(64UL);
0085         default:
0086             return std::nullopt;
0087         }
0088     }
0089 
0090     bool validateKeySize(QCryptographicHash::Algorithm algorithm, int ksize, bool requireSaneKeyLength)
0091     {
0092         std::optional<int> maybeOutputSize = outputSize(algorithm);
0093         return maybeOutputSize && ksize >= 0 && (ksize >= (*maybeOutputSize) || !requireSaneKeyLength);
0094     }
0095 
0096     std::optional<QByteArray> compute(QCryptographicHash::Algorithm algorithm, char * rawKey, int ksize, const QByteArray &message, bool requireSaneKeyLength)
0097     {
0098         if (!rawKey) {
0099             return std::nullopt;
0100         }
0101 
0102         std::optional<int> maybeBlockSize = blockSize(algorithm);
0103         if (!maybeBlockSize) {
0104             qCDebug(logger) << "Unable to determine block size for hashing algorithm:" << algorithm;
0105             return std::nullopt;
0106         }
0107 
0108         if (!validateKeySize(algorithm, ksize, requireSaneKeyLength)) {
0109             qCDebug(logger)
0110                 << "Invalid HMAC key size:" << ksize << "for hashing algorithm:" << algorithm
0111                 << "Sane key length requirements apply:" << requireSaneKeyLength;
0112             return std::nullopt;
0113         }
0114 
0115         int bs = *maybeBlockSize;
0116         QByteArray hashedKey;
0117 
0118         if (ksize > bs) {
0119             QCryptographicHash khash(algorithm);
0120             khash.addData(rawKey, ksize);
0121             hashedKey = khash.result();
0122             rawKey = hashedKey.data();
0123             ksize = hashedKey.size();
0124         }
0125 
0126         QByteArray inner = hmac_stage(algorithm, rawKey, ksize, bs, '\x36', '\x36', message);
0127         QByteArray result = hmac_stage(algorithm, rawKey, ksize, bs, '\x5c', '\x36' ^ '\x5c', inner);
0128 
0129         hashedKey.fill('\x0');
0130         return std::optional<QByteArray>(result);
0131     }
0132 }