Created
September 8, 2022 13:50
-
-
Save nmmmnu/17bdc8cf798ba0ebf9d9dd6626cce69d to your computer and use it in GitHub Desktop.
MD5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "md5.h" | |
#include <cstdio> | |
#include <cstdlib> | |
int main(int argc, char **argv) { | |
if (argc < 2) { | |
printf("usage: %s 'string'\n", argv[0]); | |
return 1; | |
} | |
std::string_view const msg = argv[1]; | |
auto const digest = md5(msg); | |
for (auto &x : digest) | |
printf("%2.2x", x); | |
printf("\n"); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* MD5 | |
* | |
* Converted to modern C++17 by Nikolay Mihaylov ([email protected]) | |
* Copyright (C) 2022-09-08 | |
* | |
* | |
* | |
* based on: | |
* http://www.zedwood.com/article/cpp-md5-function | |
* | |
* | |
* | |
* converted to C++ class by Frank Thilo ([email protected]) | |
* for bzflag (http://www.bzflag.org) | |
* | |
* based on: | |
* | |
* | |
* | |
* md5.h and md5.c | |
* reference implementation of RFC 1321 | |
* | |
* 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. | |
*/ | |
#include "md5.h" | |
#include <cstring> | |
namespace md5_implementation_{ | |
namespace{ | |
auto memset_secure(void *p, int value, size_t size){ | |
return memset(p, value, size); | |
} | |
using uint1 = MD5::uint1; | |
using uint4 = MD5::uint4; | |
using size_type = MD5::size_type; | |
constexpr uint1 padding[64] = { | |
0x80, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0 | |
}; | |
// Constants for MD5Transform routine. | |
constexpr uint1 S11 = 7; | |
constexpr uint1 S12 = 12; | |
constexpr uint1 S13 = 17; | |
constexpr uint1 S14 = 22; | |
constexpr uint1 S21 = 5; | |
constexpr uint1 S22 = 9; | |
constexpr uint1 S23 = 14; | |
constexpr uint1 S24 = 20; | |
constexpr uint1 S31 = 4; | |
constexpr uint1 S32 = 11; | |
constexpr uint1 S33 = 16; | |
constexpr uint1 S34 = 23; | |
constexpr uint1 S41 = 6; | |
constexpr uint1 S42 = 10; | |
constexpr uint1 S43 = 15; | |
constexpr uint1 S44 = 21; | |
// F, G, H and I are basic MD5 functions. | |
constexpr uint4 F(uint4 x, uint4 y, uint4 z) { | |
return (x & y) | (~x & z); | |
} | |
constexpr uint4 G(uint4 x, uint4 y, uint4 z) { | |
return (x & z) | (y & ~z); | |
} | |
constexpr uint4 H(uint4 x, uint4 y, uint4 z) { | |
return x ^ y ^ z; | |
} | |
constexpr uint4 I(uint4 x, uint4 y, uint4 z) { | |
return y ^ (x | ~z); | |
} | |
// rotate_left rotates x left n bits. | |
constexpr uint4 rotate_left(uint4 x, int n) { | |
return (x << n) | (x >> (32-n)); | |
} | |
// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. | |
// Rotation is separate from addition to prevent recomputation. | |
constexpr void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { | |
a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; | |
} | |
constexpr void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { | |
a = rotate_left(a + G(b,c,d) + x + ac, s) + b; | |
} | |
constexpr void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { | |
a = rotate_left(a + H(b,c,d) + x + ac, s) + b; | |
} | |
constexpr void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { | |
a = rotate_left(a + I(b,c,d) + x + ac, s) + b; | |
} | |
// decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. | |
void decode(uint4 *output, const uint1 *input, size_type len){ | |
for (unsigned int i = 0, j = 0; j < len; i++, j += 4) | |
output[i] = | |
(((uint4)input[j + 0]) << 0) | | |
(((uint4)input[j + 1]) << 8) | | |
(((uint4)input[j + 2]) << 16) | | |
(((uint4)input[j + 3]) << 24) | |
; | |
} | |
// encodes input (uint4) into output (unsigned char). Assumes len is a multiple of 4. | |
void encode(uint1 *output, const uint4 *input, size_type len){ | |
for (size_type i = 0, j = 0; j < len; i++, j += 4) { | |
output[j + 0] = uint1(input[i] >> 0) & 0xff; | |
output[j + 1] = uint1(input[i] >> 8) & 0xff; | |
output[j + 2] = uint1(input[i] >> 16) & 0xff; | |
output[j + 3] = uint1(input[i] >> 24) & 0xff; | |
} | |
} | |
void encode(MD5::Digest &digest, const uint4 *input){ | |
return encode(digest.data(), input, (size_type) digest.size()); | |
} | |
} // anonymous namespace | |
} // namespace md5_implementation_ | |
// apply MD5 algo on a block | |
void MD5::transform_(const uint1 block[blocksize]){ | |
using namespace md5_implementation_; | |
uint4 a = state[0]; | |
uint4 b = state[1]; | |
uint4 c = state[2]; | |
uint4 d = state[3]; | |
uint4 x[16]; | |
decode (x, block, blocksize); | |
/* Round 1 */ | |
FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ | |
FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ | |
FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ | |
FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ | |
FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ | |
FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ | |
FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ | |
FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ | |
FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ | |
FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ | |
FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ | |
FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ | |
FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ | |
FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ | |
FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ | |
FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ | |
/* Round 2 */ | |
GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ | |
GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ | |
GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ | |
GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ | |
GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ | |
GG(d, a, b, c, x[10], S22, 0x02441453); /* 22 */ | |
GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ | |
GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ | |
GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ | |
GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ | |
GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ | |
GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ | |
GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ | |
GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ | |
GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ | |
GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ | |
/* Round 3 */ | |
HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ | |
HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ | |
HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ | |
HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ | |
HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ | |
HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ | |
HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ | |
HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ | |
HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ | |
HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ | |
HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ | |
HH(b, c, d, a, x[ 6], S34, 0x04881d05); /* 44 */ | |
HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ | |
HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ | |
HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ | |
HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ | |
/* Round 4 */ | |
II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ | |
II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ | |
II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ | |
II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ | |
II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ | |
II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ | |
II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ | |
II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ | |
II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ | |
II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ | |
II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ | |
II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ | |
II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ | |
II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ | |
II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ | |
II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ | |
state[0] += a; | |
state[1] += b; | |
state[2] += c; | |
state[3] += d; | |
// Zeroize sensitive information. | |
if constexpr(MD5::secure_wipe) | |
memset_secure(x, 0, sizeof(x)); | |
} | |
// MD5 block update operation. Continues an MD5 message-digest operation, | |
// processing another message block | |
void MD5::update(const unsigned char input[], size_type length){ | |
using namespace md5_implementation_; | |
// compute number of bytes mod 64 | |
size_type index = count[0] / 8 % blocksize; | |
// Update number of bits | |
if ((count[0] += (length << 3)) < (length << 3)) | |
count[1]++; | |
count[1] += (length >> 29); | |
// number of bytes we need to fill in buffer | |
size_type firstpart = 64 - index; | |
size_type i; | |
// transform as many times as possible. | |
if (length >= firstpart){ | |
// fill buffer first, transform | |
memcpy(&buffer[index], input, firstpart); | |
transform_(buffer); | |
// transform chunks of blocksize (64 bytes) | |
for(i = firstpart; i + blocksize <= length; i += blocksize) | |
transform_(&input[i]); | |
index = 0; | |
}else | |
i = 0; | |
// buffer remaining input | |
memcpy(&buffer[index], &input[i], length-i); | |
} | |
// MD5 finalization. Ends an MD5 message-digest operation, | |
// writing the the message digest and zeroizing the context. | |
auto MD5::finalize() -> Digest{ | |
using namespace md5_implementation_; | |
// Save number of bits | |
uint1 bits[8]; | |
encode(bits, count, 8); | |
// pad out to 56 mod 64. | |
size_type index = count[0] / 8 % 64; | |
size_type padLen = (index < 56) ? (56 - index) : (120 - index); | |
update(padding, padLen); | |
// Append length (before padding) | |
update(bits, 8); | |
Digest digest; | |
// Store state in digest | |
encode(digest, state); | |
// Zeroize sensitive information. | |
if constexpr(MD5::secure_wipe){ | |
memset_secure(buffer, 0, sizeof(buffer)); | |
memset_secure(count, 0, sizeof(count )); | |
} | |
return digest; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef MY_MD5_H_ | |
#define MY_MD5_H_ | |
#include <cstdint> | |
#include <string_view> | |
#include <array> | |
#include <limits> | |
#include <cassert> | |
class MD5{ | |
public: | |
constexpr static auto bits = 128; | |
constexpr static bool secure_wipe = true; | |
using uint1 = uint8_t; | |
using uint4 = uint32_t; | |
using size_type = uint4; // must be 32bit | |
using Digest = std::array<uint1, 16>; | |
public: | |
MD5() = default; | |
MD5(std::string_view s){ | |
update(s); | |
} | |
void update(const uint1 *buffer, size_type size); | |
void update(const char *s, size_t size){ | |
assert( size < std::numeric_limits<size_type>::max() ); | |
update((const uint1 *) s, (size_type) size); | |
} | |
void update(std::string_view s){ | |
update(s.data(), s.size()); | |
} | |
Digest finalize(); | |
private: | |
constexpr static auto blocksize = 64; | |
private: | |
void transform_(const uint1 block[blocksize]); | |
private: | |
uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk | |
uint4 count[2] = { // 64bit counter for number of bits (lo, hi) | |
0, | |
0 | |
}; | |
uint4 state[4] = { // digest so far | |
0x67452301, | |
0xefcdab89, | |
0x98badcfe, | |
0x10325476 | |
}; | |
}; | |
inline MD5::Digest md5(std::string_view s){ | |
MD5 f(s); | |
return f.finalize(); | |
} | |
#endif | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment