replace md5 with custom implementation, with tests

This commit is contained in:
ziplantil 2024-05-08 20:19:04 +03:00
parent 6a3e3060ed
commit 93ea1b7d42
8 changed files with 293 additions and 507 deletions

View File

@ -672,58 +672,57 @@ inline void RestartLevelMD5() {
if (Level_md5) if (Level_md5)
delete Level_md5; delete Level_md5;
Level_md5 = new MD5(); Level_md5 = new MD5();
Level_md5->MD5Init();
} }
inline void AppendToLevelChecksum(int val) { inline void AppendToLevelChecksum(int val) {
if (!Level_md5) { if (!Level_md5) {
return; return;
} }
Level_md5->MD5Update(val); Level_md5->update(val);
} }
inline void AppendToLevelChecksum(unsigned int val) { inline void AppendToLevelChecksum(unsigned int val) {
if (!Level_md5) { if (!Level_md5) {
return; return;
} }
Level_md5->MD5Update(val); Level_md5->update(val);
} }
inline void AppendToLevelChecksum(unsigned short val) { inline void AppendToLevelChecksum(unsigned short val) {
if (!Level_md5) { if (!Level_md5) {
return; return;
} }
Level_md5->MD5Update(val); Level_md5->update(val);
} }
inline void AppendToLevelChecksum(short val) { inline void AppendToLevelChecksum(short val) {
if (!Level_md5) { if (!Level_md5) {
return; return;
} }
Level_md5->MD5Update(val); Level_md5->update(val);
} }
inline void AppendToLevelChecksum(float val) { inline void AppendToLevelChecksum(float val) {
if (!Level_md5) { if (!Level_md5) {
return; return;
} }
Level_md5->MD5Update(val); Level_md5->update(val);
} }
inline void AppendToLevelChecksum(vector val) { inline void AppendToLevelChecksum(vector val) {
if (!Level_md5) { if (!Level_md5) {
return; return;
} }
Level_md5->MD5Update(val.x); Level_md5->update(val.x);
Level_md5->MD5Update(val.y); Level_md5->update(val.y);
Level_md5->MD5Update(val.z); Level_md5->update(val.z);
} }
inline void AppendToLevelChecksum(unsigned char val) { inline void AppendToLevelChecksum(unsigned char val) {
if (!Level_md5) { if (!Level_md5) {
return; return;
} }
Level_md5->MD5Update(val); Level_md5->update(val);
} }
inline void GetLevelMD5Sum(unsigned char digest[16]) { inline void GetLevelMD5Sum(unsigned char digest[16]) {
@ -732,24 +731,18 @@ inline void GetLevelMD5Sum(unsigned char digest[16]) {
digest[i] = 0; digest[i] = 0;
return; return;
} }
Level_md5->MD5Final(digest); Level_md5->digest(digest);
} }
#include <string.h> #include <string.h>
inline char *GetCurrentSumString() { inline char *GetCurrentSumString() {
static char output_buf[100]; static char output_buf[100];
output_buf[0] = '\0'; output_buf[0] = '\0';
// Make a copy of the context so we don't mess auto digest = Level_md5->digest();
// up an in progress md5 sum.
MD5 *checksum = Level_md5->Clone();
unsigned char digest[16];
checksum->MD5Final(digest);
char bytestr[10] = ""; char bytestr[10] = "";
// Do level checksum // Do level checksum
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
snprintf(bytestr, sizeof(bytestr), "%.2x", digest[i]); snprintf(bytestr, sizeof(bytestr), "%.2x", digest[i]);
strcat(output_buf, bytestr); strcat(output_buf, bytestr);
} }
MD5::Destroy(checksum);
return output_buf; return output_buf;
} }

View File

