source: trunk/CrypPlugins/MD5/Algorithm/PresentableMD5.cs @ 970

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

+ MD5 plugin: Improved presentation

File size: 29.9 KB
Line 
1/*
2   Copyright 2009 Holger Pretzsch, University of Duisburg-Essen
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17using System;
18using System.Collections.Generic;
19using System.Linq;
20using System.Text;
21using System.Security.Cryptography;
22using System.IO;
23using System.ComponentModel;
24
25namespace Cryptool.MD5.Algorithm
26{
27    /// <summary>
28    /// Implements an MD5 algorithm that can be executed step by step and allows inspection of state anytime during execution
29    /// </summary>
30    public class PresentableMD5 : INotifyPropertyChanged
31    {
32        /// <summary>
33        /// A list containing all states that have already been calculated
34        /// </summary>
35        public List<PresentableMD5State> StateHistory { get; set; }
36
37        /// <summary>
38        /// The current state of the algorithm
39        /// </summary>
40        /// <seealso cref="PresentableMD5State"/>
41        public PresentableMD5State CurrentState { get; protected set; }
42
43        /// <summary>
44        /// Sequential number identifying the current state of the algorithm
45        /// </summary>
46        public int CurrentStateNumber { get; protected set; }
47
48        /// <summary>
49        /// The stream where data is read from
50        /// </summary>
51        protected Stream DataStream { get; set; }
52
53        /// <summary>
54        /// Returns whether this object has been initialized using the Initialize() method
55        /// </summary>
56        public bool IsInitialized { get; protected set; }
57
58        /// <summary>
59        /// Array of integer constants, each one is used in one of the compression function's 64 steps
60        /// </summary>
61        internal static readonly uint[] AdditionConstantTable = new uint[64] 
62                        {       0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
63                                0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
64                0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
65                0x6b901122,0xfd987193,0xa679438e,0x49b40821,
66                                0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
67                0xd62f105d,0x2441453,0xd8a1e681,0xe7d3fbc8,
68                0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
69                                0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
70                0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
71                0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
72                0x289b7ec6,0xeaa127fa,0xd4ef3085,0x4881d05,
73                                0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
74                0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
75                0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
76                0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
77                                0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391     };
78
79        protected HashSet<MD5StateDescription> skippedStates = new HashSet<MD5StateDescription>();
80
81        /// <summary>
82        /// Array of 64 constants indicating how far the compression function's rotate operator shifts in each step
83        /// </summary>
84        internal static readonly ushort[] ShiftConstantTable = new ushort[64] 
85                        {       7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
86                                5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
87                4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
88                6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21      };
89
90        /// <summary>
91        /// Amount of bytes in one block of data
92        /// </summary>
93        protected const int DATA_BLOCK_SIZE = 64;
94
95        /// <summary>
96        /// The state before CurrentState, null if CurrentState is first state
97        /// </summary>
98        /// <seealso cref="PresentableMD5State"/>
99        public PresentableMD5State LastState
100        {
101            get
102            {
103                if (CurrentStateNumber == 0)
104                    return null;
105                else
106                    return StateHistory[CurrentStateNumber - 1];
107            }
108        }
109
110        public void ResetSkippedStates()
111        {
112            skippedStates.Clear();
113        }
114
115        public void AddSkippedState(MD5StateDescription state)
116        {
117            skippedStates.Add(state);
118        }
119
120        /// <summary>
121        /// Returns the next state by retrieving it from history or performing the next algorithm step without changing the current state
122        /// </summary>
123        /// <seealso cref="PresentableMD5State"/>
124        public PresentableMD5State NextState
125        {
126            get
127            {
128                if (IsInFinishedState)
129                    return CurrentState;
130
131                if (!HistoryHasMoreStates)
132                {
133                    PresentableMD5State previousState = CurrentState;
134                    AddNewState();
135                    PerformStep(previousState, CurrentState);
136
137                    CurrentStateNumber--;
138                    CurrentState = StateHistory[CurrentStateNumber];
139                }
140
141                return StateHistory[CurrentStateNumber + 1];
142            }
143        }
144
145        /// <summary>
146        /// Delegate for status changed handlers
147        /// </summary>
148        public delegate void StatusChangedHandler();
149
150        /// <summary>
151        /// Raised whenever the algorithm changes its status
152        /// </summary>
153        public event StatusChangedHandler StatusChanged;
154
155        /// <summary>
156        /// Raised whenever a property changes, important for WPF binding
157        /// </summary>
158        public event PropertyChangedEventHandler PropertyChanged;
159
160        /// <summary>
161        /// Wrapper that raises a PropertyChanged event
162        /// </summary>
163        /// <param name="propertyName">The property that has changed</param>
164        void OnPropChanged(string propertyName)
165        {
166            if (PropertyChanged != null)
167                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
168        }
169
170        /// <summary>
171        /// Raises a StatusChanged event and PropertyChanged events for important properties
172        /// </summary>
173        private void OnStatusChanged()
174        {
175            OnPropChanged("CurrentState");
176            OnPropChanged("LastState");
177            OnPropChanged("NextState");
178            OnPropChanged("CurrentStateNumber");
179            OnPropChanged("IsInFinishedState");
180            OnPropChanged("HashValueBytes");
181
182            if (StatusChanged != null)
183                StatusChanged();
184        }
185
186        /// <summary>
187        /// Constructs state history list and adds the first "uninitialized" state
188        /// </summary>
189        public PresentableMD5()
190        {
191            StateHistory = new List<PresentableMD5State>();
192            SetUninitializedState();
193        }
194
195        /// <summary>
196        /// Assigns a data source and initializes the algorithm, putting it into "initialized" state
197        /// </summary>
198        /// <param name="dataStream">Data source</param>
199        public void Initialize(Stream dataStream)
200        {
201            DataStream = dataStream;
202
203            SetUninitializedState();
204            PerformInitializationStep();
205
206            IsInitialized = true;
207
208            OnStatusChanged();
209        }
210
211        /// <summary>
212        /// Clears the state history and adds the "uninitialized" state
213        /// </summary>
214        private void SetUninitializedState()
215        {
216            StateHistory.Clear();
217
218            PresentableMD5State uninitializedState = new PresentableMD5State();
219            uninitializedState.State = MD5StateDescription.UNINITIALIZED;
220            StateHistory.Add(uninitializedState);
221            CurrentState = uninitializedState;
222
223            CurrentStateNumber = 0;
224        }
225
226        /// <summary>
227        /// Adds a new state to the history and sets it as the current state
228        /// </summary>
229        protected void AddNewState()
230        {
231            if (CurrentStateNumber == -1)
232                CurrentState = new PresentableMD5State();
233            else
234                CurrentState = new PresentableMD5State(StateHistory[CurrentStateNumber]);
235
236            StateHistory.Add(CurrentState);
237            CurrentStateNumber = StateHistory.Count - 1;
238        }
239
240        /// <summary>
241        /// Determine if there are states beyond the current one available in the history
242        /// </summary>
243        public bool HistoryHasMoreStates
244        {
245            get
246            {
247                if (!IsInitialized)
248                    return false;
249                else
250                    return StateHistory.Count - 1 > CurrentStateNumber;
251            }
252        }
253
254        /// <summary>
255        /// Navigate one step back using the state history
256        /// </summary>
257        public void PreviousStep()
258        {
259            if (CurrentStateNumber == 0)
260                return;
261
262            CurrentStateNumber--;
263            CurrentState = StateHistory[CurrentStateNumber];
264            OnStatusChanged();
265
266            if (!IsInFinishedState && skippedStates.Contains(CurrentState.State))
267                PreviousStep();
268        }
269
270        /// <summary>
271        /// Determines whether the current state is the "finished" state
272        /// </summary>
273        public bool IsInFinishedState
274        {
275            get
276            {
277                return CurrentState.State == MD5StateDescription.FINISHED;
278            }
279        }
280
281        /// <summary>
282        /// Determines whether the current state is the first, "uninitialized", state
283        /// </summary>
284        public bool IsInFirstState
285        {
286            get
287            {
288                return CurrentStateNumber == 0;
289            }
290        }
291
292        /// <summary>
293        /// Goes one step forward by either restoring a previously calculated state from the history or calculating the next state
294        /// </summary>
295        public void NextStep()
296        {
297            if (!IsInitialized)
298                return;
299
300            if (IsInFinishedState)
301                return;
302
303            if (HistoryHasMoreStates)
304            {
305                CurrentStateNumber++;
306                CurrentState = StateHistory[CurrentStateNumber];
307                OnStatusChanged();
308            }
309            else
310            {
311                PresentableMD5State previousState = CurrentState;
312                AddNewState();
313                PerformStep(previousState, CurrentState);
314                OnStatusChanged();
315            }
316
317            if (!IsInFinishedState && skippedStates.Contains(CurrentState.State))
318                NextStep();
319        }
320
321        /// <summary>
322        /// Moves through the algorithm's steps until it is finished
323        /// </summary>
324        public void NextStepUntilFinished()
325        {
326            if (!IsInitialized)
327                return;
328
329            while (!IsInFinishedState)
330                NextStep();
331        }
332
333        /// <summary>
334        /// Moves one or more steps through the algorithm until the next "finished round" state
335        /// </summary>
336        public void NextStepUntilRoundEnd()
337        {
338            if (!IsInitialized)
339                return;
340
341            do
342                NextStep();
343            while (!IsInFinishedState && CurrentState.State != MD5StateDescription.FINISHED_ROUND);
344        }
345
346
347        /// <summary>
348        /// Moves one or more steps through the algorithm until the next "finished compression" state
349        /// </summary>
350        public void NextStepUntilBlockEnd()
351        {
352            if (!IsInitialized)
353                return;
354
355            do
356                NextStep();
357            while (!IsInFinishedState && CurrentState.State != MD5StateDescription.FINISHED_COMPRESSION);
358        }
359
360        /// <summary>
361        /// Performs the next step in the algorithm
362        /// </summary>
363        /// <param name="previousState">Previous state</param>
364        /// <param name="newState">The new state which is to be determined</param>
365        public void PerformStep(PresentableMD5State previousState, PresentableMD5State newState)
366        {
367            switch (previousState.State)
368            {
369                case MD5StateDescription.INITIALIZED:
370                    // Start by reading data
371                    newState.State = MD5StateDescription.READING_DATA;
372                    break;
373
374                case MD5StateDescription.READING_DATA:
375                    // Fetch next data block and enter "data read" state
376                    ReadData(newState);
377                    newState.State = MD5StateDescription.READ_DATA;
378                    break;
379
380                case MD5StateDescription.READ_DATA:
381                    // If an underfull buffer was read, enter "starting padding" state
382                    // If a full buffer was read, enter "starting compression" state
383                    if (previousState.DataLength < DATA_BLOCK_SIZE)
384                        newState.State = MD5StateDescription.STARTING_PADDING;
385                    else
386                        newState.State = MD5StateDescription.STARTING_COMPRESSION;
387                    break;
388
389                case MD5StateDescription.STARTING_PADDING:
390                    // First step of padding is adding the padding bytes
391                    newState.State = MD5StateDescription.ADDING_PADDING_BYTES;
392                    break;
393
394                case MD5StateDescription.ADDING_PADDING_BYTES:
395                    // Add necessary number of bytes and enter "added padding bytes" state
396                    AddPaddingBytes(newState);
397                    newState.State = MD5StateDescription.ADDED_PADDING_BYTES;
398                    break;
399
400                case MD5StateDescription.ADDED_PADDING_BYTES:
401                    // Next step for padding is adding the data length
402                    newState.State = MD5StateDescription.ADDING_LENGTH;
403                    break;
404
405                case MD5StateDescription.ADDING_LENGTH:
406                    // Add the length of the data and enter "added length" state
407                    AddLength(newState);
408                    newState.State = MD5StateDescription.ADDED_LENGTH;
409                    break;
410
411                case MD5StateDescription.ADDED_LENGTH:
412                    // Padding is done after adding data length, so enter "finished padding" state
413                    newState.State = MD5StateDescription.FINISHED_PADDING;
414                    break;
415
416                case MD5StateDescription.FINISHED_PADDING:
417                    // If padding is finished, call compression function for the last (two) time(s)
418                    newState.State = MD5StateDescription.STARTING_COMPRESSION;
419                    break;
420
421                case MD5StateDescription.STARTING_COMPRESSION:
422                    // Perform pre-compression initialization and continue by starting the first round
423                    StartCompression(newState);
424                    newState.State = MD5StateDescription.STARTING_ROUND;
425                    break;
426
427                case MD5StateDescription.STARTING_ROUND:
428                    // Start the round and continue with the first round step
429                    StartRound(newState);
430                    newState.State = MD5StateDescription.STARTING_ROUND_STEP;
431                    break;
432
433                case MD5StateDescription.STARTING_ROUND_STEP:
434                    // Perform the step and go into finished state
435                    PerformRoundStep(newState);
436                    newState.State = MD5StateDescription.FINISHED_ROUND_STEP;
437                    break;
438
439                case MD5StateDescription.FINISHED_ROUND_STEP:
440                    // If last step, go into 'finished round' state, else continue with next step
441                    if (previousState.IsLastStepInRound)
442                        newState.State = MD5StateDescription.FINISHED_ROUND;
443                    else
444                    {
445                        newState.RoundStepIndex++;
446                        newState.State = MD5StateDescription.STARTING_ROUND_STEP;
447                    }
448                    break;
449
450                case MD5StateDescription.FINISHED_ROUND:
451                    // If last step, go into "finishing compression" state, else continue with next round
452                    if (previousState.IsLastRound)
453                        newState.State = MD5StateDescription.FINISHING_COMPRESSION;
454                    else
455                    {
456                        newState.RoundIndex++;
457                        newState.State = MD5StateDescription.STARTING_ROUND;
458                    }
459                    break;
460
461                case MD5StateDescription.FINISHING_COMPRESSION:
462                    // Perform finishing actions and go into "finished compression" state
463                    FinishCompression(newState);
464                    newState.State = MD5StateDescription.FINISHED_COMPRESSION;
465                    break;
466
467                case MD5StateDescription.FINISHED_COMPRESSION:
468                    // If there's more data left in buffer, reenter compression function with offset
469                    if (previousState.DataLength - previousState.DataOffset > DATA_BLOCK_SIZE)
470                    {
471                        // Still some data left in buffer, rerun compression with offset
472                        newState.DataOffset += DATA_BLOCK_SIZE;
473                        newState.State = MD5StateDescription.STARTING_COMPRESSION;
474                    }
475                    else
476                    {
477                        // No data left in buffer
478
479                        if (previousState.IsPaddingDone)
480                        {
481                            // If padding was already added, we're done
482                            newState.State = MD5StateDescription.FINISHED;
483                        }
484                        else
485                        {
486                            // Read more data
487                            newState.State = MD5StateDescription.READING_DATA;
488                        }
489                    }
490                    break;
491            }
492        }
493
494        /// <summary>
495        /// Performs the steps necessary after the individual compression function steps have run
496        /// </summary>
497        /// <param name="newState">Algorithm state to modify</param>
498        private void FinishCompression(PresentableMD5State newState)
499        {
500            // Add compression function results to accumulators
501            newState.H1 += newState.A;
502            newState.H2 += newState.B;
503            newState.H3 += newState.C;
504            newState.H4 += newState.D;
505
506            // Increment the number of bytes hashed so far
507            newState.BytesHashed += DATA_BLOCK_SIZE;
508        }
509
510        /// <summary>
511        /// Reads from the data source
512        /// </summary>
513        /// <param name="newState">Algorithm state to modify</param>
514        private void ReadData(PresentableMD5State newState)
515        {
516            // Fetch up to 64 bytes of data
517            newState.Data = new byte[128];
518            newState.DataLength = (uint)DataStream.Read(newState.Data, 0, 64);
519            newState.DataOffset = 0;
520        }
521
522        /// <summary>
523        /// Performs initialization before a round
524        /// </summary>
525        /// <param name="newState">Algorithm state to modify</param>
526        private void StartRound(PresentableMD5State newState)
527        {
528            // Reset round step counter
529            newState.RoundStepIndex = 0;
530        }
531
532        /// <summary>
533        /// Performs initialization required before running compression function steps
534        /// </summary>
535        /// <param name="newState">Algorithm state to modify</param>
536        private void StartCompression(PresentableMD5State newState)
537        {
538            // Read data into unsigned 32 bit integers
539            newState.DataAsIntegers = new uint[16];
540            for (uint j = 0; j < 64; j += 4)
541            {
542                newState.DataAsIntegers[j / 4] = (((uint)newState.Data[newState.DataOffset + (j + 3)]) << 24) |
543                        (((uint)newState.Data[newState.DataOffset + (j + 2)]) << 16) |
544                        (((uint)newState.Data[newState.DataOffset + (j + 1)]) << 8) |
545                        (((uint)newState.Data[newState.DataOffset + (j)]));
546            }
547
548            // Reset round counter
549            newState.RoundIndex = 0;
550
551            // Initialize A, B, C, D with accumulated values
552            newState.A = newState.H1;
553            newState.B = newState.H2;
554            newState.C = newState.H3;
555            newState.D = newState.H4;
556        }
557
558        /// <summary>
559        /// Adds the data length part of the padding
560        /// </summary>
561        /// <param name="newState">Algorithm state to modify</param>
562        private void AddLength(PresentableMD5State newState)
563        {
564            // Determine offset behind last written byte
565            uint lengthOffset = newState.DataLength + 8;
566
567            // Write the length in bit as 8 byte little-endian integer
568            for (int i = 8; i > 0; i--)
569                newState.Data[lengthOffset - i] = (byte)(newState.LengthInBit >> ((8 - i) * 8) & 0xff);
570
571            // Remember that padding is done now
572            newState.IsPaddingDone = true;
573
574            // Update data length
575            newState.DataLength += 8;
576        }
577
578        /// <summary>
579        /// Adds padding bytes to the data
580        /// </summary>
581        /// <param name="newState">Algorithm state to modify</param>
582        private void AddPaddingBytes(PresentableMD5State newState)
583        {
584            // Save length of data in bit
585            newState.LengthInBit = (newState.BytesHashed + newState.DataLength) * 8;
586
587            // Add '1' bit to end of data
588            newState.Data[newState.DataLength] = 0x80;
589            newState.DataLength++;
590
591            // Add zero bytes until 8 bytes short of next 64-byte block
592            while (newState.DataLength % 64 != 56)
593            {
594                newState.Data[newState.DataLength++] = 0;
595            }
596        }
597
598        /// <summary>
599        /// Sets up and adds the "initialized" state
600        /// </summary>
601        public void PerformInitializationStep()
602        {
603            // Add a state
604            AddNewState();
605
606            // Initialize new state
607            CurrentState.BytesHashed = 0;
608            CurrentState.State = MD5StateDescription.INITIALIZED;
609
610            // Set initial accumulator values
611            CurrentState.H1 = 0x67452301;
612            CurrentState.H2 = 0xEFCDAB89;
613            CurrentState.H3 = 0x98BADCFE;
614            CurrentState.H4 = 0X10325476;
615
616        }
617
618        /// <summary>
619        /// Shift-Rotates an unsigned integer to the left
620        /// </summary>
621        /// <param name="uiNumber">Integer to rotate</param>
622        /// <param name="shift">Number of bits to shift</param>
623        /// <returns>Result of the shift</returns>
624        public static uint RotateLeft(uint uiNumber, ushort shift)
625        {
626            return (uiNumber << shift) | (uiNumber >> (32 - shift));
627        }
628
629        /// <summary>
630        /// Delegate for the inner round function applied in each step of the compression function
631        /// </summary>
632        /// <param name="a"></param>
633        /// <param name="b"></param>
634        /// <param name="c"></param>
635        /// <param name="d"></param>
636        /// <returns></returns>
637        protected delegate uint RoundFunction(uint a, uint b, uint c, uint d);
638
639        /// <summary>
640        /// Constant array of the four inner round functions
641        /// </summary>
642        protected readonly RoundFunction[] ROUND_FUNCTION = { FuncF, FuncG, FuncH, FuncI };
643
644        /// <summary>
645        /// Performs one step of the compression function
646        /// </summary>
647        /// <param name="newState">Algorithm state to modify</param>
648        private void PerformRoundStep(PresentableMD5State newState)
649        {
650            // Determine which round function to use
651            RoundFunction roundFunction = ROUND_FUNCTION[newState.RoundIndex];
652
653            // Determine which step in the compression function this is
654            uint stepIndex = newState.RoundIndex * 16 + newState.RoundStepIndex;
655
656            // Determine which part of the data to use in this step
657            uint wordIndex;
658            switch (newState.RoundIndex)
659            {
660                default:
661                case 0:
662                    wordIndex = stepIndex;
663                    break;
664                case 1:
665                    wordIndex = 5 * stepIndex + 1;
666                    break;
667                case 2:
668                    wordIndex = 3 * stepIndex + 5;
669                    break;
670                case 3:
671                    wordIndex = 7 * stepIndex;
672                    break;
673            }
674            wordIndex %= 16;
675
676            // Execute the chosen round function
677            ExecRoundFunction(newState, roundFunction, newState.DataAsIntegers[wordIndex], stepIndex);
678        }
679
680        /// <summary>
681        /// Executes the round function and modifies algorithm state to reflect results
682        /// </summary>
683        /// <param name="state">Algorithm state to modify</param>
684        /// <param name="function">The inner round function to execute</param>
685        /// <param name="W">The part of the data to use in the round function</param>
686        /// <param name="i">Index of this step (range 0 - 63)</param>
687        protected static void ExecRoundFunction(PresentableMD5State state, RoundFunction function, uint W, uint i)
688        {
689            // Apply central compression function
690            state.A = state.B + RotateLeft((state.A + function(state.A, state.B, state.C, state.D) + W + AdditionConstantTable[i]), ShiftConstantTable[i]);
691
692            // Right-rotate the 4 compression result accumulators
693            uint oldD = state.D;
694            state.D = state.C;
695            state.C = state.B;
696            state.B = state.A;
697            state.A = oldD;
698        }
699
700        /// <summary>
701        /// Inner round function F, applied in step 1-16 of the compression function
702        /// </summary>
703        /// <param name="a">Temporary step variable A</param>
704        /// <param name="b">Temporary step variable B</param>
705        /// <param name="c">Temporary step variable C</param>
706        /// <param name="d">Temporary step variable D</param>
707        /// <returns>Result of inner round function F</returns>
708        protected static uint FuncF(uint a, uint b, uint c, uint d)
709        {
710            return d ^ (b & (c ^ d));
711        }
712
713        /// <summary>
714        /// Inner round function G, applied in step 17-32 of the compression function
715        /// </summary>
716        /// <param name="a">Temporary step variable A</param>
717        /// <param name="b">Temporary step variable B</param>
718        /// <param name="c">Temporary step variable C</param>
719        /// <param name="d">Temporary step variable D</param>
720        /// <returns>Result of inner round function G</returns>
721        protected static uint FuncG(uint a, uint b, uint c, uint d)
722        {
723            return c ^ (d & (b ^ c));
724        }
725
726        /// <summary>
727        /// Inner round function H, applied in step 33-48 of the compression function
728        /// </summary>
729        /// <param name="a">Temporary step variable A</param>
730        /// <param name="b">Temporary step variable B</param>
731        /// <param name="c">Temporary step variable C</param>
732        /// <param name="d">Temporary step variable D</param>
733        /// <returns>Result of inner round function H</returns>
734        protected static uint FuncH(uint a, uint b, uint c, uint d)
735        {
736            return b ^ c ^ d;
737        }
738
739        /// <summary>
740        /// Inner round function I, applied in step 49-64 of the compression function
741        /// </summary>
742        /// <param name="a">Temporary step variable A</param>
743        /// <param name="b">Temporary step variable B</param>
744        /// <param name="c">Temporary step variable C</param>
745        /// <param name="d">Temporary step variable D</param>
746        /// <returns>Result of inner round function I</returns>
747        protected static uint FuncI(uint a, uint b, uint c, uint d)
748        {
749            return c ^ (b | ~d);
750        }
751
752        /// <summary>
753        /// The current state's accumulator variables as byte array
754        /// </summary>
755        /// <remarks>
756        /// When the algorithm is finished, this is the computed MD5 digest value.
757        /// </remarks>
758        public byte[] HashValueBytes
759        {
760            get
761            {
762                byte[] result = new byte[16];
763                writeUintToArray(result, 0, CurrentState.H1);
764                writeUintToArray(result, 4, CurrentState.H2);
765                writeUintToArray(result, 8, CurrentState.H3);
766                writeUintToArray(result, 12, CurrentState.H4);
767                return result;
768            }
769        }
770
771        /// <summary>
772        /// Writes an integer into an array in little-endian representation
773        /// </summary>
774        /// <param name="array">Array which should be written to</param>
775        /// <param name="offset">Offset in the array</param>
776        /// <param name="value">Integer to write to array</param>
777        protected void writeUintToArray(byte[] array, int offset, uint value)
778        {
779            byte[] byteValue = BitConverter.GetBytes(value);
780
781            if (!BitConverter.IsLittleEndian)
782                Array.Reverse(byteValue);
783
784            Array.Copy(byteValue, 0, array, offset, 4);
785        }
786    }
787}
Note: See TracBrowser for help on using the repository browser.