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

Last change on this file since 2220 was 2061, checked in by Sven Rech, 11 years ago

Added code to generate openCL bruteforce code in KeySearcher.
Not used yet, so don't try it

File size: 22.7 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 IControlEncryption clone()
563        {
564            AESControl aes = new AESControl(plugin);
565            return aes;
566        }
567
568        public void Dispose()
569        {
570        }
571
572        public string GetOpenCLCode(int decryptionLength)
573        {
574            string opencl = Properties.Resources.AESOpenCL;
575
576            int bits = -1;
577            switch (((AESSettings)plugin.Settings).Keysize)
578            {
579                case 0:
580                    bits = 16 * 8;
581                    break;
582                case 1:
583                    bits = 24 * 8;
584                    break;
585                case 2:
586                    bits = 32 * 8;
587                    break;
588            }
589            if (bits == -1)
590                return null;
591
592            opencl = opencl.Replace("$$BITS$$", "" + bits);
593
594            string decryptionCode = "";
595            int blocks = decryptionLength / 16;
596            if (blocks >= 1)
597            {
598                decryptionCode = AddOpenCLBlockDecryption(decryptionCode, 16);
599
600                if (blocks > 1)
601                {
602                    decryptionCode += string.Format("for (int b = 1; b < {0}; b++) \n {{ \n ", blocks);
603                    decryptionCode = AddOpenCLBlockDecryptionWithMode(decryptionCode, 16, "b");
604                    decryptionCode += "}\n";
605                }
606            }
607
608            if (decryptionLength % 16 != 0)
609            {
610                if (blocks == 0)
611                    decryptionCode = AddOpenCLBlockDecryption(decryptionCode, decryptionLength % 16);
612                else
613                    decryptionCode = AddOpenCLBlockDecryptionWithMode(decryptionCode, decryptionLength % 16, ""+blocks);
614            }
615
616            opencl = opencl.Replace("$$AESDECRYPT$$", decryptionCode);
617
618            return opencl;
619        }
620
621        private string AddOpenCLBlockDecryption(string decryptionCode, int size)
622        {
623            decryptionCode += "AES_decrypt(inn, block, &(key)); \n " + string.Format("for (int i = 0; i < {0}; i++) \n ", size)
624                              + "{ \n unsigned char c = block[i]; \n "
625                              + "$$COSTFUNCTIONCALCULATE$$ \n } \n";
626            return decryptionCode;
627        }
628
629        private string AddOpenCLBlockDecryptionWithMode(string decryptionCode, int size, string block)
630        {
631            decryptionCode += string.Format("AES_decrypt((inn+{0}*16), block, &(key)); \n ", block)
632                              + string.Format("for (int i = 0; i < {0}; i++) \n {{ \n ", size);
633            switch (((AESSettings) plugin.Settings).Mode)
634            {
635                case 0: //ECB
636                    decryptionCode += "unsigned char c = block[i]; \n";
637                    break;
638                case 1: //CBC
639                    decryptionCode += string.Format("unsigned char c = block[i] ^ (inn+({0}-1)*16)[i]; \n", block);
640                    break;
641                case 2: //CFB
642                    throw new NotImplementedException("CFB for OpenCL is not implemented!"); //not supported
643            }
644            decryptionCode += "$$COSTFUNCTIONCALCULATE$$ \n } \n";
645            return decryptionCode;
646        }
647
648        public void changeSettings(string setting, object value)
649        {
650        }
651
652        public IKeyTranslator getKeyTranslator()
653        {
654            return new KeySearcher.KeyTranslators.ByteArrayKeyTranslator();
655        }
656
657        #endregion
658    }
659}
Note: See TracBrowser for help on using the repository browser.