source: trunk/CrypPlugins/MD5/PresentableMd5.cs @ 853

Last change on this file since 853 was 853, checked in by pretzsch, 12 years ago

+ MD5 plugin: Added Presentation helper classes

File size: 17.3 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Security.Cryptography;
6using System.IO;
7
8namespace Cryptool.MD5
9{
10    public class PresentableMd5
11    {
12        public List<PresentableMd5State> StateHistory { get; set; }
13
14        public PresentableMd5State CurrentState { get; set; }
15
16        public int CurrentStateNumber { get; protected set; }
17
18        protected Stream DataStream { get; set; }
19
20        public bool IsInitialized { get; protected set; }
21
22        protected static readonly uint[] AdditionConstantTable = new uint[64] 
23                        {       0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
24                                0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
25                0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
26                0x6b901122,0xfd987193,0xa679438e,0x49b40821,
27                                0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
28                0xd62f105d,0x2441453,0xd8a1e681,0xe7d3fbc8,
29                0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
30                                0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
31                0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
32                0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
33                0x289b7ec6,0xeaa127fa,0xd4ef3085,0x4881d05,
34                                0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
35                0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
36                0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
37                0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
38                                0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391     };
39
40        protected static readonly ushort[] ShiftConstantTable = new ushort[64] 
41                        {       7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
42                                5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
43                4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
44                6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21      };
45
46        protected const int DATA_BLOCK_SIZE = 64;
47
48        public PresentableMd5State LastState
49        {
50            get
51            {
52                if (CurrentStateNumber == 0)
53                    return null;
54                else
55                    return StateHistory[CurrentStateNumber - 1];
56            }
57        }
58
59        public PresentableMd5(Stream dataStream)
60        {
61            Initialize(dataStream);
62        }
63
64        public PresentableMd5()
65        {
66        }
67
68        public void Initialize(Stream dataStream)
69        {
70            StateHistory = new List<PresentableMd5State>();
71
72            DataStream = dataStream;
73
74            InitFirstState();
75
76            IsInitialized = true;
77        }
78
79        protected void AddNewState()
80        {
81            if (CurrentStateNumber == -1)
82                CurrentState = new PresentableMd5State();
83            else
84                CurrentState = new PresentableMd5State(StateHistory[CurrentStateNumber]);
85
86            StateHistory.Add(CurrentState);
87            CurrentStateNumber = StateHistory.Count - 1;
88        }
89
90        public bool HistoryHasMoreStates
91        {
92            get
93            {
94                if (!IsInitialized)
95                    return false;
96                else
97                    return StateHistory.Count - 1 > CurrentStateNumber;
98            }
99        }
100
101        public void PreviousStep()
102        {
103            if (CurrentStateNumber == 0)
104                return;
105
106            CurrentStateNumber--;
107            CurrentState = StateHistory[CurrentStateNumber];
108        }
109
110        public bool IsInFinishedState
111        {
112            get
113            {
114                return CurrentState.State == PresentableMd5State.Md5State.FINISHED;
115            }
116        }
117
118        public void NextStep()
119        {
120            if (HistoryHasMoreStates)
121            {
122                CurrentStateNumber++;
123                CurrentState = StateHistory[CurrentStateNumber];
124            }
125            else
126            {
127                if (IsInFinishedState)
128                    return;
129
130                PresentableMd5State previousState = CurrentState;
131                AddNewState();
132                PerformStep(previousState, CurrentState);
133            }
134        }
135
136        public void NextStepUntilFinished()
137        {
138            while (!IsInFinishedState)
139                NextStep();
140        }
141
142        public void NextStepUntilRoundEnd()
143        {
144            while (!IsInFinishedState && CurrentState.State != PresentableMd5State.Md5State.FINISHED_ROUND)
145                NextStep();
146        }
147
148        public void NextStepUntilBlockEnd()
149        {
150            while (!IsInFinishedState && CurrentState.State != PresentableMd5State.Md5State.FINISHED_COMPRESSION)
151                NextStep();
152        }
153
154        public void PerformStep(PresentableMd5State previousState, PresentableMd5State newState)
155        {
156            switch (previousState.State)
157            {
158                case PresentableMd5State.Md5State.INITIALIZED:
159                    // If initialization is complete, start by reading data
160                    newState.State = PresentableMd5State.Md5State.READING_DATA;
161                    break;
162
163                case PresentableMd5State.Md5State.READING_DATA:
164                    // Read data and enter "data read" state
165                    ReadData(newState);
166                    newState.State = PresentableMd5State.Md5State.READ_DATA;
167                    break;
168
169                case PresentableMd5State.Md5State.READ_DATA:
170                    // If an underfull buffer was read, we're at the end of the digestible data, so enter "starting padding" state
171                    // If a full buffer was read, enter "starting compression" state
172                    if (previousState.DataLength < DATA_BLOCK_SIZE)
173                        newState.State = PresentableMd5State.Md5State.STARTING_PADDING;
174                    else
175                        newState.State = PresentableMd5State.Md5State.STARTING_COMPRESSION;
176                    break;
177
178                case PresentableMd5State.Md5State.STARTING_PADDING:
179                    // First step of padding is adding the padding bytes, so enter that state
180                    newState.State = PresentableMd5State.Md5State.ADDING_PADDING_BYTES;
181                    break;
182
183                case PresentableMd5State.Md5State.ADDING_PADDING_BYTES:
184                    // Add necessary number of bytes and enter "added padding bytes" state
185                    AddPaddingBytes(newState);
186                    newState.State = PresentableMd5State.Md5State.ADDED_PADDING_BYTES;
187                    break;
188
189                case PresentableMd5State.Md5State.ADDED_PADDING_BYTES:
190                    // The next step for padding is adding the data length, so enter that state
191                    newState.State = PresentableMd5State.Md5State.ADDING_LENGTH;
192                    break;
193
194                case PresentableMd5State.Md5State.ADDING_LENGTH:
195                    // Add the length of the data and enter "added length" state
196                    AddLength(newState);
197                    newState.State = PresentableMd5State.Md5State.ADDED_LENGTH;
198                    break;
199
200                case PresentableMd5State.Md5State.ADDED_LENGTH:
201                    // Padding is done after adding data length, so enter "finished padding" state
202                    newState.State = PresentableMd5State.Md5State.FINISHED_PADDING;
203                    break;
204
205                case PresentableMd5State.Md5State.FINISHED_PADDING:
206                    // If padding is finished, call compression function for the last (two) time(s)
207                    newState.State = PresentableMd5State.Md5State.STARTING_COMPRESSION;
208                    break;
209
210                case PresentableMd5State.Md5State.STARTING_COMPRESSION:
211                    StartCompression(newState);
212                    newState.State = PresentableMd5State.Md5State.STARTING_ROUND;
213                    break;
214
215                case PresentableMd5State.Md5State.STARTING_ROUND:
216                    StartRound(newState);
217                    newState.State = PresentableMd5State.Md5State.STARTING_ROUND_STEP;
218                    break;
219
220                case PresentableMd5State.Md5State.STARTING_ROUND_STEP:
221                    PerformRoundStep(newState);
222                    newState.State = PresentableMd5State.Md5State.FINISHED_ROUND_STEP;
223                    break;
224
225                case PresentableMd5State.Md5State.FINISHED_ROUND_STEP:
226                    if (previousState.IsLastStepInRound)
227                        newState.State = PresentableMd5State.Md5State.FINISHED_ROUND;
228                    else
229                    {
230                        newState._RoundStepIndex++;
231                        newState.State = PresentableMd5State.Md5State.STARTING_ROUND_STEP;
232                    }
233                    break;
234
235                case PresentableMd5State.Md5State.FINISHED_ROUND:
236                    if (previousState.IsLastRound)
237                        newState.State = PresentableMd5State.Md5State.FINISHING_COMPRESSION;
238                    else
239                    {
240                        newState._RoundIndex++;
241                        newState.State = PresentableMd5State.Md5State.STARTING_ROUND;
242                    }
243                    break;
244
245                case PresentableMd5State.Md5State.FINISHING_COMPRESSION:
246                    FinishCompression(newState);
247                    newState.State = PresentableMd5State.Md5State.FINISHED_COMPRESSION;
248                    break;
249
250                case PresentableMd5State.Md5State.FINISHED_COMPRESSION:
251                    // If compression is finished, check if there's more data left in buffer. If so, reenter compression function with offset
252                    if (previousState.DataLength - previousState.DataOffset > DATA_BLOCK_SIZE)
253                    {
254                        // Still some data left in buffer, rerun compression with offset
255                        newState.DataOffset += DATA_BLOCK_SIZE;
256                        newState.State = PresentableMd5State.Md5State.STARTING_COMPRESSION;
257                    }
258                    else
259                    {
260                        // No data left in buffer
261
262                        if (previousState.IsPaddingDone)
263                        {
264                            // If padding was already added, we're done
265                            newState.State = PresentableMd5State.Md5State.FINISHED;
266                        }
267                        else
268                        {
269                            // Read more data
270                            newState.State = PresentableMd5State.Md5State.READING_DATA;
271                        }
272                    }
273                    break;
274            }
275        }
276
277        private void FinishCompression(PresentableMd5State newState)
278        {
279            newState.H1 += newState.A;
280            newState.H2 += newState.B;
281            newState.H3 += newState.C;
282            newState.H4 += newState.D;
283
284            newState.BytesHashed += DATA_BLOCK_SIZE;
285        }
286
287        private void ReadData(PresentableMd5State newState)
288        {
289            newState.Data = new byte[128];
290            newState.DataLength = (uint)DataStream.Read(newState.Data, 0, 64);
291            newState.DataOffset = 0;
292        }
293
294        private void StartRound(PresentableMd5State newState)
295        {
296            newState._RoundStepIndex = 0;
297        }
298
299        private void StartCompression(PresentableMd5State newState)
300        {
301            newState.X = new uint[16];
302
303            for (uint j = 0; j < 64; j += 4)
304            {
305                newState.X[j / 4] = (((uint)newState.Data[newState.DataOffset + (j + 3)]) << 24) |
306                        (((uint)newState.Data[newState.DataOffset + (j + 2)]) << 16) |
307                        (((uint)newState.Data[newState.DataOffset + (j + 1)]) << 8) |
308                        (((uint)newState.Data[newState.DataOffset + (j)]));
309            }
310
311            newState._RoundIndex = 0;
312
313            newState.A = newState.H1;
314            newState.B = newState.H2;
315            newState.C = newState.H3;
316            newState.D = newState.H4;
317        }
318
319        private void AddLength(PresentableMd5State newState)
320        {
321            uint lengthOffset = newState.DataLength + 8;
322
323            for (int i = 8; i > 0; i--)
324                newState.Data[lengthOffset - i] = (byte)(newState.LengthInBit >> ((8 - i) * 8) & 0xff);
325
326            newState.IsPaddingDone = true;
327        }
328
329        private void AddPaddingBytes(PresentableMd5State newState)
330        {
331            // Save length of data in bit
332            newState.LengthInBit = (newState.BytesHashed + newState.DataLength) * 8;
333
334            // Add '1' bit to end of data
335            newState.Data[newState.DataLength] = 0x80;
336            newState.DataLength++;
337
338            // Add zero bytes until 8 bytes short of next 64-byte block
339            while (newState.DataLength % 64 != 56)
340            {
341                newState.Data[newState.DataLength++] = 0;
342            }
343        }
344
345        public void InitFirstState()
346        {
347            Console.WriteLine("InitFirstState()");
348
349            StateHistory.Clear();
350            CurrentStateNumber = -1;
351            AddNewState();
352
353            CurrentState.BytesHashed = 0;
354            CurrentState.H1 = 0x67452301;
355            CurrentState.H2 = 0xEFCDAB89;
356            CurrentState.H3 = 0x98BADCFE;
357            CurrentState.H4 = 0X10325476;
358            CurrentState.State = PresentableMd5State.Md5State.INITIALIZED;
359        }
360
361        public static uint RotateLeft(uint uiNumber, ushort shift)
362        {
363            return (uiNumber << shift) | (uiNumber >> (32 - shift));
364        }
365
366        protected delegate uint RoundFunction(uint a, uint b, uint c, uint d);
367        protected readonly RoundFunction[] ROUND_FUNCTION = { FuncF, FuncG, FuncH, FuncI };
368
369        private void PerformRoundStep(PresentableMd5State newState)
370        {
371            Console.WriteLine("Before R {0} S {1,2}: A = {2,10} B = {3,10} C = {4,10} D = {5,10}", newState.Round, newState.RoundStep, newState.A, newState.B, newState.C, newState.D);
372
373            RoundFunction roundFunction = ROUND_FUNCTION[newState._RoundIndex];
374
375            uint i = newState._RoundIndex * 16 + newState._RoundStepIndex;
376
377            uint wordIndex;
378            switch (newState._RoundIndex)
379            {
380                default:
381                case 0:
382                    wordIndex = i;
383                    break;
384                case 1:
385                    wordIndex = 5 * i + 1;
386                    break;
387                case 2:
388                    wordIndex = 3 * i + 5;
389                    break;
390                case 3:
391                    wordIndex = 7 * i;
392                    break;
393            }
394            wordIndex %= 16;
395
396            switch (newState._RoundStepIndex % 4)
397            {
398                case 0:
399                    ExecRoundFunction(ref newState.A, newState.B, newState.C, newState.D, roundFunction, newState.X[wordIndex], i);
400                    break;
401                case 1:
402                    ExecRoundFunction(ref newState.D, newState.A, newState.B, newState.C, roundFunction, newState.X[wordIndex], i);
403                    break;
404                case 2:
405                    ExecRoundFunction(ref newState.C, newState.D, newState.A, newState.B, roundFunction, newState.X[wordIndex], i);
406                    break;
407                case 3:
408                    ExecRoundFunction(ref newState.B, newState.C, newState.D, newState.A, roundFunction, newState.X[wordIndex], i);
409                    break;
410            }
411
412            if (newState.IsLastRound && newState.IsLastStepInRound)
413
414                Console.WriteLine("After  R {0} S {1,2}: A = {2,10} B = {3,10} C = {4,10} D = {5,10}", newState.Round, newState.RoundStep, newState.A, newState.B, newState.C, newState.D);
415        }
416
417        protected static void ExecRoundFunction(ref uint a, uint b, uint c, uint d, RoundFunction function, uint W, uint i)
418        {
419            a = b + RotateLeft((a + function(a, b, c, d) + W + AdditionConstantTable[i]), ShiftConstantTable[i]);
420        }
421
422        protected static uint FuncF(uint a, uint b, uint c, uint d)
423        {
424            return d ^ (b & (c ^ d));
425        }
426
427        protected static uint FuncG(uint a, uint b, uint c, uint d)
428        {
429            return c ^ (d & (b ^ c));
430        }
431
432        protected static uint FuncH(uint a, uint b, uint c, uint d)
433        {
434            return b ^ c ^ d;
435        }
436
437        protected static uint FuncI(uint a, uint b, uint c, uint d)
438        {
439            return c ^ (b | ~d);
440        }
441
442        public byte[] HashValueBytes
443        {
444            get
445            {
446                byte[] result = new byte[16];
447                writeUintToArray(result, 0, CurrentState.H1);
448                writeUintToArray(result, 4, CurrentState.H2);
449                writeUintToArray(result, 8, CurrentState.H3);
450                writeUintToArray(result, 12, CurrentState.H4);
451                return result;
452            }
453        }
454
455        protected void writeUintToArray(byte[] array, int offset, uint value)
456        {
457            byte[] byteValue = BitConverter.GetBytes(value);
458            Array.Copy(byteValue, 0, array, offset, 4);
459        }
460    }
461}
Note: See TracBrowser for help on using the repository browser.