1 /******************************************************************************* 2 3 This module implements the MD4 Message Digest Algorithm as described 4 by RFC 1320 The MD4 Message-Digest Algorithm. R. Rivest. April 1992. 5 6 Copyright: 7 Copyright (c) 2006 Tango contributors. 8 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 9 All rights reserved. 10 11 License: 12 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 13 See LICENSE_TANGO.txt for details. 14 15 Version: Initial release: Feb 2006 16 17 Authors: Regan Heath, Oskar Linde 18 19 *******************************************************************************/ 20 21 module ocean.util.digest.Md4; 22 23 import ocean.meta.types.Qualifiers; 24 25 public import ocean.util.digest.Digest; 26 27 import ocean.util.digest.MerkleDamgard; 28 29 version (unittest) import ocean.core.Test; 30 31 /******************************************************************************* 32 33 *******************************************************************************/ 34 35 class Md4 : MerkleDamgard 36 { 37 protected uint[4] context; 38 private static immutable ubyte padChar = 0x80; 39 40 /*********************************************************************** 41 42 Construct an Md4 43 44 ***********************************************************************/ 45 46 this() { } 47 48 /*********************************************************************** 49 50 The MD 4 digest size is 16 bytes 51 52 ***********************************************************************/ 53 54 override uint digestSize() { return 16; } 55 56 /*********************************************************************** 57 58 Initialize the cipher 59 60 Remarks: 61 Returns the cipher state to it's initial value 62 63 ***********************************************************************/ 64 65 override void reset() 66 { 67 super.reset(); 68 context[] = initial[]; 69 } 70 71 /*********************************************************************** 72 73 Obtain the digest 74 75 Returns: 76 the digest 77 78 Remarks: 79 Returns a digest of the current cipher state, this may be the 80 final digest, or a digest of the state between calls to update() 81 82 ***********************************************************************/ 83 84 override void createDigest(ubyte[] buf) 85 { 86 version (BigEndian) 87 ByteSwap.swap32 (context.ptr, context.length * uint.sizeof); 88 89 buf[] = cast(ubyte[]) context; 90 } 91 92 /*********************************************************************** 93 94 block size 95 96 Returns: 97 the block size 98 99 Remarks: 100 Specifies the size (in bytes) of the block of data to pass to 101 each call to transform(). For MD4 the blockSize is 64. 102 103 ***********************************************************************/ 104 105 protected override uint blockSize() { return 64; } 106 107 /*********************************************************************** 108 109 Length padding size 110 111 Returns: 112 the length padding size 113 114 Remarks: 115 Specifies the size (in bytes) of the padding which uses the 116 length of the data which has been ciphered, this padding is 117 carried out by the padLength method. For MD4 the addSize is 8. 118 119 ***********************************************************************/ 120 121 protected override uint addSize() { return 8; } 122 123 /*********************************************************************** 124 125 Pads the cipher data 126 127 Params: 128 data = a slice of the cipher buffer to fill with padding 129 130 Remarks: 131 Fills the passed buffer slice with the appropriate padding for 132 the final call to transform(). This padding will fill the cipher 133 buffer up to blockSize()-addSize(). 134 135 ***********************************************************************/ 136 137 protected override void padMessage(ubyte[] data) 138 { 139 data[0] = padChar; 140 data[1..$] = 0; 141 } 142 143 /*********************************************************************** 144 145 Performs the length padding 146 147 Params: 148 data = the slice of the cipher buffer to fill with padding 149 length = the length of the data which has been ciphered 150 151 Remarks: 152 Fills the passed buffer slice with addSize() bytes of padding 153 based on the length in bytes of the input data which has been 154 ciphered. 155 156 ***********************************************************************/ 157 158 protected override void padLength(ubyte[] data, ulong length) 159 { 160 length <<= 3; 161 littleEndian64((cast(ubyte*)&length)[0..8],cast(ulong[]) data); 162 } 163 164 /*********************************************************************** 165 166 Performs the cipher on a block of data 167 168 Params: 169 input = the block of data to cipher 170 171 Remarks: 172 The actual cipher algorithm is carried out by this method on 173 the passed block of data. This method is called for every 174 blockSize() bytes of input data and once more with the remaining 175 data padded to blockSize(). 176 177 ***********************************************************************/ 178 179 protected override void transform(ubyte[] input) 180 { 181 uint a,b,c,d; 182 uint[16] x; 183 184 littleEndian32(input,x); 185 186 a = context[0]; 187 b = context[1]; 188 c = context[2]; 189 d = context[3]; 190 191 /* Round 1 */ 192 ff(a, b, c, d, x[ 0], S11, 0); /* 1 */ 193 ff(d, a, b, c, x[ 1], S12, 0); /* 2 */ 194 ff(c, d, a, b, x[ 2], S13, 0); /* 3 */ 195 ff(b, c, d, a, x[ 3], S14, 0); /* 4 */ 196 ff(a, b, c, d, x[ 4], S11, 0); /* 5 */ 197 ff(d, a, b, c, x[ 5], S12, 0); /* 6 */ 198 ff(c, d, a, b, x[ 6], S13, 0); /* 7 */ 199 ff(b, c, d, a, x[ 7], S14, 0); /* 8 */ 200 ff(a, b, c, d, x[ 8], S11, 0); /* 9 */ 201 ff(d, a, b, c, x[ 9], S12, 0); /* 10 */ 202 ff(c, d, a, b, x[10], S13, 0); /* 11 */ 203 ff(b, c, d, a, x[11], S14, 0); /* 12 */ 204 ff(a, b, c, d, x[12], S11, 0); /* 13 */ 205 ff(d, a, b, c, x[13], S12, 0); /* 14 */ 206 ff(c, d, a, b, x[14], S13, 0); /* 15 */ 207 ff(b, c, d, a, x[15], S14, 0); /* 16 */ 208 209 /* Round 2 */ 210 gg(a, b, c, d, x[ 0], S21, 0x5a827999); /* 17 */ 211 gg(d, a, b, c, x[ 4], S22, 0x5a827999); /* 18 */ 212 gg(c, d, a, b, x[ 8], S23, 0x5a827999); /* 19 */ 213 gg(b, c, d, a, x[12], S24, 0x5a827999); /* 20 */ 214 gg(a, b, c, d, x[ 1], S21, 0x5a827999); /* 21 */ 215 gg(d, a, b, c, x[ 5], S22, 0x5a827999); /* 22 */ 216 gg(c, d, a, b, x[ 9], S23, 0x5a827999); /* 23 */ 217 gg(b, c, d, a, x[13], S24, 0x5a827999); /* 24 */ 218 gg(a, b, c, d, x[ 2], S21, 0x5a827999); /* 25 */ 219 gg(d, a, b, c, x[ 6], S22, 0x5a827999); /* 26 */ 220 gg(c, d, a, b, x[10], S23, 0x5a827999); /* 27 */ 221 gg(b, c, d, a, x[14], S24, 0x5a827999); /* 28 */ 222 gg(a, b, c, d, x[ 3], S21, 0x5a827999); /* 29 */ 223 gg(d, a, b, c, x[ 7], S22, 0x5a827999); /* 30 */ 224 gg(c, d, a, b, x[11], S23, 0x5a827999); /* 31 */ 225 gg(b, c, d, a, x[15], S24, 0x5a827999); /* 32 */ 226 227 /* Round 3 */ 228 hh(a, b, c, d, x[ 0], S31, 0x6ed9eba1); /* 33 */ 229 hh(d, a, b, c, x[ 8], S32, 0x6ed9eba1); /* 34 */ 230 hh(c, d, a, b, x[ 4], S33, 0x6ed9eba1); /* 35 */ 231 hh(b, c, d, a, x[12], S34, 0x6ed9eba1); /* 36 */ 232 hh(a, b, c, d, x[ 2], S31, 0x6ed9eba1); /* 37 */ 233 hh(d, a, b, c, x[10], S32, 0x6ed9eba1); /* 38 */ 234 hh(c, d, a, b, x[ 6], S33, 0x6ed9eba1); /* 39 */ 235 hh(b, c, d, a, x[14], S34, 0x6ed9eba1); /* 40 */ 236 hh(a, b, c, d, x[ 1], S31, 0x6ed9eba1); /* 41 */ 237 hh(d, a, b, c, x[ 9], S32, 0x6ed9eba1); /* 42 */ 238 hh(c, d, a, b, x[ 5], S33, 0x6ed9eba1); /* 43 */ 239 hh(b, c, d, a, x[13], S34, 0x6ed9eba1); /* 44 */ 240 hh(a, b, c, d, x[ 3], S31, 0x6ed9eba1); /* 45 */ 241 hh(d, a, b, c, x[11], S32, 0x6ed9eba1); /* 46 */ 242 hh(c, d, a, b, x[ 7], S33, 0x6ed9eba1); /* 47 */ 243 hh(b, c, d, a, x[15], S34, 0x6ed9eba1); /* 48 */ 244 245 context[0] += a; 246 context[1] += b; 247 context[2] += c; 248 context[3] += d; 249 250 x[] = 0; 251 } 252 253 /*********************************************************************** 254 255 ***********************************************************************/ 256 257 protected static uint f(uint x, uint y, uint z) 258 { 259 return (x&y)|(~x&z); 260 } 261 262 /*********************************************************************** 263 264 ***********************************************************************/ 265 266 protected static uint h(uint x, uint y, uint z) 267 { 268 return x^y^z; 269 } 270 271 /*********************************************************************** 272 273 ***********************************************************************/ 274 275 private static uint g(uint x, uint y, uint z) 276 { 277 return (x&y)|(x&z)|(y&z); 278 } 279 280 /*********************************************************************** 281 282 ***********************************************************************/ 283 284 private static void ff(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac) 285 { 286 a += f(b, c, d) + x + ac; 287 a = rotateLeft(a, s); 288 } 289 290 /*********************************************************************** 291 292 ***********************************************************************/ 293 294 private static void gg(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac) 295 { 296 a += g(b, c, d) + x + ac; 297 a = rotateLeft(a, s); 298 } 299 300 /*********************************************************************** 301 302 ***********************************************************************/ 303 304 private static void hh(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac) 305 { 306 a += h(b, c, d) + x + ac; 307 a = rotateLeft(a, s); 308 } 309 310 /*********************************************************************** 311 312 ***********************************************************************/ 313 314 private static const(uint[4]) initial = 315 [ 316 0x67452301, 317 0xefcdab89, 318 0x98badcfe, 319 0x10325476 320 ]; 321 322 /*********************************************************************** 323 324 ***********************************************************************/ 325 326 private static enum 327 { 328 S11 = 3, 329 S12 = 7, 330 S13 = 11, 331 S14 = 19, 332 S21 = 3, 333 S22 = 5, 334 S23 = 9, 335 S24 = 13, 336 S31 = 3, 337 S32 = 9, 338 S33 = 11, 339 S34 = 15, 340 } 341 } 342 343 344 /******************************************************************************* 345 346 *******************************************************************************/ 347 348 unittest 349 { 350 static istring[] strings = [ 351 "", 352 "a", 353 "abc", 354 "message digest", 355 "abcdefghijklmnopqrstuvwxyz", 356 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 357 "12345678901234567890123456789012345678901234567890123456789012345678901234567890" 358 ]; 359 360 static istring[] results = [ 361 "31d6cfe0d16ae931b73c59d7e0c089c0", 362 "bde52cb31de33e46245e05fbdbd6fb24", 363 "a448017aaf21d8525fc10ae87aa6729d", 364 "d9130a8164549fe818874806e1c7014b", 365 "d79e1c308aa5bbcdeea8ed63df412da9", 366 "043f8582f241db351ce627e153e7f0e4", 367 "e33b4ddc9c38f2199c3e7b164fcc0536" 368 ]; 369 370 Md4 h = new Md4(); 371 372 foreach (i, s; strings) 373 { 374 h.update(s); 375 char[] d = h.hexDigest; 376 test(d == results[i],":("~s~")("~d~")!=("~results[i]~")"); 377 } 378 }