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

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

replaced all BigInteger stuff with the new BigInteger class from .net 4.0

But there are still problems with some plugins (Keysearcher, BigInteger Operations...)

File size: 17.9 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;
28using System.Numerics;
29
30namespace Cryptool.Plugins.RSA
31{
32    [Author("Dennis Nolte, Raoul Falk, Sven Rech, Nils Kopal", null, "Uni Duisburg-Essen", "http://www.uni-due.de")]
33    [PluginInfo(false, "RSA", "RSA En/Decryption", "RSA/DetailedDescription/Description.xaml", "RSA/iconrsa.png", "RSA/Images/encrypt.png", "RSA/Images/decrypt.png")]
34    [EncryptionType(EncryptionType.Asymmetric)]
35    /// <summary>
36    /// This plugin does a RSA encryption/decryption on a Message M / Ciphertext C
37    /// It also encrypts/decrypts text with RSA
38    /// </summary>
39    class RSA : IEncryption
40    {
41        #region private members
42
43        private RSASettings settings = new RSASettings();
44        private BigInteger inputN = new BigInteger(1);
45        private BigInteger inputmc = new BigInteger(1);
46        private BigInteger inputed = new BigInteger(1);
47        private BigInteger outputmc = new BigInteger(1);
48        private byte[] inputText = null;
49        private byte[] outputText = null;
50        private int blocks_done = 0;
51        private ArrayList threads;
52        private bool stopped = true;
53
54        #endregion
55
56        #region events
57
58        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
59        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
60        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
61        public event StatusChangedEventHandler OnPluginStatusChanged;
62
63        #endregion
64
65        #region public
66       
67        /// <summary>
68        /// Notify that a property changed
69        /// </summary>
70        /// <param name="name">property name</param>
71        public void OnPropertyChanged(string name)
72        {
73            EventsHelper.PropertyChanged(PropertyChanged, this, new PropertyChangedEventArgs(name));
74        }
75
76        /// <summary>
77        /// Gets/Sets the Settings of this plugin
78        /// </summary>
79        public ISettings Settings
80        {
81            get { return this.settings; }
82            set { this.settings = (RSASettings)value; }
83        }
84
85        /// <summary>
86        /// Get the Presentation of this plugin
87        /// </summary>
88        public System.Windows.Controls.UserControl Presentation
89        {
90            get { return null; }
91        }
92
93        /// <summary>
94        /// Get the QuickWatchRepresentation of this plugin
95        /// </summary>
96        public System.Windows.Controls.UserControl QuickWatchPresentation
97        {
98            get { return null; }
99        }
100
101        /// <summary>
102        /// Called by the environment before execution
103        /// </summary>
104        public void PreExecution()
105        {
106            stopped = false;
107        }
108
109        /// <summary>
110        /// Called by the environment to execute this plugin
111        /// Does RSA on M/C and encrypt/decrypt the input Text
112        /// This method starts threads to speed RSA up if the user switched on more than one
113        /// thread
114        /// </summary>
115        public void Execute()
116        {
117           
118            //calculate the BigIntegers
119            try{
120                if (this.InputN != 0 && this.InputED != 0 && this.InputMC != 0 && !stopped)
121                    this.OutputMC = BigInteger.ModPow(InputMC, this.InputED, this.InputN);
122            }
123            catch (Exception ex)
124            {
125                GuiLogMessage("RSA could not work because of: " + ex.Message, NotificationLevel.Error);             
126            }
127
128            //
129            // RSA on Texts
130            //
131            if (this.InputText is object && this.InputN != 0 && this.InputED != 0 && !stopped)
132            {
133                DateTime startTime = DateTime.Now;
134                GuiLogMessage("starting RSA on texts", NotificationLevel.Info);
135
136                threads = ArrayList.Synchronized(new ArrayList());
137                int blocksize_input = 0;
138                int blocksize_output = 0;
139
140                //calculate block sizes from N         
141                //Encryption
142                if (settings.Action == 0)
143                {
144                    blocksize_input = (int)Math.Floor(BigInteger.Log(InputN, 256));
145                    blocksize_output = (int)Math.Ceiling(BigInteger.Log(InputN, 256));
146                }
147                //Decryption
148                else
149                {
150                    blocksize_input = (int)Math.Ceiling(BigInteger.Log(InputN, 256));
151                    blocksize_output = (int)Math.Floor(BigInteger.Log(InputN, 256));
152                }
153
154                GuiLogMessage("Input blocksize = " + blocksize_input, NotificationLevel.Debug);
155                GuiLogMessage("Output blocksize = " + blocksize_output, NotificationLevel.Debug);
156               
157                if (blocksize_input == 0)
158                {
159                    GuiLogMessage("Input blocksize 0 - RSA can not work", NotificationLevel.Error);
160                    return;
161                }
162
163                if (blocksize_output == 0)
164                {
165                    GuiLogMessage("Input blocksize 0 - RSA can not work", NotificationLevel.Error);
166                    return;
167                }
168
169                //calculate amount of blocks and the difference between the input text
170                //and the blocked input text
171                int blockcount = (int)Math.Ceiling((double)this.InputText.Length / blocksize_input);               
172               
173                GuiLogMessage("Blockcount = " + blockcount, NotificationLevel.Debug);
174               
175                //Generate input and output array of correct block size
176                byte[] output = new byte[blocksize_output * blockcount];
177                blocks_done = 0;
178
179                for (int i = 1; i < this.settings.CoresUsed + 1;i++ ) // CoresUsed starts with 0 (so 0 => use 1 Core)
180                {
181                    ParameterizedThreadStart pts = new ParameterizedThreadStart(this.crypt);
182                    Thread thread = new Thread(pts);
183                    thread.Name = "RSA worker thread " + i;
184                    threads.Add(thread);
185                    thread.Start(new Object[6] { output, blockcount, blocksize_input, blocksize_output, i, thread});
186                    GuiLogMessage("started: " + thread.Name, NotificationLevel.Debug);
187
188                    if (stopped)
189                        return;
190
191                }//end for
192
193                //main thread should work also
194                crypt(new Object[6] { output, blockcount, blocksize_input, blocksize_output, 0, null });
195
196                //Wait for all worker threads to stop
197                //Worker threads will be removed by themselves from the list
198                //in finally block
199                while (threads.Count != 0)
200                {
201                    if (stopped)
202                        return;
203
204                    Thread.Sleep(0);
205                }
206               
207                output = removeZeros(output);
208                this.OutputText = output;
209
210                DateTime stopTime = DateTime.Now;
211                TimeSpan duration = stopTime - startTime;
212
213                GuiLogMessage("finished RSA on texts in " + duration, NotificationLevel.Info);
214
215            }//end if           
216            ProgressChanged(1.0, 1.0);
217        }//end Execute
218
219        /// <summary>
220        /// Called by the environment after execution of this plugin
221        /// </summary>
222        public void PostExecution()
223        {
224            this.stopped = true;
225        }
226
227        /// <summary>
228        /// Called by the environment to pause this plugin
229        /// </summary>
230        public void Pause()
231        {
232        }
233
234        /// <summary>
235        /// Called by the environment to stop this plugin
236        /// </summary>
237        public void Stop()
238        {
239            this.stopped = true;
240        }
241
242        /// <summary>
243        /// Called by the environment to initialize this plugin
244        /// </summary>
245        public void Initialize()
246        {
247            this.stopped = true;
248        }
249
250        /// <summary>
251        /// Called by the environment to Dispose this plugin
252        /// </summary>
253        public void Dispose()
254        {
255
256        }
257
258        /// <summary>
259        /// Gets/Sets the one part of the public/private key called N
260        /// </summary>
261        [PropertyInfo(Direction.InputData, "Public key / private Key N input", "Enter your public key / private key N here", "", DisplayLevel.Beginner)]
262        public BigInteger InputN
263        {
264            get
265            {
266                return inputN;
267            }
268            set
269            {
270                this.inputN = value;
271                OnPropertyChanged("InputN");
272            }
273        }
274
275        /// <summary>
276        /// Gets/Sets a input message/ciphertext as BigInteger called M / C
277        /// </summary>
278        [PropertyInfo(Direction.InputData, "Message M / ciphertext C input", "Enter your message M / ciphertext C here", "", DisplayLevel.Beginner)]
279        public BigInteger InputMC
280        {
281            get
282            {
283                return inputmc;
284            }
285            set
286            {
287                this.inputmc = value;
288                OnPropertyChanged("InputMC");
289            }
290        }
291
292        /// <summary>
293        /// Gets/Sets the one part of the public/private key called E / D
294        /// </summary>
295        [PropertyInfo(Direction.InputData, "Public key E / private key D input", "Enter your public key E / private key D here", "", DisplayLevel.Beginner)]
296        public BigInteger InputED
297        {
298            get
299            {
300                return inputed;
301            }
302            set
303            {
304                this.inputed = value;
305                OnPropertyChanged("InputED");
306            }
307        }
308
309        /// <summary>
310        /// Gets/Sets a output message/ciphertext as BigInteger called C / M
311        /// </summary>
312        [PropertyInfo(Direction.OutputData, "Cipher C output / message M output", "Your cipher C / message M will be send here", "", DisplayLevel.Beginner)]
313        public BigInteger OutputMC
314        {
315            get
316            {
317                return outputmc;
318            }
319            set
320            {
321                this.outputmc = value;
322                OnPropertyChanged("OutputMC");
323            }
324        }
325
326        /// <summary>
327        /// Gets/Sets a text input for encryption/decryption
328        /// </summary>
329        [PropertyInfo(Direction.InputData, "Text input", "Enter your text here", "", DisplayLevel.Beginner)]
330        public byte[] InputText
331        {
332            get
333            {
334                return inputText;
335            }
336            set
337            {
338                this.inputText = value;
339                //GuiLogMessage("InputText: " + (int)inputText[0] + " " + (int)inputText[1] + " " + (int)inputText[2] + " " + (int)inputText[3] + " ", NotificationLevel.Info);
340                OnPropertyChanged("InputText");
341            }
342        }
343
344        /// <summary>
345        /// Gets/Sets a text output for encrypted/decrypted data
346        /// </summary>       
347        [PropertyInfo(Direction.OutputData, "Text output", "Your text will be send here", "", DisplayLevel.Beginner)]
348        public byte[] OutputText
349        {
350            get
351            {
352                return outputText;
353            }
354            set
355            {
356                this.outputText = value;
357                //GuiLogMessage("OutputText: " + (int)outputText[0] + " " +(int)outputText[1] + " "+(int)outputText[2] + " "+(int)outputText[3] + " ", NotificationLevel.Info);
358                OnPropertyChanged("OutputText");
359            }
360        }
361
362        #endregion
363
364        #region private
365
366        /// <summary>
367        /// Encrypts/Decrypts all blocks belonging to the thread nr
368        /// </summary>
369        /// <param name="parameters">parameters</param>
370        private void crypt(Object parameters)
371        {
372            byte[] output = (byte[])((Object[])parameters)[0];
373            int blockcount = (int)((Object[])parameters)[1];
374            int blocksize_input = (int)((Object[])parameters)[2];
375            int blocksize_output = (int)((Object[])parameters)[3];
376            int threadnr = (int)((Object[])parameters)[4];
377            Thread thread = (Thread)((Object[])parameters)[5];
378
379            try
380            {
381
382                BigInteger bint = new BigInteger();
383               
384                //encrypt/decrypt each block
385                for (int i = threadnr; i < blockcount; i += (this.settings.CoresUsed + 1)) //walk over the blocks
386                // CoresUsed starts with 0 (so 0 => use 1 Core)
387                {
388
389                    //create a big integer from a block
390                    byte[] help = new byte[blocksize_input+1];
391                    for (int j = 0; j < blocksize_input; j++)
392                    {
393                        if (i * blocksize_input + j < InputText.Length)
394                            help[j] = InputText[i * blocksize_input + j];
395                        if (stopped)
396                            return;
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 = BigInteger.ModPow(bint, this.InputED, this.InputN);
411
412                    //create a block from the byte array of the BigInteger
413                    byte[] bytes = removeZeros(bint.ToByteArray());
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.