source: trunk/CrypPlugins/AES/AES.cs @ 1174

Last change on this file since 1174 was 1174, checked in by Sven Rech, 12 years ago

better AES implementation for bruteforcing

File size: 23.8 KB
Line 
1/*                              Apache License
2                           Version 2.0, January 2004
3                        http://www.apache.org/licenses/
4
5   Copyright [2008] [Dr. Arno Wacker, University of Duisburg-Essen]
6
7   Licensed under the Apache License, Version 2.0 (the "License");
8   you may not use this file except in compliance with the License.
9   You may obtain a copy of the License at
10
11       http://www.apache.org/licenses/LICENSE-2.0
12
13   Unless required by applicable law or agreed to in writing, software
14   distributed under the License is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18*/
19
20using System;
21using System.Collections.Generic;
22using System.Linq;
23using System.Text;
24using System.IO;
25using System.Security.Cryptography;
26using Cryptool.PluginBase;
27using System.ComponentModel;
28using Cryptool.PluginBase.Cryptography;
29using Cryptool.PluginBase.IO;
30using System.Windows.Controls;
31using System.Runtime.CompilerServices;
32using Cryptool.PluginBase.Miscellaneous;
33using System.Runtime.Remoting.Contexts;
34using Cryptool.PluginBase.Control;
35using System.Reflection;
36using NativeCryptography;
37
38namespace Cryptool.Plugins.Cryptography.Encryption
39{
40    [Author("Dr. Arno Wacker", "arno.wacker@cryptool.org", "Uni Duisburg", "http://www.uni-duisburg-essen.de")]
41    [PluginInfo(false, "AES", "Advanced Encryption Standard (Rijndael)", "AES/DetailedDescription/Description.xaml", "AES/Images/AES.png", "AES/Images/encrypt.png", "AES/Images/decrypt.png", "AES/Images/Rijndael.png")]
42    [EncryptionType(EncryptionType.SymmetricBlock)]
43    public class AES : ContextBoundObject, IEncryption
44    {
45        #region Private variables
46        private AESSettings settings;
47        private CryptoolStream inputStream;
48        private CryptoolStream outputStream;
49        private byte[] inputKey;
50        private byte[] inputIV;
51        private CryptoStream p_crypto_stream;
52        private bool stop = false;
53        private List<CryptoolStream> listCryptoolStreamsOut = new List<CryptoolStream>();
54        #endregion
55
56        public AES()
57        {
58            this.settings = new AESSettings();
59            this.settings.OnPluginStatusChanged += settings_OnPluginStatusChanged;
60        }
61
62        void settings_OnPluginStatusChanged(IPlugin sender, StatusEventArgs args)
63        {
64          if (OnPluginStatusChanged != null) OnPluginStatusChanged(this, args);
65        }
66
67        public ISettings Settings
68        {
69            get { return this.settings; }
70            set { this.settings = (AESSettings)value; }
71        }
72
73        [PropertyInfo(Direction.InputData, "Input", "Data to be encrypted or decrypted", "", true, false, DisplayLevel.Beginner, QuickWatchFormat.Hex, null)]
74        public CryptoolStream InputStream
75        {
76            get 
77            {
78              if (inputStream != null)
79              {
80                CryptoolStream cs = new CryptoolStream();
81                cs.OpenRead(inputStream.FileName);
82                listCryptoolStreamsOut.Add(cs);
83                return cs;
84              }
85              else return null;
86            }
87            set 
88            { 
89              this.inputStream = value;
90              if (value != null) listCryptoolStreamsOut.Add(value);
91              OnPropertyChanged("InputStream");
92            }
93        }
94
95        [PropertyInfo(Direction.InputData, "Key", "The provided key should be 16, 24 or 32 bytes, dependig on the settings. Too short/long keys will be extended/truncated!", "", true, false, DisplayLevel.Beginner, QuickWatchFormat.Hex, null)]
96        public byte[] InputKey
97        {
98            get { return this.inputKey; }
99            set 
100            { 
101              this.inputKey = value;
102              OnPropertyChanged("InputKey");
103            }
104        }
105
106        [PropertyInfo(Direction.InputData, "IV", "The initialisation vector (IV) which is used in chaining modes. It always must be the same as the blocksize.", "", false, false, DisplayLevel.Professional, QuickWatchFormat.Hex, null)]
107        public byte[] InputIV
108        {
109            get { return this.inputIV; }
110            set 
111            { 
112              this.inputIV = value;
113              OnPropertyChanged("InputIV");
114            }
115        }
116
117        [PropertyInfo(Direction.OutputData, "Output stream", "Encrypted or decrypted output data", "", true, false, DisplayLevel.Beginner, QuickWatchFormat.Hex, null)]
118        public CryptoolStream OutputStream
119        {
120            get 
121            {
122              if (this.outputStream != null)
123              {
124                CryptoolStream cs = new CryptoolStream();
125                listCryptoolStreamsOut.Add(cs);
126                cs.OpenRead(this.outputStream.FileName);
127                return cs;
128              }
129              return null;
130            }
131            set 
132            {
133              outputStream = value;
134              if (value != null) listCryptoolStreamsOut.Add(value);
135              OnPropertyChanged("OutputStream");
136            }
137        }
138
139        private void ConfigureAlg(SymmetricAlgorithm alg)
140        {
141            // first set all paramter, then assign input values!
142            switch (settings.Mode)
143            { //0="ECB"=default, 1="CBC", 2="CFB", 3="OFB"
144                case 1: alg.Mode = CipherMode.CBC; break;
145                case 2: alg.Mode = CipherMode.CFB; break;
146                // case 3: alg.Mode = CipherMode.OFB; break;
147                default: alg.Mode = CipherMode.ECB; break;
148            }
149
150            switch (settings.Padding)
151            { //0="Zeros"=default, 1="None", 2="PKCS7" , 3="ANSIX923", 4="ISO10126"
152                case 1: alg.Padding = PaddingMode.None; break;
153                case 2: alg.Padding = PaddingMode.PKCS7; break;
154                case 3: alg.Padding = PaddingMode.ANSIX923; break;
155                case 4: alg.Padding = PaddingMode.ISO10126; break;
156                default: alg.Padding = PaddingMode.Zeros; break;
157            }
158            if (settings.CryptoAlgorithm == 1)
159            {
160                switch (settings.Blocksize)
161                {
162                    case 1: alg.BlockSize = 192; break;
163                    case 2: alg.BlockSize = 256; break;
164                    default:
165                        alg.BlockSize = 128;
166                        break;
167                }
168            }
169
170            //check for a valid key
171            if (this.inputKey == null)
172            {               
173                //create a trivial key
174                inputKey = new byte[16];
175                // write a warning to the ouside word
176                GuiLogMessage("ERROR: No key provided. Using 0x000..00!", NotificationLevel.Error);
177            }
178
179            int keySizeInBytes = (16 + settings.Keysize * 8);
180
181            //prepare a long enough key
182            byte[] key = new byte[keySizeInBytes];
183
184            // copy the input key into the temporary key array
185            Array.Copy(inputKey, key, Math.Min(inputKey.Length, key.Length));
186
187            // Note: the SymmetricAlgorithm.Key setter clones the passed byte[] array and keeps his own copy
188            alg.Key = key;
189
190            if (inputKey.Length > keySizeInBytes)
191            {
192                GuiLogMessage("Overlength (" + inputKey.Length * 8 + " Bits) key provided. Removing trailing bytes to fit the desired key length of " + (keySizeInBytes * 8) + " Bits: " + bytesToHexString(key), NotificationLevel.Warning);
193            }
194
195            if (inputKey.Length < keySizeInBytes)
196            {
197                GuiLogMessage("Short (" + inputKey.Length * 8 + " Bits) key provided. Adding zero bytes to fill up to the desired key length of " + (keySizeInBytes * 8) + " Bits: " + bytesToHexString(key), NotificationLevel.Warning);
198            }
199
200            //check for a valid IV
201            if (this.inputIV == null)
202            {
203                //create a trivial key
204                inputIV = new byte[alg.BlockSize / 8];
205                GuiLogMessage("NOTE: No IV provided. Using 0x000..00!", NotificationLevel.Info);
206            }
207            alg.IV = this.inputIV;
208        }
209
210        private string bytesToHexString(byte[] array)
211        {
212            StringBuilder sb = new StringBuilder();
213            foreach (byte b in array)
214            {
215                sb.Append(b.ToString("X2"));
216            }
217            return sb.ToString();
218        }
219
220        private void checkForInputStream()
221        {
222            if (settings.Action == 0 && (inputStream == null || (inputStream != null && inputStream.Length == 0)))
223            {
224                //create some input
225                String dummystring = "Dummy string - no input provided - \"Hello AES World\" - dummy string - no input provided!";
226                this.inputStream = new CryptoolStream();
227                this.inputStream.OpenRead(Encoding.Default.GetBytes(dummystring.ToCharArray()));
228                // write a warning to the ouside word
229                GuiLogMessage("WARNING: No input provided. Using dummy data. (" + dummystring + ")", NotificationLevel.Warning);
230            }
231        }
232
233        public void Execute()
234        {
235            process(settings.Action);
236        }
237
238        public bool isStopped()
239        {
240
241            return this.stop;
242        }
243
244        private void process(int action)
245        {
246            //Encrypt/Decrypt Stream
247          try
248          {
249            checkForInputStream();
250            if (inputStream == null || inputStream.Length == 0)
251            {
252              GuiLogMessage("No input given. Not using dummy data in decrypt mode. Aborting now.", NotificationLevel.Error);
253              return;
254            }
255
256            if (this.inputStream.CanSeek) this.inputStream.Position = 0;
257            SymmetricAlgorithm p_alg = null;
258            if (settings.CryptoAlgorithm == 1)
259            { p_alg = new RijndaelManaged(); }
260            else
261            { p_alg = new AesCryptoServiceProvider(); }
262
263            ConfigureAlg(p_alg);
264
265            ICryptoTransform p_encryptor = null;
266            switch (action)
267            {
268              case 0:
269                p_encryptor = p_alg.CreateEncryptor();
270                break;
271              case 1:
272                p_encryptor = p_alg.CreateDecryptor();
273                break;
274            }
275
276            outputStream = new CryptoolStream();
277            listCryptoolStreamsOut.Add(outputStream);
278            outputStream.OpenWrite();
279            p_crypto_stream = new CryptoStream((Stream)inputStream, p_encryptor, CryptoStreamMode.Read);
280            byte[] buffer = new byte[p_alg.BlockSize / 8];
281            int bytesRead;
282            int position = 0;
283            string mode = action == 0 ? "encryption" : "decryption";
284            GuiLogMessage("Starting " + mode + " [Keysize=" + p_alg.KeySize.ToString() + " Bits, Blocksize=" + p_alg.BlockSize.ToString() + " Bits]", NotificationLevel.Info);
285            DateTime startTime = DateTime.Now;
286            while ((bytesRead = p_crypto_stream.Read(buffer, 0, buffer.Length)) > 0 && !stop)
287            {
288              outputStream.Write(buffer, 0, bytesRead);
289
290              if ((int)(inputStream.Position * 100 / inputStream.Length) > position)
291              {
292                position = (int)(inputStream.Position * 100 / inputStream.Length);
293                ProgressChanged(inputStream.Position, inputStream.Length);
294              }
295            }
296
297
298            long outbytes = outputStream.Length;
299            p_crypto_stream.Flush();           
300            // p_crypto_stream.Close();
301            DateTime stopTime = DateTime.Now;
302            TimeSpan duration = stopTime - startTime;
303            // (outputStream as CryptoolStream).FinishWrite();
304
305            if (!stop)
306            {
307                mode = action == 0 ? "Encryption" : "Decryption";
308                GuiLogMessage(mode + " complete! (in: " + inputStream.Length.ToString() + " bytes, out: " + outbytes.ToString() + " bytes)", NotificationLevel.Info);
309                GuiLogMessage("Wrote data to file: " + outputStream.FileName, NotificationLevel.Info);
310                GuiLogMessage("Time used: " + duration.ToString(), NotificationLevel.Debug);
311                outputStream.Close();
312                OnPropertyChanged("OutputStream");
313            }
314            CryptoolStream test = outputStream;
315            if (stop)
316            {
317                outputStream.Close();
318                GuiLogMessage("Aborted!", NotificationLevel.Info);
319            }
320          }
321          catch (CryptographicException cryptographicException)
322          {
323            // TODO: For an unknown reason p_crypto_stream can not be closed after exception.
324            // Trying so makes p_crypto_stream throw the same exception again. So in Dispose
325            // the error messages will be doubled.
326            // As a workaround we set p_crypto_stream to null here.
327            p_crypto_stream = null;
328            GuiLogMessage(cryptographicException.Message, NotificationLevel.Error);
329          }
330          catch (Exception exception)
331          {
332            GuiLogMessage(exception.Message, NotificationLevel.Error);
333          }
334          finally
335          {
336              ProgressChanged(1, 1);
337          }
338        }
339
340        public void Encrypt()
341        {
342            //Encrypt Stream
343            process(0);
344        }
345       
346        public void Decrypt()
347        {
348            //Decrypt Stream
349            process(1);
350        }
351
352        #region IPlugin Member
353
354
355        public System.Windows.Controls.UserControl Presentation
356        {
357          get { return null; }
358        }
359
360        public UserControl QuickWatchPresentation
361        {
362          get { return null; }
363        }
364
365        public void Initialize()
366        {           
367        }
368
369        public void Dispose()
370        {
371          try
372          {
373            stop = false;
374            inputKey = null;
375            inputIV = null;
376            outputStream = null;
377            inputStream = null;
378
379            //if (inputStream != null)
380            //{
381            //  inputStream.Flush();
382            //  inputStream.Close();
383            //  inputStream = null;
384            //}
385            //if (outputStream != null)
386            //{
387            //  outputStream.Flush();
388            //  outputStream.Close();
389            //  outputStream = null;
390            //}
391            foreach (CryptoolStream stream in listCryptoolStreamsOut)
392            {
393              stream.Close();
394            }
395            listCryptoolStreamsOut.Clear();
396
397            if (p_crypto_stream != null)
398            {
399              p_crypto_stream.Flush();
400              p_crypto_stream.Close();
401              p_crypto_stream = null;
402            }
403          }
404          catch (Exception ex)
405          {         
406             GuiLogMessage(ex.Message, NotificationLevel.Error);
407          }
408          this.stop = false;
409        }
410
411        public void Stop()
412        {
413          this.stop = true;
414        }
415
416        public void PostExecution()
417        {
418          Dispose();
419        }
420
421        public void PreExecution()
422        {
423          Dispose();
424        }
425
426        #endregion
427
428        #region INotifyPropertyChanged Members
429
430        public event PropertyChangedEventHandler PropertyChanged;
431
432        public void OnPropertyChanged(string name)
433        {
434          EventsHelper.PropertyChanged(PropertyChanged, this, new PropertyChangedEventArgs(name));
435          //if (PropertyChanged != null)
436          //{
437          //  PropertyChanged(this, new PropertyChangedEventArgs(name));
438          //}
439        }
440
441        #endregion
442
443        #region IPlugin Members
444
445        public event StatusChangedEventHandler OnPluginStatusChanged;       
446
447        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
448        private void GuiLogMessage(string message, NotificationLevel logLevel)
449        {
450          EventsHelper.GuiLogMessage(OnGuiLogNotificationOccured, this, new GuiLogEventArgs(message, this, logLevel));
451        }
452
453        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
454        private void ProgressChanged(double value, double max)
455        {
456          EventsHelper.ProgressChanged(OnPluginProgressChanged, this, new PluginProgressEventArgs(value, max));
457        }
458       
459        public void Pause()
460        {
461         
462        }
463
464        #endregion
465
466        private IControlEncryption controlSlave;
467        [PropertyInfo(Direction.ControlSlave, "AES Slave", "AES Slave", "", DisplayLevel.Experienced)]
468        public IControlEncryption ControlSlave
469        {
470          get 
471          {
472              if (controlSlave == null)
473                  controlSlave = new AESControl(this);
474              return controlSlave; 
475          }
476        }     
477    }
478
479    public class AESControl : IControlEncryption
480    {
481        public event KeyPatternChanged keyPatternChanged;
482        public event IControlStatusChangedEventHandler OnStatusChanged;
483
484        private AES plugin;
485     
486        public AESControl(AES Plugin)
487        {
488            this.plugin = Plugin;
489        }
490
491        #region IControlEncryption Members
492
493        public byte[] Encrypt(byte[] key, int blocksize)
494        {
495            /// not implemented, currently not needed
496            return null;
497        }
498
499        public byte[] Decrypt(byte[] ciphertext, byte[] key)
500        {
501            return Decrypt(ciphertext, key, ciphertext.Length);
502        }
503
504        public byte[] Decrypt(byte[] ciphertext, byte[] key, int bytesToUse)
505        {
506            CryptoStream crypto_stream = null;
507            int size = bytesToUse > ciphertext.Length ? ciphertext.Length : bytesToUse;
508
509            int bits = -1;
510            switch (((AESSettings)plugin.Settings).Keysize)
511            {
512                case 0:
513                    bits = 16*8;
514                    break;
515                case 1:
516                    bits = 24*8;
517                    break;
518                case 2:
519                    bits = 32*8;
520                    break;
521            }
522
523            if (bits == -1)
524                return null;
525
526            return NativeCryptography.Crypto.decryptAES(ciphertext, key, bits, size);
527        }
528
529     
530
531        private void ConfigureAlg(SymmetricAlgorithm alg, byte[] key)
532        {
533            try
534            {
535                alg.Key = key;
536            }
537            catch
538            {
539                //dirty hack to allow weak keys:
540                FieldInfo field = alg.GetType().GetField("KeyValue", BindingFlags.NonPublic | BindingFlags.Instance);
541                field.SetValue(alg, key);
542            }
543
544            //try
545            //{
546            //    alg.IV = this.plugin.InputIV;
547            //}
548            //catch
549            //{
550            //    //dirty hack to allow weak keys:
551            //    FieldInfo field = aes_algorithm.GetType().GetField("IVValue", BindingFlags.NonPublic | BindingFlags.Instance);
552            //    field.SetValue(alg, this.plugin.InputIV);
553            //}
554            alg.IV = new byte[alg.BlockSize / 8];
555
556
557            switch (((AESSettings)plugin.Settings).Mode)
558            { //0="ECB"=default, 1="CBC", 2="CFB", 3="OFB"
559                case 1: alg.Mode = CipherMode.CBC; break;
560                case 2: alg.Mode = CipherMode.CFB; break;
561                case 3: alg.Mode = CipherMode.OFB; break;
562                default: alg.Mode = CipherMode.ECB; break;
563            }
564
565            alg.Padding = PaddingMode.Zeros;
566
567            //switch (((AESSettings)plugin.Settings).Padding)
568            //{ //0="Zeros"=default, 1="None", 2="PKCS7"
569            //    case 1: alg.Padding = PaddingMode.None; break;
570            //    case 2: alg.Padding = PaddingMode.PKCS7; break;
571            //    case 3: alg.Padding = PaddingMode.ANSIX923; break;
572            //    case 4: alg.Padding = PaddingMode.ISO10126; break;
573            //    default: alg.Padding = PaddingMode.Zeros; break;
574            //}
575           
576        }
577       
578        public string getKeyPattern()
579        {
580            int bytes = 0;
581            switch (((AESSettings)plugin.Settings).Keysize)
582            {
583                case 0:
584                    bytes = 16;
585                    break;
586                case 1:
587                    bytes = 24;
588                    break;
589                case 2:
590                    bytes = 32;
591                    break;
592            }
593            string pattern = "";
594            for (int i = 1; i < bytes; i++)
595                pattern += "[0-9A-F][0-9A-F]-";
596            pattern += "[0-9A-F][0-9A-F]";
597            return pattern;
598        }
599
600        public byte[] getKeyFromString(string key, ref int[] arrayPointers, ref int[] arraySuccessors, ref int[] arrayUppers)
601        {
602            int bytes = 0;
603            switch (((AESSettings)plugin.Settings).Keysize)
604            {
605                case 0:
606                    bytes = 16;
607                    break;
608                case 1:
609                    bytes = 24;
610                    break;
611                case 2:
612                    bytes = 32;
613                    break;
614            }
615            byte[] bkey = new byte[bytes];
616            int counter = 0;
617            bool allocated = false;
618            for (int i = 0; i < bytes; i++)
619            {
620                try
621                {
622                    string substr = key.Substring(i * 3, 2);
623                    if (!allocated && (substr[0] == '*' || substr[1] == '*'))
624                    {
625                        arrayPointers = new int[bytes];
626                        for (int j = 0; j < bytes; j++)
627                            arrayPointers[j] = -1;
628                        arraySuccessors = new int[bytes];
629                        arrayUppers = new int[bytes];
630                        allocated = true;
631                    }
632                    if (substr[0] != '*' && substr[1] != '*')
633                        bkey[i] = Convert.ToByte(substr, 16);
634                    else if (substr[0] == '*' && substr[1] == '*')
635                    {
636                        bkey[i] = 0;
637                        arrayPointers[counter] = i;
638                        arraySuccessors[counter] = 1;
639                        arrayUppers[counter] = 255;
640                        counter++;
641                    }
642                    else if (substr[0] != '*' && substr[1] == '*')
643                    {
644                        bkey[i] = Convert.ToByte(substr[0] + "0", 16);
645                        arrayPointers[counter] = i;
646                        arraySuccessors[counter] = 1;
647                        arrayUppers[counter] = Convert.ToByte(substr[0] + "F", 16);
648                        counter++;
649                    }
650                    else if (substr[0] == '*' && substr[1] != '*')
651                    {
652                        bkey[i] = Convert.ToByte("0" + substr[1], 16);
653                        arrayPointers[counter] = i;
654                        arraySuccessors[counter] = 16;
655                        arrayUppers[counter] = Convert.ToByte("F" + substr[1], 16);
656                        counter++;
657                    }
658                }
659                catch (Exception ex)
660                {
661                    return null;
662                }
663            }
664            return bkey;
665        }
666
667        public IControlEncryption clone()
668        {
669            AESControl aes = new AESControl(plugin);
670            return aes;
671        }
672
673        public void Dispose()
674        {
675        }
676
677        #endregion
678
679        #region IControlEncryption Member
680
681
682        public void changeSettings(string setting, object value)
683        {
684           
685        }
686
687        #endregion
688    }
689}
Note: See TracBrowser for help on using the repository browser.