@ -2982,18 +2982,17 @@ void MultiDoDoneObjects(ubyte *data) {
// Skip header stuff // Skip header stuff
SKIP_HEADER(data, &count); SKIP_HEADER(data, &count);
// Get the salt. // Get the salt.
MD5 *playermd5 = Level_md5->Clone(); MD5 playermd5(*Level_md5);
// Get the salt value from the server. // Get the salt value from the server.
int salt = MultiGetInt(data, &count); int salt = MultiGetInt(data, &count);
// process the salt through the md5 // process the salt through the md5
playermd5->MD5Update(salt); playermd5.update(salt);
// process the ships through the md5 // process the ships through the md5
for (int i = 0; i < MAX_SHIPS; i++) for (int i = 0; i < MAX_SHIPS; i++)
if (Ships[i].used) if (Ships[i].used)
MultiProcessShipChecksum(playermd5, i); MultiProcessShipChecksum(&playermd5, i);
// save the digest value to send to the server // save the digest value to send to the server
playermd5->MD5Final(NetPlayers[Player_num].digest); playermd5.digest(NetPlayers[Player_num].digest);
MD5::Destroy(playermd5);
NetPlayers[Player_num].sequence = NETSEQ_REQUEST_WORLD; NetPlayers[Player_num].sequence = NETSEQ_REQUEST_WORLD;

View File

@ -693,27 +693,27 @@ int MultiGetShipChecksum(int ship_index) {
void MultiProcessShipChecksum(MD5 *md5, int ship_index) { void MultiProcessShipChecksum(MD5 *md5, int ship_index) {
ship *s = &Ships[ship_index]; ship *s = &Ships[ship_index];
md5->MD5Update(s->phys_info.full_thrust); md5->update(s->phys_info.full_thrust);
md5->MD5Update(s->phys_info.full_rotthrust); md5->update(s->phys_info.full_rotthrust);
md5->MD5Update(s->phys_info.mass); md5->update(s->phys_info.mass);
md5->MD5Update(s->phys_info.drag); md5->update(s->phys_info.drag);
md5->MD5Update(s->phys_info.rotdrag); md5->update(s->phys_info.rotdrag);
md5->MD5Update(s->phys_info.flags); md5->update(s->phys_info.flags);
poly_model *pm = GetPolymodelPointer(s->model_handle); poly_model *pm = GetPolymodelPointer(s->model_handle);
md5->MD5Update(pm->rad); md5->update(pm->rad);
for (int w = 0; w < MAX_PLAYER_WEAPONS; w++) { for (int w = 0; w < MAX_PLAYER_WEAPONS; w++) {
for (int i = 0; i < MAX_WB_FIRING_MASKS; i++) { for (int i = 0; i < MAX_WB_FIRING_MASKS; i++) {
md5->MD5Update(s->static_wb[w].gp_fire_wait[i]); md5->update(s->static_wb[w].gp_fire_wait[i]);
md5->MD5Update(s->static_wb[w].gp_fire_masks[i]); md5->update(s->static_wb[w].gp_fire_masks[i]);
} }
md5->MD5Update(s->static_wb[w].energy_usage); md5->update(s->static_wb[w].energy_usage);
md5->MD5Update(s->static_wb[w].ammo_usage); md5->update(s->static_wb[w].ammo_usage);
md5->MD5Update(s->fire_flags[w]); md5->update(s->fire_flags[w]);
} }
} }

View File

@ -972,18 +972,17 @@ void MultiSendDoneObjects(int slot) {
mprintf((0, "Sending done objects\n")); mprintf((0, "Sending done objects\n"));
// Take the level checksum and clone it. // Take the level checksum and clone it.
MD5 *playermd5 = Level_md5->Clone(); MD5 playermd5(*Level_md5);
// Generate a random salt value and send it to the client. // Generate a random salt value and send it to the client.
int salt = ps_rand(); int salt = ps_rand();
// process the salt through the md5 // process the salt through the md5
playermd5->MD5Update(salt); playermd5.update(salt);
// process the ships through the md5 // process the ships through the md5
for (int i = 0; i < MAX_SHIPS; i++) for (int i = 0; i < MAX_SHIPS; i++)
if (Ships[i].used) if (Ships[i].used)
MultiProcessShipChecksum(playermd5, i); MultiProcessShipChecksum(&playermd5, i);
// save the digest value in the netplayer slot // save the digest value in the netplayer slot
playermd5->MD5Final(NetPlayers[slot].digest); playermd5.digest(NetPlayers[slot].digest);
MD5::Destroy(playermd5);
size = START_DATA(MP_DONE_OBJECTS, data, &count); size = START_DATA(MP_DONE_OBJECTS, data, &count);
MultiAddInt(salt, data, &count); MultiAddInt(salt, data, &count);

View File

@ -1,422 +1,192 @@
/* /* Descent 3
* Descent 3 * Copyright (C) 2024 Descent Developers
* Copyright (C) 2024 Parallax Software *
* * This program is free software: you can redistribute it and/or modify
* 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
* it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or * (at your option) any later version.
* (at your option) any later version. *
* * This program is distributed in the hope that it will be useful,
* This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details.
* GNU General Public License for more details. *
* * You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>.
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
--- HISTORICAL COMMENTS FOLLOW ---
This is the C++ implementation of the MD5 Message-Digest
Algorithm desrcipted in RFC 1321.
I translated the C code from this RFC to C++.
There is now warranty.
Feb. 12. 2005
Benjamin Grüdelbach
*/
/*
Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
*/
// md5 class include
#include "md5.h" #include "md5.h"
#include <byteswap.h> #include <byteswap.h>
#include <string.h>
#include "stdio.h" #include <algorithm>
static FILE *md5log = NULL;
#define MD5_DEBUG_LOG 0 namespace {
#ifdef OUTRAGE_BIG_ENDIAN const unsigned int md5_round_shifts[] = {7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21};
void byteReverse(unsigned char *buf, unsigned longs) {
uint32_t t;
do {
t = (uint32_t)((unsigned)buf[3] << 8 | buf[2]) << 16 | ((unsigned)buf[1] << 8 | buf[0]);
*(uint32_t *)buf = t;
buf += 4;
} while (--longs);
}
#else
#define byteReverse(buf, len) /* Nothing */
#endif
MD5::~MD5() { const std::uint32_t md5_round_constants[] = {
#if MD5_DEBUG_LOG 0xD76AA478UL, 0xE8C7B756UL, 0x242070DBUL, 0xC1BDCEEEUL, 0xF57C0FAFUL, 0x4787C62AUL, 0xA8304613UL, 0xFD469501UL,
if (md5log) { 0x698098D8UL, 0x8B44F7AFUL, 0xFFFF5BB1UL, 0x895CD7BEUL, 0x6B901122UL, 0xFD987193UL, 0xA679438EUL, 0x49B40821UL,
fclose(md5log); 0xF61E2562UL, 0xC040B340UL, 0x265E5A51UL, 0xE9B6C7AAUL, 0xD62F105DUL, 0x02441453UL, 0xD8A1E681UL, 0xE7D3FBC8UL,
md5log = NULL; 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};
std::uint32_t rotl_uint32(std::uint32_t a, std::uint32_t b) noexcept { return b ? (a << b) | (a >> (32 - b)) : a; };
}; // namespace
void MD5::round(std::array<std::uint32_t, 4> &sums, const unsigned char *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);
} }
#endif
}
/* MD5 initialization. Begins an MD5 operation, writing a new context. */ std::uint32_t a = sums[0], b = sums[1], c = sums[2], d = sums[3];
void MD5::MD5Init() {
MD5_CTX *context = &ctx;
context->buf[0] = 0x67452301;
context->buf[1] = 0xefcdab89;
context->buf[2] = 0x98badcfe;
context->buf[3] = 0x10325476;
context->bits[0] = 0; // 64 rounds...
context->bits[1] = 0; for (unsigned int r = 0; r < 64; ++r) {
#if MD5_DEBUG_LOG std::uint32_t f, g;
if (!md5log) { unsigned int s = md5_round_shifts[((r >> 2) & ~3) | (r & 3)];
md5log = fopen("md5.log", "wt+"); switch (r >> 4) {
} case 0:
if (md5log) { f = (b & c) | (~b & d), g = r;
fprintf(md5log, "Starting new sum...\n"); break;
} case 1:
#endif f = (b & d) | (c & ~d), g = (5 * r + 1) & 15;
} break;
case 2:
void MD5::MD5Update(float valin) { f = b ^ c ^ d, g = (3 * r + 5) & 15;
float val = INTEL_FLOAT(valin); break;
unsigned char *p = (unsigned char *)&val; case 3:
#if MD5_DEBUG_LOG f = c ^ (b | ~d), g = (7 * r) & 15;
if (md5log) { break;
fprintf(md5log, "[float]"); default:; // unreachable
}
#endif
MD5Update(p, sizeof(float));
}
void MD5::MD5Update(int valin) {
int val = INTEL_INT(valin);
unsigned char *p = (unsigned char *)&val;
#if MD5_DEBUG_LOG
if (md5log) {
fprintf(md5log, "[int]");
}
#endif
MD5Update(p, sizeof(int));
}
void MD5::MD5Update(short valin) {
short val = INTEL_SHORT(valin);
unsigned char *p = (unsigned char *)&val;
#if MD5_DEBUG_LOG
if (md5log) {
fprintf(md5log, "[short]");
}
#endif
MD5Update(p, sizeof(short));
}
void MD5::MD5Update(unsigned int valin) {
unsigned int val = INTEL_INT(valin);
unsigned char *p = (unsigned char *)&val;
#if MD5_DEBUG_LOG
if (md5log) {
fprintf(md5log, "[u_int]");
}
#endif
MD5Update(p, sizeof(unsigned int));
}
void MD5::MD5Update(unsigned char val) {
unsigned char *p = (unsigned char *)&val;
#if MD5_DEBUG_LOG
if (md5log) {
fprintf(md5log, "[u_char]");
}
#endif
MD5Update(p, sizeof(unsigned char));
}
/*
MD5 block update operation. Continues an MD5 message-digest
operation, processing another message block, and updating the
context.
*/
void MD5::MD5Update(unsigned char *buf, unsigned int len) {
MD5_CTX *context = &ctx;
#if MD5_DEBUG_LOG
if (md5log) {
fprintf(md5log, "(%d) ", len);
for (unsigned int a = 0; a < len; a++) {
fprintf(md5log, "%x ", buf[a] & 0xff);
} }
fprintf(md5log, "\n"); f = rotl_uint32(f + a + m[g] + md5_round_constants[r], s) + b;
a = d;
d = c;
c = b;
b = f;
} }
#endif
uint32_t t;
/* Update bitcount */ sums[0] += a, sums[1] += b, sums[2] += c, sums[3] += d;
}
t = context->bits[0]; void MD5::round(const unsigned char *block) noexcept { round(sums_, block); }
if ((context->bits[0] = t + ((uint32_t)len << 3)) < t)
context->bits[1]++; /* Carry from low to high */
context->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ void MD5::update(const unsigned char *data, std::size_t n) noexcept {
message_len_ += n * 8;
/* Handle any leading odd-sized chunks */ // if there is already some data in tmpbuf, check if we would fill it
if (tmpbuf_n_) {
if (t) { if ((n >= tmpbuf_.size() || tmpbuf_n_ + n >= tmpbuf_.size())) {
unsigned char *p = (unsigned char *)context->in + t; // we would fill tmpbuf at least once; handle partial block first.
std::size_t remaining = tmpbuf_.size() - tmpbuf_n_;
t = 64 - t; std::copy(data, data + remaining, tmpbuf_.begin() + tmpbuf_n_);
if (len < t) { data += remaining;
memcpy(p, buf, len); 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;
return; return;
} }
memcpy(p, buf, t);
byteReverse(context->in, 16);
MD5Transform(context->buf, (uint32_t *)context->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(context->in, buf, 64);
byteReverse(context->in, 16);
MD5Transform(context->buf, (uint32_t *)context->in);
buf += 64;
len -= 64;
} }
/* Handle any remaining bytes of data. */ // do rounds on full blocks for as long as we can
while (n >= tmpbuf_.size()) {
memcpy(context->in, buf, len); round(data);
} data += tmpbuf_.size();
n -= tmpbuf_.size();
/* MD5 finalization. Ends an MD5 message-digest operation, writing the
the message digest and zeroizing the context.
*/
void MD5::MD5Final(unsigned char digest[16]) {
MD5_CTX *context = &ctx;
unsigned count;
unsigned char *p;
/* Compute number of bytes mod 64 */
count = (context->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = context->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(context->in, 16);
MD5Transform(context->buf, (uint32_t *)context->in);
/* Now fill the next block with 56 bytes */
memset(context->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
} }
byteReverse(context->in, 14);
/* Append length in bits and transform */ if (n) {
((uint32_t *)context->in)[14] = context->bits[0]; // copy partial block
((uint32_t *)context->in)[15] = context->bits[1]; std::copy(data, data + n, tmpbuf_.begin());
tmpbuf_n_ = n;
MD5Transform(context->buf, (uint32_t *)context->in);
byteReverse((unsigned char *)context->buf, 4);
memcpy(digest, context->buf, 16);
memset(context, 0, sizeof(*context)); /* In case it's sensitive */
/* The original version of this code omitted the asterisk. In
effect, only the first part of context was wiped
* with zeros, not
the whole thing. Bug found by Derek Jones. Original line: */
// memset(context, 0, sizeof(context)); /* In case it's sensitive */
}
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new
* data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
void MD5::MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
/* Encodes input (unsigned long int) into output (unsigned char). Assumes len is
a multiple of 4.
*/
void MD5::Encode(unsigned char *output, unsigned int *input, unsigned int len) {
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4) {
unsigned int inp = INTEL_INT(input[i]);
output[j] = (unsigned char)(inp & 0xff);
output[j + 1] = (unsigned char)((inp >> 8) & 0xff);
output[j + 2] = (unsigned char)((inp >> 16) & 0xff);
output[j + 3] = (unsigned char)((inp >> 24) & 0xff);
} }
} }
/* Decodes input (unsigned char) into output (unsigned long int). Assumes len is void MD5::place_length(unsigned char *destination) const noexcept {
a multiple of 4. for (std::size_t i = 0; i < 8; ++i)
*/ destination[i] = static_cast<unsigned char>(message_len_ >> (8 * i));
void MD5::Decode(unsigned int *output, unsigned char *input, unsigned int len) { }
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4) { std::array<unsigned char, 16> MD5::digest() const noexcept {
unsigned int inp0 = INTEL_INT(input[j]); // copies of sums and buffers
unsigned int inp1 = INTEL_INT(input[j + 1]); auto sums = sums_;
unsigned int inp2 = INTEL_INT(input[j + 2]); auto buf = tmpbuf_;
unsigned int inp3 = INTEL_INT(input[j + 3]); std::size_t n = tmpbuf_n_;
output[i] = ((unsigned int)inp0) | (((unsigned int)inp1) << 8) | (((unsigned int)inp2) << 16) |
(((unsigned int)inp3) << 24); if (n > 56) {
// must append current block, length won't fit.
// n is never buf.size() yet
buf[n++] = 0x80U;
while (n < buf.size())
buf[n++] = 0;
round(sums, buf.data());
// all zeroes, except for length
std::fill(buf.begin(), buf.begin() + 56, 0);
} else if (n < 56) {
// append padding to tmpbuf and then the length
buf[n++] = 0x80U;
while (n < 56)
buf[n++] = 0;
} }
place_length(&buf[56]);
round(sums, buf.data());
return {static_cast<unsigned char>(sums[0]), static_cast<unsigned char>(sums[0] >> 8),
static_cast<unsigned char>(sums[0] >> 16), static_cast<unsigned char>(sums[0] >> 24),
static_cast<unsigned char>(sums[1]), static_cast<unsigned char>(sums[1] >> 8),
static_cast<unsigned char>(sums[1] >> 16), static_cast<unsigned char>(sums[1] >> 24),
static_cast<unsigned char>(sums[2]), static_cast<unsigned char>(sums[2] >> 8),
static_cast<unsigned char>(sums[2] >> 16), static_cast<unsigned char>(sums[2] >> 24),
static_cast<unsigned char>(sums[3]), static_cast<unsigned char>(sums[3] >> 8),
static_cast<unsigned char>(sums[3] >> 16), static_cast<unsigned char>(sums[3] >> 24)};
} }
/* Note: Replace "for loop" with standard memcpy if possible. void MD5::update(float valin) noexcept {
*/ float val = INTEL_FLOAT(valin);
unsigned char *p = (unsigned char *)&val;
void MD5::MD5_memcpy(POINTER output, POINTER input, unsigned int len) { update(p, sizeof(float));
unsigned int i;
for (i = 0; i < len; i++)
output[i] = input[i];
} }
/* Note: Replace "for loop" with standard memset if possible. void MD5::update(int valin) noexcept {
*/ int val = INTEL_INT(valin);
void MD5::MD5_memset(POINTER output, int value, unsigned int len) { unsigned char *p = (unsigned char *)&val;
unsigned int i; update(p, sizeof(int));
for (i = 0; i < len; i++) {
((char *)output)[i] = (char)value;
}
} }
MD5 *MD5::Clone() { void MD5::update(short valin) noexcept {
MD5 *clone = new MD5(); short val = INTEL_SHORT(valin);
clone->ctx = this->ctx; unsigned char *p = (unsigned char *)&val;
return clone; update(p, sizeof(short));
} }
void MD5::Destroy(MD5 *obj) { void MD5::update(unsigned int valin) noexcept {
if (obj) unsigned int val = INTEL_INT(valin);
delete obj; unsigned char *p = (unsigned char *)&val;
update(p, sizeof(unsigned int));
}
void MD5::update(unsigned char val) noexcept {
unsigned char *p = (unsigned char *)&val;
update(p, sizeof(unsigned char));
}
void MD5::digest(unsigned char *destination) const noexcept {
auto digest_data = digest();
std::copy(digest_data.begin(), digest_data.end(), destination);
} }

