//
//
//This file contains an implementation of the MD4 hash algorithm.
//The implementation was derived from the example source code that
//is provided in RFC 1320 - "The MD4 Message-Digest Algorithm".
//
//The code has been ported from C to C#.
//The original copyright is preserved here.
//
//
// 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. MD4 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. MD4 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.
//
//
using System;
using System.Diagnostics;
using System.Text;
namespace System.Security.Cryptography
{
///
/// This class implements the MD4 message digest algorithm.
/// The implementation is a C# port of the original RSA C implementation that is
/// provided in RFC 1320.
///
public class MD4Context
{
public MD4Context()
{
Reset();
}
public const int DigestLength = 16;
///
/// This field is "true" if GetDigest has been called.
/// It cannot be called again, unless Reset is called.
///
bool _done;
public void Clear()
{
_state0 = 0;
_state1 = 0;
_state2 = 0;
_state3 = 0;
_count = 0;
for (int i = 0; i < 64; i++)
_buffer[i] = 0;
}
///
/// This method can be used to reset the state of the MD4 context. After returning, the
/// state of the MD4Context is equivalent to its state immediately after the constructor
/// finished.
///
public void Reset()
{
// Load magic initialization constants.
_count = 0;
_state0 = 0x67452301;
_state1 = 0xefcdab89;
_state2 = 0x98badcfe;
_state3 = 0x10325476;
_done = false;
}
//
//MD4 basic transformation. Transforms state based on block.
//
void Transform(byte[]/*!*/ block, int offset)
{
if (offset < 0)
throw new ArgumentException("offset cannot be negative.");
Decode(_block, 0, block, offset, 64);
Transform(_block);
// Zeroize sensitive information.
// MD4_memset ((POINTER)x, 0, sizeof (x));
}
#region Constants for MD4Transform routine.
const int S11 = 3;
const int S12 = 7;
const int S13 = 11;
const int S14 = 19;
const int S21 = 3;
const int S22 = 5;
const int S23 = 9;
const int S24 = 13;
const int S31 = 3;
const int S32 = 9;
const int S33 = 11;
const int S34 = 15;
#endregion
///
/// This routine transforms the current MD4 context, using a block of input.
/// The input is 16 words, where each word is 32 unsigned bits. This method
/// is the core of the MD4 algorithm.
///
/// The message data to use to transform the context.
void Transform(uint[] x)
{
uint a = _state0;
uint b = _state1;
uint c = _state2;
uint d = _state3;
// Round 1
a = FF(a, b, c, d, x[00], S11); // 1
d = FF(d, a, b, c, x[01], S12); // 2
c = FF(c, d, a, b, x[02], S13); // 3
b = FF(b, c, d, a, x[03], S14); // 4
a = FF(a, b, c, d, x[04], S11); // 5
d = FF(d, a, b, c, x[05], S12); // 6
c = FF(c, d, a, b, x[06], S13); // 7
b = FF(b, c, d, a, x[07], S14); // 8
a = FF(a, b, c, d, x[08], S11); // 9
d = FF(d, a, b, c, x[09], S12); // 10
c = FF(c, d, a, b, x[10], S13); // 11
b = FF(b, c, d, a, x[11], S14); // 12
a = FF(a, b, c, d, x[12], S11); // 13
d = FF(d, a, b, c, x[13], S12); // 14
c = FF(c, d, a, b, x[14], S13); // 15
b = FF(b, c, d, a, x[15], S14); // 16
// Round 2
a = GG(a, b, c, d, x[00], S21); // 17
d = GG(d, a, b, c, x[04], S22); // 18
c = GG(c, d, a, b, x[08], S23); // 19
b = GG(b, c, d, a, x[12], S24); // 20
a = GG(a, b, c, d, x[01], S21); // 21
d = GG(d, a, b, c, x[05], S22); // 22
c = GG(c, d, a, b, x[09], S23); // 23
b = GG(b, c, d, a, x[13], S24); // 24
a = GG(a, b, c, d, x[02], S21); // 25
d = GG(d, a, b, c, x[06], S22); // 26
c = GG(c, d, a, b, x[10], S23); // 27
b = GG(b, c, d, a, x[14], S24); // 28
a = GG(a, b, c, d, x[03], S21); // 29
d = GG(d, a, b, c, x[07], S22); // 30
c = GG(c, d, a, b, x[11], S23); // 31
b = GG(b, c, d, a, x[15], S24); // 32
// Round 3
a = HH(a, b, c, d, x[00], S31); // 33
d = HH(d, a, b, c, x[08], S32); // 34
c = HH(c, d, a, b, x[04], S33); // 35
b = HH(b, c, d, a, x[12], S34); // 36
a = HH(a, b, c, d, x[02], S31); // 37
d = HH(d, a, b, c, x[10], S32); // 38
c = HH(c, d, a, b, x[06], S33); // 39
b = HH(b, c, d, a, x[14], S34); // 40
a = HH(a, b, c, d, x[01], S31); // 41
d = HH(d, a, b, c, x[09], S32); // 42
c = HH(c, d, a, b, x[05], S33); // 43
b = HH(b, c, d, a, x[13], S34); // 44
a = HH(a, b, c, d, x[03], S31); // 45
d = HH(d, a, b, c, x[11], S32); // 46
c = HH(c, d, a, b, x[07], S33); // 47
b = HH(b, c, d, a, x[15], S34); // 48
_state0 = unchecked(_state0 + a);
_state1 = unchecked(_state1 + b);
_state2 = unchecked(_state2 + c);
_state3 = unchecked(_state3 + d);
}
public const int BytesPerTransform = 64;
public const int WordsPerTransform = 16;
uint _state0; // state A
uint _state1; // state B
uint _state2; // state C
uint _state3; // state D
Int64 _count; // number of bits, modulo 2^64
///
/// This buffer holds data that was submitted using the Update method, but which
/// was too short to complete a full transform block (64 bytes).
///
readonly byte[]/*!*/ _buffer = new byte[BytesPerTransform];
///
/// This buffer contains the current block (of message data) that is being transformed.
///
readonly uint[]/*!*/ _block = new uint[WordsPerTransform];
static MD4Context()
{
PADDING = new byte[BytesPerTransform];
Array.Clear(PADDING, 0, 64);
PADDING[0] = 0x80;
}
static readonly byte[]/*!*/ PADDING;
//
//{
// 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
//};
//
// F, G and H are basic MD4 functions.
//
static uint F(uint x, uint y, uint z) { return (x & y) | ((~x) & z); }
static uint G(uint x, uint y, uint z) { return (x & y) | (x & z) | (y & z); }
static uint H(uint x, uint y, uint z) { return x ^ y ^ z; }
// ROTATE_LEFT rotates x left n bits.
//
static uint RotateLeft(uint x, int n)
{
return (x << n) | (x >> (32 - n));
}
// FF, GG and HH are transformations for rounds 1, 2 and 3
// Rotation is separate from addition to prevent recomputation
// (Or at least it used to be, when these were C macros.
static uint FF(uint a, uint b, uint c, uint d, uint x, int s)
{
a = unchecked(a + F(b, c, d) + x);
return RotateLeft(a, s);
}
static uint GG(uint a, uint b, uint c, uint d, uint x, int s)
{
a = unchecked(a + G(b, c, d) + x + (uint)0x5a827999);
return RotateLeft(a, s);
}
static uint HH(uint a, uint b, uint c, uint d, uint x, int s)
{
a = unchecked(a + H(b, c, d) + x + (uint)0x6ed9eba1);
return RotateLeft(a, s);
}
// Decodes input (unsigned char) into output (uint). Assumes len is
// a multiple of 4.
//
//
///
///
///
///
///
///
///
/// number of BYTES to transfer
static void Decode(uint[]/*!*/ output, int outputoffset, byte[]/*!*/ input, int inputoffset, int length)
{
Debug.Assert(length % 4 == 0);
int outpos = outputoffset;
for (int j = 0; j < length; j += 4) {
uint value = ((uint)input[inputoffset + j])
| (((uint)input[inputoffset + j + 1]) << 8)
| (((uint)input[inputoffset + j + 2]) << 16)
| (((uint)input[inputoffset + j + 3]) << 24);
output[outpos] = value;
outpos++;
}
}
static void EncodeLe(uint value, byte[]/*!*/ output, int pos)
{
output[pos] = (byte)(value & 0xff);
output[pos + 1] = (byte)((value >> 8) & 0xff);
output[pos + 2] = (byte)((value >> 16) & 0xff);
output[pos + 3] = (byte)((value >> 24) & 0xff);
}
///
/// MD4 block update operation. Continues an MD4 message-digest operation,
/// processing another message block, and updating the context.
///
/// The buffer containing data to process.
/// The offset within the buffer where the data begins.
/// The length of the data.
public void Update(byte[]/*!*/ input, int offset, int length)
{
if (_done)
throw new InvalidOperationException("The hash context has been closed (GetDigest has been called). It cannot be reused until Reset is called.");
if (length == 0)
return;
// Compute number of bytes mod 64
int index = (int)((_count >> 3) & 0x3F);
_count += length << 3;
int partLen = 64 - index;
// Transform as many times as possible.
//
int i;
if (length >= partLen) {
// MD4_memcpy((POINTER)&buffer_pinned[index], (POINTER)&input[offset], partLen);
Array.Copy(input, offset, _buffer, index, partLen);
Transform(_buffer, 0);
for (i = partLen; i + 63 < length; i += 64)
Transform(input, offset + i);
index = 0;
}
else
i = 0;
// Buffer remaining input
// MD4_memcpy((POINTER)&_buffer[index], (POINTER)&input[i], inputLen-i);
// MD4_memcpy((POINTER)&buffer_pinned[index], (POINTER)&input[offset + i], inputLen-i);
Array.Copy(input, offset + i, _buffer, index, length - i);
}
public static MD4Digest GetDigest(byte[]/*!*/ buffer)
{
return GetDigest(buffer, 0, buffer.Length);
}
public static MD4Digest GetDigest(byte[]/*!*/ buffer, int offset, int length)
{
MD4Context context = new MD4Context();
context.Update(buffer, offset, length);
return context.GetDigest();
}
// MD4 finalization. Ends an MD4 message-digest operation, writing the
// the message digest and zeroizing the context.
//
public MD4Digest GetDigest()
{
if (_done)
throw new InvalidOperationException("GetDigest cannot be called twice for a single hash sequence. Call Reset() to reset the context.");
byte[]/*!*/ bits = new byte[8];
// Save number of bits
EncodeLe((uint)(_count & 0xffffffffu), bits, 0);
EncodeLe((uint)(_count >> 32), bits, 4);
// Pad out to 56 mod 64.
int index = (int)((_count >> 3) & 0x3f);
int padLen = (index < 56) ? (56 - index) : (120 - index);
Update(PADDING, 0, padLen);
// Append length (before padding)
Update(bits, 0, 8);
// Store state in digest
MD4Digest digest;
digest.state0 = _state0;
digest.state1 = _state1;
digest.state2 = _state2;
digest.state3 = _state3;
Clear();
_done = true;
return digest;
}
}
public struct MD4Digest
{
public uint state0;
public uint state1;
public uint state2;
public uint state3;
static void PackLe(byte[]/*!*/ dest, int offset, uint value)
{
dest[offset + 0] = (byte)((value) & 0xff);
dest[offset + 1] = (byte)((value >> 8) & 0xff);
dest[offset + 2] = (byte)((value >> 16) & 0xff);
dest[offset + 3] = (byte)((value >> 24) & 0xff);
}
public const int DigestLength = 16;
public byte[]/*!*/ ToArray()
{
byte[] result = new byte[DigestLength];
ToArray(result);
return result;
}
public void ToArray(byte[]/*!*/ result)
{
if (result.Length < 0x10)
throw new ArgumentException("Input array is too short.");
PackLe(result, 0, state0);
PackLe(result, 4, state1);
PackLe(result, 8, state2);
PackLe(result, 12, state3);
}
public static implicit operator byte[]/*!*/(MD4Digest digest)
{
return digest.ToArray();
}
override public string/*!*/ ToString()
{
string hex = "0123456789abcdef";
byte[] arr = ToArray();
StringBuilder buffer = new StringBuilder(DigestLength * 2);
for (int i = 0; i < 0x10; i++) {
byte b = arr[i];
buffer.Append(hex[b >> 4]);
buffer.Append(hex[b & 0xf]);
}
return buffer.ToString();
}
}
}