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

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

o Refactoring in CrypP2P
o AES: NullReference in AES-Plugin fixed
o Small fixes in KeySearcher, StorageKeyGenerator uses IV and truncates input data

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