118
md5/md5.h
View File

@ -1,91 +1,47 @@
/* /*
* Descent 3 * Descent 3
* Copyright (C) 2024 Parallax Software * Copyright (C) 2024 Parallax Software
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
--- HISTORICAL COMMENTS FOLLOW ---
This is the C++ implementation of the MD5 Message-Digest
Algorithm desrcipted in RFC 1321.
I translated the C code from this RFC to C++.
There is now warranty.
Feb. 12. 2005
Benjamin Grüdelbach
*/
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
*/ */
#pragma once #ifndef MD5_H
#define MD5_H
typedef unsigned char *POINTER; #include <array>
typedef unsigned int uint32_t; #include <cstdint>
/* MD5 context. */
typedef struct _context_md5_t {
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
} context_md5_t;
typedef context_md5_t MD5_CTX;
class MD5 { class MD5 {
private: private:
MD5_CTX ctx; std::array<std::uint32_t, 4> sums_{0x67452301UL, 0XEFCDAB89UL, 0x98BADCFEUL, 0x10325476UL};
void MD5Transform(uint32_t buf[4], uint32_t const in[16]); std::array<unsigned char, 64> tmpbuf_;
void Encode(unsigned char *, unsigned int *, unsigned int); std::size_t tmpbuf_n_{0};
void Decode(unsigned int *, unsigned char *, unsigned int); std::uint64_t message_len_{0};
void MD5_memcpy(POINTER, POINTER, unsigned int);
void MD5_memset(POINTER, int, unsigned int); void round(const unsigned char *block) noexcept;
void round(std::array<std::uint32_t, 4> &sums, const unsigned char *block) const noexcept;
void place_length(unsigned char *destination) const noexcept;
public: public:
void MD5Init(); void update(const unsigned char *data, std::size_t n) noexcept;
void update(float val) noexcept;
void MD5Update(unsigned char *, unsigned int); void update(int val) noexcept;
void MD5Update(float val); void update(unsigned int val) noexcept;
void MD5Update(int val); void update(unsigned char val) noexcept;
void MD5Update(unsigned int val); void update(short val) noexcept;
void MD5Update(unsigned char val); std::array<unsigned char, 16> digest() const noexcept;
void MD5Update(short val); void digest(unsigned char *destination) const noexcept;
void MD5Final(unsigned char[16]);
~MD5();
MD5(){};
MD5 *Clone();
static void Destroy(MD5 *obj);
}; };
#endif // MD5_H

