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

Last change on this file since 1892 was 1706, checked in by Paul Lelgemann, 12 years ago

o AES: FileNotFound exception handled

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