Descent3/md5/md5.cpp

193 lines
6.4 KiB
C++
Raw Permalink Normal View History

/* Descent 3
* Copyright (C) 2024 Descent Developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2024-04-16 20:21:35 +00:00
#include "md5.h"
#include <byteswap.h>
2024-04-16 20:21:35 +00:00
#include <algorithm>
2024-04-16 20:21:35 +00:00
namespace {
2024-04-16 20:21:35 +00:00
2024-05-24 02:51:16 +00:00
const uint32_t md5_round_shifts[] = {7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21};
2024-04-16 20:21:35 +00:00
const std::uint32_t md5_round_constants[] = {
0xD76AA478UL, 0xE8C7B756UL, 0x242070DBUL, 0xC1BDCEEEUL, 0xF57C0FAFUL, 0x4787C62AUL, 0xA8304613UL, 0xFD469501UL,
0x698098D8UL, 0x8B44F7AFUL, 0xFFFF5BB1UL, 0x895CD7BEUL, 0x6B901122UL, 0xFD987193UL, 0xA679438EUL, 0x49B40821UL,
0xF61E2562UL, 0xC040B340UL, 0x265E5A51UL, 0xE9B6C7AAUL, 0xD62F105DUL, 0x02441453UL, 0xD8A1E681UL, 0xE7D3FBC8UL,
0x21E1CDE6UL, 0xC33707D6UL, 0xF4D50D87UL, 0x455A14EDUL, 0xA9E3E905UL, 0xFCEFA3F8UL, 0x676F02D9UL, 0x8D2A4C8AUL,
0xFFFA3942UL, 0x8771F681UL, 0x6D9D6122UL, 0xFDE5380CUL, 0xA4BEEA44UL, 0x4BDECFA9UL, 0xF6BB4B60UL, 0xBEBFBC70UL,
0x289B7EC6UL, 0xEAA127FAUL, 0xD4EF3085UL, 0x04881D05UL, 0xD9D4D039UL, 0xE6DB99E5UL, 0x1FA27CF8UL, 0xC4AC5665UL,
0xF4292244UL, 0x432AFF97UL, 0xAB9423A7UL, 0xFC93A039UL, 0x655B59C3UL, 0x8F0CCC92UL, 0xFFEFF47DUL, 0x85845DD1UL,
0x6FA87E4FUL, 0xFE2CE6E0UL, 0xA3014314UL, 0x4E0811A1UL, 0xF7537E82UL, 0xBD3AF235UL, 0x2AD7D2BBUL, 0xEB86D391UL};
2024-04-16 20:21:35 +00:00
std::uint32_t rotl_uint32(std::uint32_t a, std::uint32_t b) noexcept { return b ? (a << b) | (a >> (32 - b)) : a; };
2024-04-16 20:21:35 +00:00
}; // namespace
2024-04-16 20:21:35 +00:00
2024-05-24 02:58:46 +00:00
void MD5::round(std::array<std::uint32_t, 4> &sums, const uint8_t *block) const noexcept {
// break 64 bytes into 16 dwords
std::uint32_t m[16];
for (std::size_t i = 0; i < 16; ++i) {
m[i] = (static_cast<std::uint32_t>(block[(4 * i)])) | (static_cast<std::uint32_t>(block[(4 * i) + 1]) << 8) |
(static_cast<std::uint32_t>(block[(4 * i) + 2]) << 16) |
(static_cast<std::uint32_t>(block[(4 * i) + 3]) << 24);
2024-04-16 20:21:35 +00:00
}
std::uint32_t a = sums[0], b = sums[1], c = sums[2], d = sums[3];
// 64 rounds...
2024-05-24 02:51:16 +00:00
for (uint32_t r = 0; r < 64; ++r) {
std::uint32_t f, g;
2024-05-24 02:51:16 +00:00
uint32_t s = md5_round_shifts[((r >> 2) & ~3) | (r & 3)];
switch (r >> 4) {
case 0:
f = (b & c) | (~b & d), g = r;
break;
case 1:
f = (b & d) | (c & ~d), g = (5 * r + 1) & 15;
break;
case 2:
f = b ^ c ^ d, g = (3 * r + 5) & 15;
break;
case 3:
f = c ^ (b | ~d), g = (7 * r) & 15;
break;
default:; // unreachable
2024-04-16 20:21:35 +00:00
}
f = rotl_uint32(f + a + m[g] + md5_round_constants[r], s) + b;
a = d;
d = c;
c = b;
b = f;
2024-04-16 20:21:35 +00:00
}
sums[0] += a, sums[1] += b, sums[2] += c, sums[3] += d;
}
2024-04-16 20:21:35 +00:00
2024-05-24 02:58:46 +00:00
void MD5::round(const uint8_t *block) noexcept { round(sums_, block); }
2024-04-16 20:21:35 +00:00
2024-05-24 02:58:46 +00:00
void MD5::update(const uint8_t *data, std::size_t n) noexcept {
message_len_ += n * 8;
2024-04-16 20:21:35 +00:00
// if there is already some data in tmpbuf, check if we would fill it
if (tmpbuf_n_) {
if ((n >= tmpbuf_.size() || tmpbuf_n_ + n >= tmpbuf_.size())) {
// we would fill tmpbuf at least once; handle partial block first.
std::size_t remaining = tmpbuf_.size() - tmpbuf_n_;
std::copy(data, data + remaining, tmpbuf_.begin() + tmpbuf_n_);
data += remaining;
n -= remaining;
round(tmpbuf_.data());
tmpbuf_n_ = 0;
} else {
// we will not fill the buffer - just copy and return
std::copy(data, data + n, tmpbuf_.begin() + tmpbuf_n_);
tmpbuf_n_ += n;
2024-04-16 20:21:35 +00:00
return;
}
}
// do rounds on full blocks for as long as we can
while (n >= tmpbuf_.size()) {
round(data);
data += tmpbuf_.size();
n -= tmpbuf_.size();
2024-04-16 20:21:35 +00:00
}
if (n) {
// copy partial block
std::copy(data, data + n, tmpbuf_.begin());
tmpbuf_n_ = n;
2024-04-16 20:21:35 +00:00
}
}
2024-05-24 02:58:46 +00:00
void MD5::place_length(uint8_t *destination) const noexcept {
for (std::size_t i = 0; i < 8; ++i)
2024-05-24 02:58:46 +00:00
destination[i] = static_cast<uint8_t>(message_len_ >> (8 * i));
}
2024-04-16 20:21:35 +00:00
2024-05-24 02:58:46 +00:00
std::array<uint8_t, 16> MD5::digest() const noexcept {
// copies of sums and buffers
auto sums = sums_;
auto buf = tmpbuf_;
std::size_t n = tmpbuf_n_;
2024-04-16 20:21:35 +00:00
2024-05-08 17:58:59 +00:00
if (n >= 56) {
// must append current block, required padding bit and length won't fit.
// n is never buf.size() (= 64) yet
buf[n++] = 0x80U;
while (n < buf.size())
buf[n++] = 0;
round(sums, buf.data());
2024-04-16 20:21:35 +00:00
// all zeroes, except for length
std::fill(buf.begin(), buf.begin() + 56, 0);
2024-04-16 20:21:35 +00:00
2024-05-08 17:58:59 +00:00
} else { // n < 56
// append padding to tmpbuf and then the length
buf[n++] = 0x80U;
while (n < 56)
buf[n++] = 0;
}
2024-04-16 20:21:35 +00:00
place_length(&buf[56]);
round(sums, buf.data());
2024-04-16 20:21:35 +00:00
2024-05-24 02:58:46 +00:00
return {static_cast<uint8_t>(sums[0]), static_cast<uint8_t>(sums[0] >> 8),
static_cast<uint8_t>(sums[0] >> 16), static_cast<uint8_t>(sums[0] >> 24),
static_cast<uint8_t>(sums[1]), static_cast<uint8_t>(sums[1] >> 8),
static_cast<uint8_t>(sums[1] >> 16), static_cast<uint8_t>(sums[1] >> 24),
static_cast<uint8_t>(sums[2]), static_cast<uint8_t>(sums[2] >> 8),
static_cast<uint8_t>(sums[2] >> 16), static_cast<uint8_t>(sums[2] >> 24),
static_cast<uint8_t>(sums[3]), static_cast<uint8_t>(sums[3] >> 8),
static_cast<uint8_t>(sums[3] >> 16), static_cast<uint8_t>(sums[3] >> 24)};
2024-04-16 20:21:35 +00:00
}
void MD5::update(float valin) noexcept {
float val = INTEL_FLOAT(valin);
2024-05-24 02:58:46 +00:00
uint8_t *p = (uint8_t *)&val;
update(p, sizeof(float));
2024-04-16 20:21:35 +00:00
}
void MD5::update(int valin) noexcept {
int val = INTEL_INT(valin);
2024-05-24 02:58:46 +00:00
uint8_t *p = (uint8_t *)&val;
update(p, sizeof(int));
2024-04-16 20:21:35 +00:00
}
void MD5::update(int16_t valin) noexcept {
int16_t val = INTEL_SHORT(valin);
2024-05-24 02:58:46 +00:00
uint8_t *p = (uint8_t *)&val;
update(p, sizeof(int16_t));
2024-04-16 20:21:35 +00:00
}
2024-05-24 02:51:16 +00:00
void MD5::update(uint32_t valin) noexcept {
uint32_t val = INTEL_INT(valin);
2024-05-24 02:58:46 +00:00
uint8_t *p = (uint8_t *)&val;
2024-05-24 02:51:16 +00:00
update(p, sizeof(uint32_t));
2024-04-16 20:21:35 +00:00
}
2024-05-24 02:58:46 +00:00
void MD5::update(uint8_t val) noexcept {
uint8_t *p = (uint8_t *)&val;
update(p, sizeof(uint8_t));
2024-04-16 20:21:35 +00:00
}
2024-05-24 02:58:46 +00:00
void MD5::digest(uint8_t *destination) const noexcept {
auto digest_data = digest();
std::copy(digest_data.begin(), digest_data.end(), destination);
}