View File

@ -9,3 +9,16 @@ target_link_libraries(
target_include_directories(byteswap_tests PRIVATE ${PROJECT_SOURCE_DIR}/lib) target_include_directories(byteswap_tests PRIVATE ${PROJECT_SOURCE_DIR}/lib)
gtest_discover_tests(byteswap_tests) gtest_discover_tests(byteswap_tests)
add_executable(
md5_tests
../md5/md5.cpp
md5_tests.cpp
)
target_link_libraries(
md5_tests
GTest::gtest_main
)
target_include_directories(md5_tests PRIVATE ${PROJECT_SOURCE_DIR}/md5 ${PROJECT_SOURCE_DIR}/lib)
gtest_discover_tests(md5_tests)

56
tests/md5_tests.cpp Normal file
View File

@ -0,0 +1,56 @@
/*
* 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/>.
*/
#include <gtest/gtest.h>
#include <sstream>
#include <iomanip>
#include "md5.h"
namespace {
std::string hexdigest_after_n_rounds_of_fuzz(int n) {
MD5 md5;
for (int i = 0; i < n; ++i) {
md5.update((float)1.0f);
md5.update((int)2);
md5.update((short)3);
md5.update((unsigned int)4U);
md5.update((unsigned char)5);
}
std::ostringstream hexdigest;
hexdigest << std::hex;
auto digest = md5.digest();
for (int i = 0; i < 16; ++i)
hexdigest << std::setw(2) << std::setfill('0') << std::nouppercase << static_cast<unsigned int>(digest[i]);
return hexdigest.str();
}
}; // namespace
TEST(D3, MD5) {
EXPECT_EQ(hexdigest_after_n_rounds_of_fuzz(0), "d41d8cd98f00b204e9800998ecf8427e");
EXPECT_EQ(hexdigest_after_n_rounds_of_fuzz(1), "c56c005504f5b9b17df9d83f1106e9b2");
EXPECT_EQ(hexdigest_after_n_rounds_of_fuzz(2), "98327a3f4ee311c9bdc8613508922c95");
EXPECT_EQ(hexdigest_after_n_rounds_of_fuzz(100), "99b30c1dcc42d97c3d914c26a14bb1d7");
EXPECT_EQ(hexdigest_after_n_rounds_of_fuzz(1999), "82eeff8c7d574c8232b0ca6ca2c9dd40");
}