source: trunk/CrypPlugins/RSA/RSA.cs @ 1029

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

fixed RSA progressChange

File size: 17.8 KB
Line 
1/*                             
2   Copyright 2009 Team CrypTool (Sven Rech,Dennis Nolte,Raoul Falk,Nils Kopal), Uni 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 Cryptool.PluginBase.Cryptography;
22using Cryptool.PluginBase;
23using Cryptool.PluginBase.Miscellaneous;
24using System.ComponentModel;
25using System.Security.Cryptography;
26using System.Threading;
27using System.Collections;
28
29namespace Cryptool.Plugins.RSA
30{
31    [Author("Dennis Nolte, Raoul Falk, Sven Rech, Nils Kopal", null, "Uni Duisburg-Essen", "http://www.uni-due.de")]
32    [PluginInfo(false, "RSA", "RSA En/Decryption", "RSA/DetailedDescription/Description.xaml", "RSA/iconrsa.png", "RSA/Images/encrypt.png", "RSA/Images/decrypt.png")]
33    [EncryptionType(EncryptionType.Asymmetric)]
34    /// <summary>
35    /// This plugin does a RSA encryption/decryption on a Message M / Ciphertext C
36    /// It also encrypts/decrypts text with RSA
37    /// </summary>
38    class RSA : IEncryption
39    {
40        #region private members
41
42        private RSASettings settings = new RSASettings();
43        private BigInteger inputN = new BigInteger(1);
44        private BigInteger inputmc = new BigInteger(1);
45        private BigInteger inputed = new BigInteger(1);
46        private BigInteger outputmc = new BigInteger(1);
47        private byte[] inputText = null;
48        private byte[] outputText = null;
49        private int blocks_done = 0;
50        private ArrayList threads;
51        private bool stopped = true;
52
53        #endregion
54
55        #region events
56
57        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
58        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
59        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
60        public event StatusChangedEventHandler OnPluginStatusChanged;
61
62        #endregion
63
64        #region public
65       
66        /// <summary>
67        /// Notify that a property changed
68        /// </summary>
69        /// <param name="name">property name</param>
70        public void OnPropertyChanged(string name)
71        {
72            EventsHelper.PropertyChanged(PropertyChanged, this, new PropertyChangedEventArgs(name));
73        }
74
75        /// <summary>
76        /// Gets/Sets the Settings of this plugin
77        /// </summary>
78        public ISettings Settings
79        {
80            get { return this.settings; }
81            set { this.settings = (RSASettings)value; }
82        }
83
84        /// <summary>
85        /// Get the Presentation of this plugin
86        /// </summary>
87        public System.Windows.Controls.UserControl Presentation
88        {
89            get { return null; }
90        }
91
92        /// <summary>
93        /// Get the QuickWatchRepresentation of this plugin
94        /// </summary>
95        public System.Windows.Controls.UserControl QuickWatchPresentation
96        {
97            get { return null; }
98        }
99
100        /// <summary>
101        /// Called by the environment before execution
102        /// </summary>
103        public void PreExecution()
104        {
105            stopped = false;
106        }
107
108        /// <summary>
109        /// Called by the environment to execute this plugin
110        /// Does RSA on M/C and encrypt/decrypt the input Text
111        /// This method starts threads to speed RSA up if the user switched on more than one
112        /// thread
113        /// </summary>
114        public void Execute()
115        {
116           
117            //calculate the BigIntegers
118            try{
119                if (this.InputN is object && this.InputED is object && this.InputMC is object && !stopped)   
120                    this.OutputMC = InputMC.modPow(this.InputED, this.InputN);
121            }
122            catch (Exception ex)
123            {
124                GuiLogMessage("RSA could not work because of: " + ex.Message, NotificationLevel.Error);             
125            }
126
127            //
128            // RSA on Texts
129            //
130            if (this.InputText is object && this.InputN is object && this.InputED is object && !stopped)
131            {
132                DateTime startTime = DateTime.Now;
133                GuiLogMessage("starting RSA on texts", NotificationLevel.Info);
134
135                threads = ArrayList.Synchronized(new ArrayList());
136                int blocksize_input = 0;
137                int blocksize_output = 0;
138
139                //calculate block sizes from N         
140                //Encryption
141                if (settings.Action == 0)
142                {
143                    blocksize_input = (int)Math.Floor(this.InputN.log(256));
144                    blocksize_output = (int)Math.Ceiling(this.InputN.log(256));
145                }
146                //Decryption
147                else
148                {
149                    blocksize_input = (int)Math.Ceiling(this.InputN.log(256));
150                    blocksize_output = (int)Math.Floor(this.InputN.log(256));
151                }
152
153                GuiLogMessage("Input blocksize = " + blocksize_input, NotificationLevel.Debug);
154                GuiLogMessage("Output blocksize = " + blocksize_output, NotificationLevel.Debug);
155               
156                if (blocksize_input == 0)
157                {
158                    GuiLogMessage("Input blocksize 0 - RSA can not work", NotificationLevel.Error);
159                    return;
160                }
161
162                if (blocksize_output == 0)
163                {
164                    GuiLogMessage("Input blocksize 0 - RSA can not work", NotificationLevel.Error);
165                    return;
166                }
167
168                //calculate amount of blocks and the difference between the input text
169                //and the blocked input text
170                int blockcount = (int)Math.Ceiling((double)this.InputText.Length / blocksize_input);               
171               
172                GuiLogMessage("Blockcount = " + blockcount, NotificationLevel.Debug);
173               
174                //Generate input and output array of correct block size
175                byte[] output = new byte[blocksize_output * blockcount];
176                blocks_done = 0;
177
178                for (int i = 1; i < this.settings.CoresUsed + 1;i++ ) // CoresUsed starts with 0 (so 0 => use 1 Core)
179                {
180                    ParameterizedThreadStart pts = new ParameterizedThreadStart(this.crypt);
181                    Thread thread = new Thread(pts);
182                    thread.Name = "RSA worker thread " + i;
183                    threads.Add(thread);
184                    thread.Start(new Object[6] { output, blockcount, blocksize_input, blocksize_output, i, thread});
185                    GuiLogMessage("started: " + thread.Name, NotificationLevel.Debug);
186
187                    if (stopped)
188                        return;
189
190                }//end for
191
192                //main thread should work also
193                crypt(new Object[6] { output, blockcount, blocksize_input, blocksize_output, 0, null });
194
195                //Wait for all worker threads to stop
196                //Worker threads will be removed by themselves from the list
197                //in finally block
198                while (threads.Count != 0)
199                {
200                    if (stopped)
201                        return;
202
203                    Thread.Sleep(0);
204                }
205               
206                output = removeZeros(output);
207                this.OutputText = output;
208
209                DateTime stopTime = DateTime.Now;
210                TimeSpan duration = stopTime - startTime;
211
212                GuiLogMessage("finished RSA on texts in " + duration, NotificationLevel.Info);
213
214            }//end if           
215            ProgressChanged(1.0, 1.0);
216        }//end Execute
217
218        /// <summary>
219        /// Called by the environment after execution of this plugin
220        /// </summary>
221        public void PostExecution()
222        {
223            this.stopped = true;
224        }
225
226        /// <summary>
227        /// Called by the environment to pause this plugin
228        /// </summary>
229        public void Pause()
230        {
231        }
232
233        /// <summary>
234        /// Called by the environment to stop this plugin
235        /// </summary>
236        public void Stop()
237        {
238            this.stopped = true;
239        }
240
241        /// <summary>
242        /// Called by the environment to initialize this plugin
243        /// </summary>
244        public void Initialize()
245        {
246            this.stopped = true;
247        }
248
249        /// <summary>
250        /// Called by the environment to Dispose this plugin
251        /// </summary>
252        public void Dispose()
253        {
254
255        }
256
257        /// <summary>
258        /// Gets/Sets the one part of the public/private key called N
259        /// </summary>
260        [PropertyInfo(Direction.InputData, "Public key / private Key N input", "Enter your public key / private key N here", "", DisplayLevel.Beginner)]
261        public BigInteger InputN
262        {
263            get
264            {
265                return inputN;
266            }
267            set
268            {
269                this.inputN = value;
270                OnPropertyChanged("InputN");
271            }
272        }
273
274        /// <summary>
275        /// Gets/Sets a input message/ciphertext as BigInteger called M / C
276        /// </summary>
277        [PropertyInfo(Direction.InputData, "Message M / ciphertext C input", "Enter your message M / ciphertext C here", "", DisplayLevel.Beginner)]
278        public BigInteger InputMC
279        {
280            get
281            {
282                return inputmc;
283            }
284            set
285            {
286                this.inputmc = value;
287                OnPropertyChanged("InputMC");
288            }
289        }
290
291        /// <summary>
292        /// Gets/Sets the one part of the public/private key called E / D
293        /// </summary>
294        [PropertyInfo(Direction.InputData, "Public key E / private key D input", "Enter your public key E / private key D here", "", DisplayLevel.Beginner)]
295        public BigInteger InputED
296        {
297            get
298            {
299                return inputed;
300            }
301            set
302            {
303                this.inputed = value;
304                OnPropertyChanged("InputED");
305            }
306        }
307
308        /// <summary>
309        /// Gets/Sets a output message/ciphertext as BigInteger called C / M
310        /// </summary>
311        [PropertyInfo(Direction.OutputData, "Cipher C output / message M output", "Your cipher C / message M will be send here", "", DisplayLevel.Beginner)]
312        public BigInteger OutputMC
313        {
314            get
315            {
316                return outputmc;
317            }
318            set
319            {
320                this.outputmc = value;
321                OnPropertyChanged("OutputMC");
322            }
323        }
324
325        /// <summary>
326        /// Gets/Sets a text input for encryption/decryption
327        /// </summary>
328        [PropertyInfo(Direction.InputData, "Text input", "Enter your text here", "", DisplayLevel.Beginner)]
329        public byte[] InputText
330        {
331            get
332            {
333                return inputText;
334            }
335            set
336            {
337                this.inputText = value;
338                //GuiLogMessage("InputText: " + (int)inputText[0] + " " + (int)inputText[1] + " " + (int)inputText[2] + " " + (int)inputText[3] + " ", NotificationLevel.Info);
339                OnPropertyChanged("InputText");
340            }
341        }
342
343        /// <summary>
344        /// Gets/Sets a text output for encrypted/decrypted data
345        /// </summary>       
346        [PropertyInfo(Direction.OutputData, "Text output", "Your text will be send here", "", DisplayLevel.Beginner)]
347        public byte[] OutputText
348        {
349            get
350            {
351                return outputText;
352            }
353            set
354            {
355                this.outputText = value;
356                //GuiLogMessage("OutputText: " + (int)outputText[0] + " " +(int)outputText[1] + " "+(int)outputText[2] + " "+(int)outputText[3] + " ", NotificationLevel.Info);
357                OnPropertyChanged("OutputText");
358            }
359        }
360
361        #endregion
362
363        #region private
364
365        /// <summary>
366        /// Encrypts/Decrypts all blocks belonging to the thread nr
367        /// </summary>
368        /// <param name="parameters">parameters</param>
369        private void crypt(Object parameters)
370        {
371            byte[] output = (byte[])((Object[])parameters)[0];
372            int blockcount = (int)((Object[])parameters)[1];
373            int blocksize_input = (int)((Object[])parameters)[2];
374            int blocksize_output = (int)((Object[])parameters)[3];
375            int threadnr = (int)((Object[])parameters)[4];
376            Thread thread = (Thread)((Object[])parameters)[5];
377
378            try
379            {
380
381                BigInteger bint = new BigInteger();
382               
383                //encrypt/decrypt each block
384                for (int i = threadnr; i < blockcount; i += (this.settings.CoresUsed + 1)) //walk over the blocks
385                // CoresUsed starts with 0 (so 0 => use 1 Core)
386                {
387
388                    //create a big integer from a block
389                    byte[] help = new byte[blocksize_input];
390                    for (int j = 0; j < blocksize_input; j++)
391                    {
392                        if (i * blocksize_input + j < InputText.Length)
393                            help[j] = InputText[i * blocksize_input + j];
394                        if (stopped)
395                            return;
396
397                    }
398                    bint = new BigInteger(help);
399
400                    //Check if the text could be encrypted/decrypted
401                    //this is only possible if the m < N
402                    if (bint > this.InputN)
403                    {
404                        //Go out with an error because encryption/decryption is not possible
405                        GuiLogMessage("N = " + this.InputN + " is not suitable for encrypting this text: M = " + new BigInteger(help) + " > N. Please choose another pair of primes!", NotificationLevel.Error);
406                        return;
407                    }
408
409                    //here we encrypt/decrypt with rsa algorithm
410                    bint = bint.modPow(this.InputED, this.InputN);
411
412                    //create a block from the byte array of the BigInteger
413                    byte[] bytes = bint.getBytes();
414                    int diff = (blocksize_output - (bytes.Length % blocksize_output)) % blocksize_output;
415
416                    for (int j = 0; j < bytes.Length; j++)
417                    {
418                        output[i * blocksize_output + j + diff] = bytes[j];
419                        if (stopped)
420                            return;
421                    }
422
423                    if (stopped)
424                        return;
425
426                    blocks_done++;
427                    ProgressChanged((double)blocks_done / blockcount, 1.0);
428
429                }//end for i
430            }
431            finally
432            {
433                //remove thread from list so that main thread will stop
434                //if all threads are removed
435                if (this.threads != null && thread != null){
436                    threads.Remove(thread);
437                    GuiLogMessage("stopped: " + thread.Name, NotificationLevel.Debug);
438                }
439            }
440
441        }//end crypt
442       
443        /// <summary>
444        /// Remove all '0' from a byte arrays end
445        /// example
446        ///
447        /// { 'a','b','c',0,0 } => { 'a','b','c' }
448        /// </summary>
449        /// <param name="input">byte array</param>
450        /// <returns>byte array</returns>
451        private byte[] removeZeros(byte[] input)
452        {
453            //1. Count zeros
454            int zeros = 0;
455            for (int i=input.Length-1;i>0;i--){
456
457                if (input[i] == 0)
458                {
459                    zeros++;
460                }
461                else
462                {
463                    break;
464                }
465
466            }
467
468            //2. Create new smaller byte array with
469            byte[] output = new byte[input.Length - zeros];
470
471            //3. Copy from input array beginning at the first byte <> 0 to the output array
472            for (int i = 0; i < input.Length - zeros; i++)
473            {
474                output[i] = input[i];
475            }
476
477            return output;
478        }
479
480        /// <summary>
481        /// Change the progress of this plugin
482        /// </summary>
483        /// <param name="value">value</param>
484        /// <param name="max">max</param>
485        private void ProgressChanged(double value, double max)
486        {
487            EventsHelper.ProgressChanged(OnPluginProgressChanged, this, new PluginProgressEventArgs(value, max));
488        }
489
490        /// <summary>
491        /// Logg a message to cryptool
492        /// </summary>
493        /// <param name="p">p</param>
494        /// <param name="notificationLevel">notificationLevel</param>
495        private void GuiLogMessage(string p, NotificationLevel notificationLevel)
496        {
497            EventsHelper.GuiLogMessage(OnGuiLogNotificationOccured, this, new GuiLogEventArgs(p, this, notificationLevel));
498        }
499
500        #endregion
501
502    }//end rsa
503
504}//end namespace
Note: See TracBrowser for help on using the repository browser.