SPDX-FileCopyrightText: © 2020 3saster SPDX-FileCopyrightText: © 2026 Alexander Kromm <mmaulwurff@gmail.com> SPDX-License-Identifier: BSD-3-Clause
1. About
This is MD5 ZScript library by 3saster, repackaged as a DoomToolbox module by m8f. Also, truncation parameter is removed, and signed integer warning fixed.
2. Source
// Copyright 2020 3saster // Copyright 2026 Alexander Kromm <mmaulwurff@gmail.com> // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // 3. The name of the author may not be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. // IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //=========================================================================== // // MD5 Implementation in ZScript // by 3saster // // A class that returns a string containing the MD5 of an input string, using // the Hash method. // // You are welcome to to use this in your mods, no need to ask for permission, // as long as the above copyright notice is included, and credit is given. // //=========================================================================== Class NAMESPACE_MD5 { // Some functions needed for MD5 protected static uint F (uint B, uint C, uint D) { return (B & C) | (~B & D); } protected static uint G (uint B, uint C, uint D) { return (B & D) | (C & ~D); } protected static uint H (uint B, uint C, uint D) { return B ^ C ^ D; } protected static uint I (uint B, uint C, uint D) { return C ^ (B | ~D); } protected static uint leftrotate (uint x, uint c) { return (x << c) | (x >> (32-c)); } // Swap endianess of byte bitwise protected static uint swapByte (uint x) { uint r = 0; for(int i=0; i<8; i++) r += ((x>>i) & 1) << (7-i); return r; } // Swap endianess of word bytewise protected static uint swapWord (uint x) { uint r = 0; for(int i=0; i<32; i += 8) r += ((x>>i) & 0xFF) << (24-i); return r; } static string Hash(string key) { // s specifies the per-round shift amounts uint s[64] = { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 }; // Constants uint K[64] = { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; // Initialize variables: uint a0 = 0x67452301; // A uint b0 = 0xefcdab89; // B uint c0 = 0x98badcfe; // C uint d0 = 0x10325476; // D // Turn string to byte array Array<uint> input; for(uint i=0; i < key.length(); i++) { input.push(key.ByteAt(i)); } // Padding - add a single 1 bit, then add zeros until the number of bytes is 56 mod 64 input.push(0x80); while( input.size()%64 != 56 ) { input.push(0x00); } // Pad the remaining 8 bytes with the original message length in little endian uint strLen = 8*key.length(); for(uint i=0; i<8; i++) { input.push(strLen & 0xFF); strLen >>= 8; } // Swap endianess of each byte uint inputSize = uint(input.size()); for(uint i=0; i < inputSize; i++) { input[i] = swapByte(input[i]); } // Break into 64 byte chunks for(uint front=0; front < inputSize; front += 64) { //break chunk into 16 four-byte words Array<uint> M; for(int k=0; k<64; k += 4) { M.push( ( swapByte(input[front + k + 0]) << 0 ) + ( swapByte(input[front + k + 1]) << 8 ) + ( swapByte(input[front + k + 2]) << 16) + ( swapByte(input[front + k + 3]) << 24) ); } // Initialize hash value for this chunk: uint A = a0; uint B = b0; uint C = c0; uint D = d0; uint f; uint g; // Main Loop for(uint i=0; i<64; i++) { if(0 <= i && i <= 15) { f = F(B,C,D); g = i; } else if(16 <= i && i <= 31) { f = G(B,C,D); g = (5*i+1)%16; } else if(32 <= i && i <= 47) { f = H(B,C,D); g = (3*i+5)%16; } else //if(48 <= i && i <= 63) { f = I(B,C,D); g = (7*i)%16; } f = f + A + K[i] + M[g]; A = D; D = C; C = B; B = B + leftrotate(f, s[i]); } // Add this chunk's hash to result so far: a0 += A; b0 += B; c0 += C; d0 += D; } return string.format("%08x%08x%08x%08x",swapWord(a0),swapWord(b0),swapWord(c0),swapWord(d0)); } }
3. Test
// GameInfo { EventHandlers = "md5_TestEventHandler" }
version 4.14.2 #include "MD5.zs" class md5_TestEventHandler : StaticEventHandler { override void networkProcess(ConsoleEvent event) { if (event.name != "md5-test") return; Map<string, string> cases; cases.insert("", "d41d8cd98f00b204e9800998ecf8427e"); cases.insert("The quick brown fox jumps over the lazy dog", "9e107d9d372bb6826bd81d3542a419d6"); // Note the dot at the end. cases.insert("The quick brown fox jumps over the lazy dog.", "e4d909c290d0fb1ca068ffaddf22cbd0"); foreach (input, expected: cases) { string actual = NAMESPACE_MD5.hash(input); if (actual == expected) continue; Console.printf("ERROR: input: %s, actual: %s, expected: %s", input, actual, expected); } } }
wait 2; map map01; wait 2; netevent md5-test; wait 2; quit